2012-12-07 18 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的

似乎腳本不這麼慢:)

相關問題