2012-12-07 19 views
2

一个简单的测试脚本,在这里:Bash一边读LINE一边比较猫慢,为什么?

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的输入行我需要做一些更复杂的脚本操作(关闭并打开一些数据正在馈送的管道)。

+0

除了bash脚本和编译工具之间的区别之外(请参阅paxdiablo的答案),您的比较不公平:cat只是在您的脚本执行一些计算时进行读取(行计数) – Matteo

+0

替换LINECOUNT = $(($ LINECOUNT + 1))''用'((LINECOUNT ++))' –

+0

也为了实际比较,您需要从脚本中删除条件,现在您的问题如下所示:'当我试图运输20tonns木材,当我运行它没有拖车它使用十倍以下!' –

回答

8

原因while read是如此之慢是,壳需要为每个字节进行一次系统调用。它不能从管道读取大量缓冲区,因为shell不能从输入流中读取多行,因此必须将每个字符与换行符进行比较。如果在while read循环中运行strace,则可以看到此行为。这种行为是可取的,因为它使得能够可靠地做这样的事情:

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

在循环内的命令是从同一个流的外壳从读读书。如果shell通过读取大型缓冲区来消耗大量数据,则内部命令将无法访问该数据。不幸的副作用是read是荒谬的缓慢。

3

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

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

符合你“打印每1000行”样本。

如果您通过另一个进程想(每行)输出的字符,随后的线本身的行数和管道,你也能做到这一点:

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

工具,比如awksedgrep,cut和功能更强大的perl比解释的shell脚本更适合这些任务。

+0

经过1Gb的输入线后,我需要做一些更复杂的动作,关闭几根管道并重新打开它们。 awk能够让我执行这些更复杂的脚本操作吗? –

+1

'awk',可能不是,但有很多其他工具,这就是为什么你应该问你的_actual_问题,而不是一些示例问题:-) – paxdiablo

+0

尝试使用Perl的任务 –

0

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

从一个bash脚本文件中读取数据时,不要cat您的文件和管道它到你的脚本,而不是像这样做:

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

我正在从curl输入输入通过管道 –

+0

不使用'read -r'是一个问题,不在'echo'中引用变量$ line“'是双倍的。不要使用这个。这是一个非常糟糕的'猫'重新实现。 – tripleee

1

每串的数字节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的

似乎脚本不这么慢:)

相关问题