13

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

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半秒内的事情。

有没有更有效的方法来做到这一点。我只需要从标准输入读取一行,计算字节数,然后将其写入命名管道。但即使是这个例子的速度也慢得令人难以置信。

每 1Gb 的输入行我需要执行一些更复杂的脚本操作(关闭和打开一些数据正在馈送到的管道)。

4

4 回答 4

23

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

while read size; do test "$size" -gt 0 || break; dd bs="$size" count=1 of=file$(( i++ )); done

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

于 2012-12-07T13:38:59.983 回答
5

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

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

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

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

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

awk, sed,grepcut更强大perl的工具比解释的 shell 脚本更适合这些任务。

于 2012-12-07T12:01:56.380 回答
2

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 运行

似乎脚本没那么慢:)

于 2012-12-07T12:34:28.593 回答
-1

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

不要cat您的文件并将其通过管道传输到您的脚本,而是在使用 bash 脚本从文件中读取时这样做:

while read line    
do    
    echo $line
done <file.txt
于 2012-12-07T12:03:50.597 回答