【问题标题】:Bash Script execution too longBash 脚本执行时间过长
【发布时间】:2012-07-18 07:55:14
【问题描述】:

我编写的一个脚本有问题,执行时间太长(比如 24 分钟),但时间是可变的(取决于日志),并且在不久的将来,时间肯定会增加.

麻烦在一个嵌套的for循环中:

obtener_ErroresLanzados()
{
        #Buscamos los equipos del log lanzados_a_pendientes en los logs de instala_sw_qcc para ver el porque no se han lanzado.
        totalLanzadosPendientes=`cat $rutaTemporales/lanzados_a_pendientes.log | wc -l`;
        lanzadosPendientes=$(cat $rutaTemporales/lanzados_a_pendientes.log);
        #grep "$paqueteBuscado" instala_sw_qcc_2012*.log | cut -f 1 -d ":" > $rutaTemporales/logsErrores.log;
        find $rutaTrazas -name "instala_sw_qcc_2012*" | xargs grep -l "$rutaQcc/$paqueteBuscado" | xargs grep -l "ERROR \[" | cut -f 9 -d "/" > $rutaTemporales/logsErrores.log;
        logsErrores=$(cat $rutaTemporales/logsErrores.log);
        totalLogsErrores=`cat $rutaTemporales/logsErrores.log | wc -l`;

for (( j=1; j<=$totalLanzadosPendientes; j++ ))
    do
equipoBusqueda=`echo $lanzadosPendientes | cut -f $j -d " "`;
            for (( k=1; k<=$totalLogsErrores; k++ ))
            do
            logBusqueda=`echo $logsErrores | cut -f $k -d " "`;
            grep "ERROR \[$equipoBusqueda\]" $rutaTrazas/$logBusqueda >> $rutaTemporales/erroresPendientes.log;
            if [ $? -eq 0 ];then
                    break;
            fi;
                    done;
    done;
    cat $rutaTemporales/erroresPendientes.log | sed 's/  / /g' | sed '/No se ha podido/d' | cut -f 7-14 -d " " | sort -u > $rutaTemporales/erroresPendientes_Final.log;
}

问题在于 $totalLogsErrores 大于 20k...

我可以通过其他方式吗?

谢谢!

----------------------- 编辑 1 ---------- -

$ time find $rutaTrazas -name "instala_sw_qcc_2012*" | xargs grep -l "$rutaQcc/$paqueteBuscado" | xargs grep -l "ERROR [" | cut -f 9 -d "/" 

real 0m3.862s
user 0m0.959s
sys 0m2.941s 

$ du -h ../trazas/instala_sw_qcc_20120718091838.log 

4.0K ../trazas/instala_sw_qcc_20120718091838.log 

$ time grep error ../trazas/instala_sw_qcc_20120718091838.log 

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

【问题讨论】:

  • 请尝试解释您想要实现的目标,并给出一些输入和预期输出的示例。更容易理解您的问题。
  • 你不能使用像 perl 脚本这样的东西吗?这里有很多二进制调用,这意味着很多“fork”调用,这些调用很昂贵。
  • 问题是第一个 for 可能有 100 次重复,而在这个 for 里面还有其他 for 重复次数超过 10k,即 100 x 10000 reps...

标签: bash shell loops for-loop


【解决方案1】:

循环处理效率低

几乎不可能弄清楚您真正在此处尝试做什么,因为没有语料库或示例输出显示您实际尝试解析的内容。但是,您可以将此问题归结为处理效率低下和进程分叉。

使用 AWK 进行面向记录的操作

日志文件通常是面向记录的,其中每一行是具有多个字段的记录。如果这是您的用例,那么 AWK(或 AWK 仿真模式下的 Perl/Ruby)通常是适合这项工作的工具。这样可以确保您只处理每行一次,并且读取行和拆分字段非常有效。

例如,使用 Bash 4 和 GNU awk(又名 gawk):

shopt -s globstar
awk 'BEGIN {error_count = 0}
     /ERROR/ {print $9; ++error_count}
     # other pattern/action pairs
     END {print "Total errors:", error_count}
    ' **/instala_sw_qcc_2012* > /path/to/output/file

您可以将多个模式匹配应用于每一行,或者如果您确实需要这样做,则可以从 awk 内部直接输出到单个文件。但是,通过让 awk 在一个进程中处理循环和行解析,您可能会获得很大的效率。

【讨论】:

    【解决方案2】:

    要限定此性能问题,您可以尝试以下方法:

    评估您的第一个 findgrep 命令的影响:

    $ time find $rutaTrazas -name "instala_sw_qcc_2012*" | xargs grep -l "$rutaQcc/$paqueteBuscado" | xargs grep -l "ERROR \[" | cut -f 9 -d "/" > $rutaTemporales/logsErrores.log;
    

    在嵌套的for 循环中,评估grep 的影响。文件有多大?在您的评论中,您提到了 100*10000 重复,如果每个 grep 花费 4 毫秒,这将是巨大的。

    当您有很多子目录时,find 变得昂贵,而当您的文件足够大时,grep 变得昂贵。

    $ du -h file.out 
      20K   file.out
    
    
    $ time grep ERROR file.out 
    
    real    0m0.004s
    user    0m0.000s
    sys     0m0.003s
    

    如果你有 1000000 个循环,那将需要很多 :)

    【讨论】:

      【解决方案3】:

      你正在将整个文件读入一个 shell 变量,然后用 单独的切割过程。这是非常低效的。

      很难理解你想做什么。也许你可以更换功能 像这样:

      $ cd $rutaTrazas
      $ sed 's/^/ERROR \[/; s/$/\]/' $rutaTemporales/lanzados_a_pendientes.log > search_strings
      $ xargs grep -F -f search_strings \
          < $rutaTemporales/logsErrores.log \
          >> $rutaTemporales/erroresPendientes.log
      $ < $rutaTemporales/erroresPendientes.log \
          sed 's/  / /g' | sed '/No se ha podido/d' |
          cut -f 7-14 -d " " | 
          sort -u > $rutaTemporales/erroresPendientes_Final.log
      

      【讨论】:

        【解决方案4】:

        无用的 cat :wc -l &lt;file 而不是 cat file | wc -l

        无用的 wc 使用:while read line; do ...;done &lt;file 而不是 for 循环:

        find $rutaTrazas -name "instala_sw_qcc_2012*" | xargs grep -l "$rutaQcc/$paqueteBuscado" | xargs grep -l "ERROR \[" | cut -f 9 -d "/" > $rutaTemporales/logsErrores.log;
        while read equipoBusqueda; do
            while read logBusqueda; do
                grep "ERROR \[$equipoBusqueda\]" $rutaTrazas/$logBusqueda >> $rutaTemporales/erroresPendientes.log
                if [ $? -eq 0 ];then
                    break; 
                fi;
            done <$rutaTemporales/logsErrores.log
        done <$rutaTemporales/lanzados_a_pendientes.log
        cat $rutaTemporales/erroresPendientes.log | sed 's/  / /g' | sed '/No se ha podido/d' |
            cut -f 7-14 -d " " | sort -u > $rutaTemporales/erroresPendientes_Final.log;
        

        终于find、grep、sed、cut等命令可以简化了。

        【讨论】:

        • 您错过了删除cat。我不认为你已经回答了主要问题——如果你回答了,你需要指出你的答案在哪里可以节省时间。你可以做if grep...
        • 解决了第二部分的问题:logsErrores that is greater than 20k
        • 也许还有第一部分:对于 100 000 次重复 echo | cut,效率非常低
        • 明天我会尝试并发布结果
        猜你喜欢
        • 1970-01-01
        • 2020-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-04
        • 1970-01-01
        • 2015-11-19
        • 1970-01-01
        相关资源
        最近更新 更多