如何优化此bash函数?

时间:2018-01-22 19:51:47

标签: regex bash performance

我已经制作了一个bash函数来分隔大于一定大小的图像文件,但是我有大约20000个文件并且它花费了太多时间并且它根本没有使用CPU,所以我想知道也许没有真正复杂的多处理就可以对它进行一些优化(我不介意多处理,但我不想为这么简单的任务编写20行代码)

这是我的代码:

getpics() {
    dir="larger than $1x$2"
    mkdir "$dir"
    for f in `ls *`; do  
        a=`file "$f" | grep -Po ", \K[\d]*x[\d]*"`
        x=`grep -Po "\d*(?=x)" <<< "$a"`
        y=`grep -Po "x\K\d*" <<< "$a"`
        echo "$a _______________________ $x, $y"
        if [ $x -gt $1 ] && [ $y -gt $2 ] ; then
            mv "$f" "$dir/$f"
        fi  
    done
}

3 个答案:

答案 0 :(得分:4)

您可以尝试尽可能避免调用外部工具,而是使用bash内置函数。

例如,要替换所有grep,您可以使用bash ERE(适用于Bash 4 +):

re='^.* ([0-9]+)x([0-9]+),.*$'
for f in *; do
    desc=$(file "$f")
    if [[ $desc =~ $re ]]; then
        x=${BASH_REMATCH[1]}
        y=${BASH_REMATCH[2]}
        # ... check size & move
    fi
done

答案 1 :(得分:2)

  1. 您不得解析ls
  2. 的输出
  3. 您可以使用BASH正则表达式避免2 grep次呼叫(感谢下面的评论和@ randomir的答案)
  4. 重构的脚本:

    re=', ([0-9]+)x([0-9]+)'
    getpics() {
        dir="larger than $1x$2"
        mkdir "$dir"
        for f in *; do  
            if [[ $(file "$f") =~ $re ]]; then
               x=${BASH_REMATCH[1]}
               y=${BASH_REMATCH[2]}
               echo "$a _______________________ $x, $y"
               (( x > $1 && y > $2 )) && mv "$f" "$dir/$f"
            fi  
        done
    }
    

答案 2 :(得分:1)

首先让我们做一些基准测试:

我们从if:

开始
$ time for i in `seq 1 100000`; do if [ 2 -gt 1 ] && [ 3 -gt 2 ]; then a=1; fi; done

real    0m0.694s
user    0m0.693s
sys 0m0.003s

$ time for i in `seq 1 100000`; do if [[ 2 -gt 1 && 3 -gt 2 ]]; then a=1; fi; done

real    0m0.428s
user    0m0.424s
sys 0m0.006s

$ time for i in `seq 1 100000`; do if (( 2 > 1 && 3 > 2 )); then a=1; fi; done

real    0m0.366s
user    0m0.364s
sys 0m0.003s

$ time for i in `seq 1 100000`; do (( 2 > 1 && 3 > 2 )) && a=1; done

real    0m0.355s
user    0m0.352s
sys 0m0.005s

现在让我们看一下ls

$ time for i in `ls *`; do a=1; done

real    0m0.280s
user    0m0.249s
sys 0m0.036s

$ time for i in *; do a=1; done

real    0m0.128s
user    0m0.128s
sys 0m0.000s

现在有些人可能想知道是否

desc=$(file "$f")
if [[ $desc =~ $re ]]; then

不同
if [[ $(file "$f") =~ $re ]]; then

但结果没有区别。我也测试了很多次,但每次都比其他人快。但我不是把结果放在这里,因为我觉得它没用。

你可能想知道

之间是否存在差异

^.* ([0-9]+)x([0-9]+),.*$([0-9]+)x([0-9]+),

但是我测试了它并且没有。但是根据regex101最好的正则表达式(保留分组)是:

.*, ([0-9]+)x([0-9]*)     : 33 steps.
, ([0-9]+)x([0-9]+)       : 34 steps.
^.* ([0-9]+)x([0-9]+),.*$ : 38 steps.

现在让我们比较获取xy的不同方式:

$ time (files=( * ); for f in "${files[@]:0:1000}"; do IFS=, a=(`file $f`);IFS=x b=(${a[8]});done;)

real    0m5.580s
user    0m1.147s
sys 0m4.498s

$ time (files=( * ); for f in "${files[@]:0:1000}"; do if [[ $(file "$f") =~ $re ]]; then x=${BASH_REMATCH[1]}; y=${BASH_REMATCH[2]}; fi; done)

real    0m5.817s
user    0m1.234s
sys 0m4.619s

$ time (files=( * ); for f in "${files[@]:0:1000}"; do a=(`convert $f -print "%w %h\n" /dev/null`);done;)

real    0m10.356s
user    0m3.624s
sys 0m6.793s

$ time (files=( * ); for f in "${files[@]:0:1000}"; do a=$(file "$f" | grep -Po ", \K\d+x\d+"); IFS=x read x y <<<"$a"; done;)

real    0m12.645s
user    0m2.235s
sys 0m13.914s