一个 bash 脚本性能优化的问题?

2019-07-23 15:03:16 +08:00
 0x11901

我想要的功能是寻找当前目录及子目录下所有与传入的分辨率相同的图片的地址。

环境

  1. Windows
  2. Git Bash + MinGW

思路

  1. 用 find 找到所有后缀名为 png 的文件的相对路径
  2. 把结果写到一个 temp 文件里
  3. 用 read 读取 temp 文件每一行,以一行一行处理相对路径
  4. 使用 file 获得对应路径的图片文件的信息
  5. 使用 awk 得到其中的分辨率字段
  6. 如果分辨率正确,打印相对路径

问题

然而,实际根本无法使用……虽然只有这么一点功能,但是架不住我要找的目录里图片多啊……差不多有 10g 了……第一版直接用不了,我调试了一下,find 还是可以接受的,但是一行一行 awk 太慢了,我尝试了多线程和回溯,勉强可以出结果了,但是我觉得不应该这么慢的,可能是我太菜了……毕竟我甚至不知道怎么把 find 的结果直接在内存中处理,甚至需要写到文件中的……

代码

#!/usr/bin/env bash

set -euo pipefail

if [[ $# != 2 ]]; then
    echo bad argument.
    exit 1
fi

TEMP_FILE=".todo.find"

find . -name "*.png" >>${TEMP_FILE}

while read line; do
    {
        resolution=$(file "${line}" | awk -F ',' '{print $2}')
        width=$(echo ${resolution} | awk '{print $1}')

        [[ ${width} != $1 ]] && continue

        height=$(echo ${resolution} | awk '{print $3}')

        if [[ ${height} == $2 ]]; then
            echo ${line}
        fi
    } &
done <${TEMP_FILE}

rm ${TEMP_FILE}

需求

总之希望有大佬指点一下怎么优化这个脚本!万分感谢!!!

2674 次点击
所在节点    问与答
39 条回复
dream10201
2019-07-23 15:09:41 +08:00
用其他语言写,然后找到就别存了,直接获取信息判断
momocraft
2019-07-23 15:14:51 +08:00
10g 的图片文件一个个 file 本来就会慢

写得这么复杂,性能也未必比 `find | xargs file | grep` 好很多
0x11901
2019-07-23 15:25:23 +08:00
@dream10201 主要我也不怎么会 shell,用它是感觉这个小需求没必要用其他的语言增加复杂度……
0x11901
2019-07-23 15:25:58 +08:00
@momocraft 主要还是太菜了,所以写成这样了……
dream10201
2019-07-23 15:28:27 +08:00
1、递归查找图片
2、获取文件信息判断分辨率
两步解决,多线程一开,走位走位
0x11901
2019-07-23 15:36:42 +08:00
@dream10201 其实我感觉可能是获取分辨率这里遇到瓶颈了,也许我能用 c 写一个小程序,输入地址打印分辨率……全部换语言太懒了不想改……
thedrwu
2019-07-23 15:38:07 +08:00
其实不是你的错。装个虚拟机,至少 10 倍速度
dream10201
2019-07-23 15:39:58 +08:00
@0x11901 不不,find 查找所有文件(耗时),写到文件里(耗时),读文件(耗时),这几个才是关键,本来可以一步搞定,非要整几步
momocraft
2019-07-23 15:51:53 +08:00
这个文件名列表多大
耗时的真的是读写这个列表(而不是每个图像文件都开 2 个 file 3 个 awk 进程)吗?
ant2017
2019-07-23 15:55:54 +08:00
find . -name "*.png" |xargs file>>${TEMP_FILE}
awk -F "," -v height=$1 -v width=$2 '$1==height && $2==width' ${TEMP_FILE}
0x11901
2019-07-23 15:56:58 +08:00
@dream10201 不是 find,我调了一下,find 很快的,读写也挺快的,就是 awk 太慢了。
0x11901
2019-07-23 15:57:28 +08:00
@momocraft 就是 awk 那里太久了!
0x11901
2019-07-23 15:58:07 +08:00
@ant2017 这是什么神奇操作,我试一下!
0x11901
2019-07-23 16:00:46 +08:00
@dream10201 而且主要是我不知道怎么把这个结果搞到内存里,所以才用的 io
ETiV
2019-07-23 16:04:03 +08:00
xargs 有个大写 P 参数,试试这个
0x11901
2019-07-23 16:04:11 +08:00
@ant2017 老哥,不好使啊……
ReVanTis
2019-07-23 16:08:44 +08:00
awk 换成别的工具会好点吗? cut 啥的。。。应该会比 awk 快点
rrfeng
2019-07-23 16:14:26 +08:00
简单优化一下:

find | while read x; do file $x; done | awk .....

总结:
1. 文件缓存没必要,增加大量 IO
2. 既然用了 awk,直接使用 awk 解决所有判断、输出的问题,不要用过多的指令(重复 fork 非常多)
3. 还是要看下到底哪一步耗时才好优化。

awk 启动慢,因为你启动了太多次,将 file 的结果合并到一起传给 awk 按行处理即可。
0x11901
2019-07-23 16:23:26 +08:00
@rrfeng 感觉非常有道理……问题的关键是我 awk 是在阮一峰的入门教程那里速成的啊 😂
maxbon
2019-07-23 16:28:04 +08:00
find 可以搭配 xargs 使用,find 的结果传过来就好了,然后可以用 & 伪多线程

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/585451

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX