13

ここに簡単なテストスクリプトがあります:

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

私が行うとcat my450klinefile.txt | myscript、CPUは100%でロックアップし、1秒間に約1000行を処理できます。0.5秒で何をするかを処理するのに約5分cat my450klinefile.txt >/dev/null

本質的にこれを行うためのより効率的な方法はありますか?stdinから行を読み取り、バイトをカウントして、名前付きパイプに書き出すだけです。しかし、この例でさえ速度は信じられないほど遅いです。

1Gbの入力行ごとに、いくつかのより複雑なスクリプトアクションを実行する必要があります(データがフィードされているいくつかのパイプを閉じたり開いたりします)。

4

4 に答える 4

23

非常に遅い理由while readは、シェルがすべてのバイトに対してシステムコールを行う必要があるためです。シェルは入力ストリームから複数の行を読み取ってはならず、したがって各文字を改行と比較する必要があるため、パイプから大きなバッファーを読み取ることはできません。ループで実行するstraceと、この動作を確認できます。while readこの動作は、次のようなことを確実に実行できるため、望ましいものです。

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

ループ内のコマンドは、シェルが読み取るのと同じストリームから読み取っています。シェルが大きなバッファを読み取ることによって大量のデータを消費した場合、内部コマンドはそのデータにアクセスできません。不幸な副作用は、それ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、解釈されたシェルスクリプトよりも、これらのタスクにはるかに適しています。grepcutperl

于 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 に答える