为何批量处理图片的bash脚本需两次终止后才会正常执行?
问题原因
核心触发逻辑
你遇到的现象完全是大数量级文件下的shell通配符扩展阻塞导致的,具体运行逻辑如下:
- 脚本启动后会先执行
files=(./pics/all/*.jpg)这一行,shell需要先完成通配符*.jpg的扩展:遍历目录下所有3万+文件,匹配后缀后把所有符合条件的文件路径全量加载到内存生成数组。这个过程在文件数量极大时耗时非常久,且不会有任何输出,所以你会觉得脚本“没有任何反应”,此时脚本还停留在参数扩展阶段,根本没进入循环逻辑。 - 第一次按下
Ctrl + C时,你终止的是正在进行的通配符扩展/数组构建任务,此时shell会输出当前已完成扩展的所有文件名(就是你看到的完整文件数组打印),但数组实际上已经被成功赋值。第二次按下Ctrl + C时,终止的是shell原本要执行的中断退出流程,反而回到了脚本的后续执行逻辑,所以才会进入循环开始处理图片。
优化方案
不要一次性全量加载文件名到数组,改用find逐文件处理,避免启动阻塞和高内存占用,同时优化原有脚本的性能问题:
# 先统计总文件数,速度远快于通配符扩展 total=$(find ./pics/all -maxdepth 1 -type f -name "*.jpg" | wc -l) i=0 # 逐行读取find的输出处理,不需要预加载所有文件名 find ./pics/all -maxdepth 1 -type f -name "*.jpg" | while read -r file; do filename="${file##*/}" # 所有变量加双引号,避免文件名带空格/特殊字符时出错 convert "$file" -sampling-factor 4:2:0 -strip -quality 1 -interlace JPEG -colorspace RGB "./pics/all_resampled/$filename" ((i++)) # 用bash内置整数运算,不要调用bc,减少子进程开销 progress=$(( i * 100 / total )) echo "$i/$total ${progress}% $file RESAMPLED" done
如果要进一步提升处理速度,可以用并行工具parallel多进程处理,比单循环效率高几倍:
find ./pics/all -maxdepth 1 -type f -name "*.jpg" | parallel -j $(nproc) convert {} -sampling-factor 4:2:0 -strip -quality 1 -interlace JPEG -colorspace RGB ./pics/all_resampled/{/}
-j $(nproc)表示用和CPU核心数相同的并行进程数,可根据实际需求调整。
内容的提问来源于stack exchange,提问作者Sir Rubberduck
相关产品推荐
相关产品推荐

