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

Dav*_*rks 7 linux bash shell performance

一个简单的测试脚本:

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

当我这样做时cat my450klinefile.txt | myscript,CPU锁定为100%,并且每秒可以处理大约1000行.大约5分钟处理cat my450klinefile.txt >/dev/null半秒钟的事情.

有没有更有效的方法来实现这一点.我只需要从stdin读取一行,计算字节数,然后将其写入命名管道.但即便是这个例子的速度也很慢.

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

Wil*_*ell 14

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

while read size; do dd bs=$size count=1 of=file$(( i++ )); done
Run Code Online (Sandbox Code Playgroud)

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


pax*_*blo 5

这是因为bash在这种情况下,脚本是经过解释的,并未真正针对速度进行优化。通常,最好使用以下外部工具之一:

awk 'NR%1000==0{print}' inputFile
Run Code Online (Sandbox Code Playgroud)

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

如果要(对于每行)输出以字符为单位的行数,然后输出该行本身,并通过另一进程将其传递给管道,则您也可以这样做:

awk '{print length($0)" "$0}' inputFile | someOtherProcess
Run Code Online (Sandbox Code Playgroud)

之类的工具awksedgrepcut和更强大的perl是更加适合于这些任务不是解释shell脚本。

  • `awk`,可能不是,但还有很多其他工具,这就是为什么你应该问你的 _actual_ 问题而不是一些示例问题 :-) (2认同)