用Python3/Shell做开发任务

准备工作(可选,mac环境)

安装Homebrew
安装python3
设置默认python
(.bashrc文件在文件中添加 alias python="python3"即可)
安装python编辑器Sublime3

其他常用命令

用Python检查资源文件是否Valid

有时候编译打包时会报资源文件格式错误。比如某个jpg的文件被重命名为png, 然后放到了图片资源目录下,就可能导致编译失败。
为此写了一个Python脚本来自动检测此类错误。

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import os
import imghdr

PATH = input("Dir:").strip()
pngString = "png"
for path, dirs, files in os.walk(PATH):
    for filename in files:
        fullpath = os.path.join(path, filename)
        try:
         format = imghdr.what(fullpath)
         print("image path: " + fullpath)
         print("image format: " + str(format))
        except Exception as e:
            print(f"Error processing file: {fullpath}")
            print(f"Error message: {str(e)}") 

PATH 是要检测的路径名,walk方法会遍历改PATH下的所有文件,包括子文件夹下的文件。然后检测真实的格式(format)(即使重命名过也没事)。最后如果格式不是png,就会打印出文件名。

使用方法:

  1. 文本编辑器复制这段代码,保存为.py结尾的文件。
  2. 改成自己的检测路径
  3. 在命令行中运行,比如python /Users/apple/Desktop/imgcheck.py 即可

批量重命名图片并复制到drawable文件夹

带注释的脚本:

# -*- coding: utf-8 -*-
import os
import shutil

# 从哪里拷贝图片
pathFrom = input("move drawable from:").strip()
# 拷贝到哪个文件夹  比如项目下的drawable—xhdpi文件夹
pathTo = input("move drawable to:").strip()
# 要批量重命名的字符串 一般是@2x。也是需要复制的图片的标志,因为如果不需要重命名的话,就直接复制 不需要脚本了。
iDentifyString = input("iDentifyString:").strip()
for path, dirs, files in os.walk(pathFrom):
    for fileName in files:
        print(fileName)
        if iDentifyString.lower() in fileName.lower():
            fromFullpath = os.path.join(path, fileName)
            # 去掉@2x 替换不合法方字符 改成小写
            newFileName = fileName.replace(
                iDentifyString, "").replace("-", "_").lower()
            print(newFileName)
            toFullPath = os.path.join(pathTo, newFileName)
            shutil.copyfile(fromFullpath, toFullPath)

使用场景:UI给了切图,但是是按IOS命名的。@2x @3x 这样。安卓只需要拿@2x的图片,并去掉@2x这几个字符。

批量重命名

windows系统没有提供批量重命名的工具,就自己用写了个脚本:

import re

# 从哪里拷贝文件
pathFrom = input("move file from:").strip()
# 拷贝到哪个文件夹  目前是新建rename文件夹
pathTo = os.path.join(pathFrom, "rename")
if not os.path.exists(pathTo):
    os.makedirs(pathTo)
# 要批量重命名的字符串
iDentifyString = "_sign"
for path, dirs, files in os.walk(pathFrom):
    for fileName in files:
        # 没有重命名的文件也复制
        newFileName = fileName
        fromFullpath = os.path.join(path, fileName)
        if iDentifyString.lower() in fileName.lower():
            # 去掉iDentifyString
            newFileName = fileName.replace(
                iDentifyString, "")
            newFileName = re.sub(r'(_(\d{1,3}))+_', r'_', newFileName)
            print(newFileName)
        toFullPath = os.path.join(pathTo, newFileName)
        #如果不是rename文件夹下的文件
        if not "rename" in fromFullpath:
            shutil.copyfile(fromFullpath, toFullPath)

这样,文件名 appName_1.1.1_111_1_qq_sign.apk 就会重命名为 appName_1.1.1_qq.apk

bug解决

使用zipFile压缩文件时, 发现中文的文件名会乱码, 搜索无果,就自己调试, 最后发现zipfile.py中执行_encodeFilenameFlags方法时, 没有执行utf-8编码.
(具体原因没时间去探究,就通过改源码的方式解决了, 毕竟就我的日常操作而言,执行UTF-8编码是没有问题的)
原来的代码:

def _encodeFilenameFlags(self):
        if isinstance(self.filename, unicode):
            try:
                return self.filename.encode('ascii'), self.flag_bits
            except UnicodeEncodeError:
                return self.filename.encode('utf-8'), self.flag_bits | 0x800
        else:
            return self.filename, self.flag_bits

修改后代码:

def _encodeFilenameFlags(self):
        if isinstance(self.filename, unicode):
            try:
                return self.filename.encode('ascii'), self.flag_bits
            except UnicodeEncodeError:
                return self.filename.encode('utf-8'), self.flag_bits | 0x800
        else:
            return self.filename.encode('utf-8'), self.flag_bits | 0x800
  • Tip
    老版本的python比如3.9下载的opencv库,使用新版本python如3.12时会找不到,需要重新下载

分文件夹脚本

(某文件夹有两千多张照片, 翻页会卡, 就平均分割成多个文件夹,方便查看):

# -*- coding: utf-8 -*-
import os
import imghdr
import shutil

currentIndex = 0
currentPath = ""
        # 从哪里拷贝
currentFolderIndex = 0        
pathFrom = input("move from:").strip().lstrip('\"').rstrip('\"').lstrip('\'').rstrip('\'')
# 拷贝到哪个文件夹
pathTo = input("move to:").strip().lstrip('\"').rstrip('\"').lstrip('\'').rstrip('\'')
print(pathFrom)
currentPath = os.path.join(pathTo, str(currentFolderIndex))
os.mkdir(currentPath)
for path, dirs, files in os.walk(pathFrom):
    for fileName in files:
        print(fileName)
        if currentIndex >= 100:
            currentIndex = 0
            currentFolderIndex += 1
            currentPath = os.path.join(pathTo, str(currentFolderIndex))
            os.mkdir(currentPath)

        fromFullpath = os.path.join(path, fileName)
        # print(newFileName)
        toFullPath = os.path.join(currentPath, fileName)
        shutil.copyfile(fromFullpath, toFullPath)
        currentIndex += 1

帮算法组写的整理数据的功能(文件输入 字符串搜索 遍历数组)

# -*- coding: utf-8 -*-
import os
import imghdr
import shutil


def read_file_as_str(file_path):
    if not os.path.isfile(file_path):
       raise TypeError(file_path + " does not exist")
    all_the_text = open(file_path).read()
    return all_the_text


errorDataFileLocation = input("errorDataFileLocation:").strip().lstrip('\"').rstrip('\"').lstrip('\'').rstrip('\'')
errorDataListStr = read_file_as_str(errorDataFileLocation)
dataSourceFileLocation = input("dataSourceFileLocation:").strip().lstrip('\"').rstrip('\"').lstrip('\'').rstrip('\'')
dataSourceStr = read_file_as_str(dataSourceFileLocation)
dataSource = dataSourceStr.replace(".pts", "").replace("\n", "").split("./");
print("items start")
for item in dataSource:
    if item in errorDataListStr:
        errorDataListStr = errorDataListStr.replace(item, "")
        print("./" + item + ".jpg")
print("items end")
print("errorDataListStr")
print(errorDataListStr.strip())

动态执行command命令

合成2个视频

# -*- coding: utf-8 -*-
import os
import subprocess
tag = input("Tag:").strip()
command = 'ffmpeg -i /Users/xx/Desktop/Demo_RenderSDK/{Tag}_AE.mov -i /Users/xx/Desktop/Demo_RenderSDK/{Tag}_Demo.mp4 -filter_complex "[0:v]pad=iw*2:ih[a];[a][1:v]overlay=w:0" /Users/xx/Desktop/对比2/{Tag}左AE右Demo.mp4'.format(Tag = tag)
subprocess.call(command, shell=True)

合成3个视频

# -*- coding: utf-8 -*-
import os
import subprocess
tag = input("Tag:").strip()
command = 'ffmpeg -i /Users/atlasv-hongjunmin/Desktop/material/videos/{Tag}_V1.mp4 -i /Users/atlasv-hongjunmin/Desktop/material/videos/{Tag}_V2.mp4 -i /Users/atlasv-hongjunmin/Desktop/material/videos/{Tag}_V3.mp4 -filter_complex "[0:v]pad=iw*3:ih[a];[a][1:v]overlay=w[b];[b][2:v]overlay=w*2:0" /Users/atlasv-hongjunmin/Desktop/material/对比/{Tag}_左V1中V2右V3.mp4'.format(Tag = tag)
subprocess.call(command, shell=True)

取视频第一帧(如果要缩略图,在图片文件名前加-s 320*280这样的参数)

# -*- coding: utf-8 -*-
import os
import subprocess
path = input("videoPath:").strip()
filepath,fullflname = os.path.split(path)
command = 'ffmpeg -i {PATH} -vframes 1 -update 1 -f image2 {FILEPATH}/1.jpg'.format(PATH = path,FILEPATH = filepath)
print("command: " + command)
subprocess.call(command, shell=True)

Tip:

jsonpath性能差但是至少能用,jsonpath-ng遇到稍微大一点的json就崩溃了,报错之多令人费解,jsonpath支持正则表达,jsonpath-ng 不支持

直接使用Python自带的json模块来操作json的话,示例代码如下:

#以只读模式打开Json文件
with open('xxx.json', 'r') as templetesFile:
    templetesJson = json.load(templetesFile)
#Python里字典默认用单引号包裹key和value(双引号也支持),但json格式里的key需要是双引号,所以需要下面的语句来转化成json格式。最后一个参数设置为False,就不会将中文转换成ascii码
msg = json.dumps(targetJson, indent=4, ensure_ascii=False)
  • 引号嵌套
    在f-string中,如果在大括号内使用引号,则大括号内引号必须与字符串引号不同,否则会报错。
#内双引号,外单引号
f'python is {"good"}'
# 内单引号,外双引号
f"python is {'good'}"

Python判断实例的类型:
How to check if type of a variable is string? [duplicate]

multiprocessing.Manager.dict()的线程和进程安全,是指
在单个字典操作的级别上锁定,而不是整块代码。所以类似 if key not in dict, dict[key] = value
这种,就需要全局lock的require lock操作
另外,manager.dict()读取比较慢,写操作更慢,网上有解决的trick,比如在普通字典里写好值后调用manager.dict() 的update方法,或者用其他方式实现多线程和多进程共享的字典

  • Pandas相关
  1. 5招学会Pandas数据类型转化

  2. Pandas对cvs迭代并修改数据保存:

def file2dataframe(filename: str, sheet_name=0) -> pd.DataFrame:
    """文件转pandas的DataFrame"""
    if filename.endswith('csv'):
        return pd.read_csv(filename)
    elif filename.endswith('xlsx'):
        return pd.read_excel(filename, sheet_name=sheet_name)
    else:
        raise ValueError('不支持的文件格式')

# 赋值后会是传值,即新开辟一块内存,所以要直接赋值: https://blog.csdn.net/qq_16555103/article/details/115185138
df = df.where(df.notnull(), None)
self.bar = tqdm(total=len(df), desc='替换url 迭代行')
        for index, row in df.iterrows():
            df.loc[index, 'XXX'] = self._replaceUrl(row['XXX'])
            self.bar.update(1)
self.bar.close()
month = datetime.datetime.now().month
day = datetime.datetime.now().day
df.to_csv(f"{month}_{day}.csv")
  • 处理音频数据
  1. 音量处理,可以实现音量归一化等
import glob
import os
from pydub import AudioSegment

#convert to mp3 128 bits
sound = AudioSegment.from_file("input-file.aac")
sound.export("output-file.mp3", format="mp3", bitrate="128k")

#sound.max_dBFS shows how far below the limit the highest sample is (in dB)
sound = AudioSegment.from_file("output.mp3", format="mp3")
max_gain_without_distortion = -1 * sound.max_dBFS

#increase volume by"max_gain_without_distortion" dB
from pydub.playback import play
song = AudioSegment.from_mp3("output-file.mp3")
louder_song = song + max_gain_without_distortion

#save louder song
louder_song.export("output.mp3", format='mp3')

def printError(tag, doTraceback=False):
    errorMsg = {}
    _, value, tb = sys.exc_info()
    tbs = traceback.extract_tb(tb)
    if len(tbs) > 0:
        errorMsg['Location'] = f'File {tbs[-1].filename}, line {tbs[-1].lineno}, in {tbs[-1].name}'
        errorMsg['Message'] = f'{value.__class__.__name__}: {str(value)}'
        errorMsg['tag'] = f'{tag}'
        if doTraceback:
            errorMsg['Stack'] = f'{traceback.format_exc()}'
    print(f'errorMsg: {errorMsg}')


def getDb(path, format):
    try:
        sound = AudioSegment.from_file(path, format=format)
        print(f"path{path} \n duration{sound.duration_seconds}")
        max = sound.max
        dBFS = sound.dBFS
        max_dBFS = sound.max_dBFS
        result = {"sound": sound, "path": path, "max": max, "dBFS": dBFS, "max_dBFS": max_dBFS}
        #print(result)
        return result
    except Exception as e:
        printError(path)


@multitasking.task
def toSameDb(pathStandard, formatStandard, pathTomodify, formatToModify, targetPath):
    try:
        dbStandard = getDb(pathStandard, formatStandard)
        dbTomodify = getDb(pathTomodify, formatToModify)
        change_in_dBFS = -1 * (dbTomodify["dBFS"] - dbStandard["dBFS"])
        targetSong = dbTomodify["sound"].apply_gain(change_in_dBFS)
        targetSong.export(targetPath, format=formatToModify)
    except Exception as e:
        printError(pathTomodify)


def toSameDbs(pathStandard, formatStandard, dirTomodify, formatToModify, targetDir):
    resourceFiles = os.listdir(dirTomodify)
    progressBar = tqdm(total=len(resourceFiles), desc='音频统一音量')
    for fileName in resourceFiles:
        #print(f"fileName:{fileName} basename:{os.path.basename(fileName)}")
        toSameDb(pathStandard, formatStandard, os.path.join(dirTomodify, fileName), formatToModify, os.path.join(targetDir, fileName))
        progressBar.update(1)
    multitasking.wait_for_tasks()
    progressBar.close()
  1. Python获取音频时长的完整指南
  • OSS
    python罗列oss文件(方法1会把目录当File, 方法2只会过滤文件夹)

  • Shell
    在shell脚本里调用第三方命令或脚本后查看命令的返回值

ret_code=$? # 执行执行的结果需要马上获取。一般0表示success
echo "command ret_code $ret_code"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,059评论 25 707
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,478评论 6 427
  • 当《战狼2》火爆霸屏的时候,每一天的各大公众号都竞相争抢和关注着与它相关的各种新闻,旧闻,绯闻,废文……甚至连广告...
    追光者ZH阅读 342评论 0 2
  • 人生到底要不要有生涯规划?到底要不要设定一个远方?我觉得这事是这样的,我们应该有个远方,或者应该对自己的人生做某种...
    金算珠阅读 958评论 0 0
  • 京都清水寺,日本年度汉字评选的投票箱,要求是选出最能代表今年的一个汉字。本来想等转完一圈,想想后再折回来写,却...
    LV太阳阅读 240评论 0 0