与cat相比,Bash读取循环速度极慢,为什么?

时间:2012-12-07 11:56:59

标签: linux bash performance shell

这里有一个简单的测试脚本:

while read LINE; do
        LINECOUNT=$(($LINECOUNT+1))
        if [[ $(($LINECOUNT % 1000)) -eq 0 ]]; then echo $LINECOUNT; fi
done

当我执行cat my450klinefile.txt | myscript时,CPU锁定为100%,并且每秒可以处理大约1000行。大约5分钟处理cat my450klinefile.txt >/dev/null在半秒内完成的工作。

是否有更有效的方式基本上做这件事。我只需要从stdin读取一行,计算字节数,然后将其写入命名管道。但即便是这个例子的速度也很慢。

每1Gb输入行我需要做一些更复杂的脚本操作(关闭并打开一些数据被输入的管道)。

4 个答案:

答案 0 :(得分:12)

while read这么慢的原因是shell需要为每个字节进行系统调用。它无法从管道中读取大缓冲区,因为shell不能从输入流中读取多行,因此必须将每个字符与换行符进行比较。如果您在strace循环上运行while read,则可以看到此行为。这种行为是可取的,因为它可以可靠地执行以下操作:

while read size; do dd bs=$size count=1 of=file$(( i++ )); done

其中循环内的命令是从shell读取的同一个流中读取的。如果shell通过读取大缓冲区消耗了大量数据,则内部命令将无法访问该数据。不幸的副作用是read非常慢。

答案 1 :(得分:5)

这是因为bash脚本在这种情况下被解释并且没有针对速度进行真正优化。通常最好使用其中一种外部工具,例如:

awk 'NR%1000==0{print}' inputFile

与您的“每1000行打印”样本匹配。

如果您希望(对于每一行)输出字符行数后跟行本身,并将其通过另一个进程输出,您也可以这样做:

awk '{print length($0)" "$0}' inputFile | someOtherProcess

awksedgrepcut等工具以及功能更强大的perl比解释的shell脚本更适合这些任务。

答案 2 :(得分:1)

每个字符串的count个字节的perl解决方案:

perl -p -e '
use Encode;
print length(Encode::encode_utf8($_))."\n";$_=""' 

例如:

dd if=/dev/urandom bs=1M count=100 |
   perl -p -e 'use Encode;print length(Encode::encode_utf8($_))."\n";$_=""' |
   tail

对我来说是7.7Mb / s

比较使用的脚本数量:

dd if=/dev/urandom bs=1M count=100 >/dev/null

以9.1Mb / s的速度运行

似乎脚本不是那么慢:)。

答案 3 :(得分:0)

不确定你的脚本应该做什么。所以这可能不是你问题的答案,而是更多的通用提示。

不要cat您的文件并将其传输到您的脚本,而是从带有bash脚本的文件中读取时请执行以下操作:

while read line    
do    
    echo $line
done <file.txt