次のように、約100万行のファイルがあります。
"ID" "1" "2"
"00000687" 0 1
"00000421" 1 0
"00000421" 1 0
"00000421" 1 0
最後の行は100万回以上繰り返されました。 この質問 からインスピレーションを得て、提案されているソリューションのいくつかを試して、どちらが速いかを確認しました。 1つのプロセスのみを使用するソリューションは、1つのプロセスしか使用しないため、パイプラインを使用するソリューションよりも高速であると期待していました。しかし、それらは私のテストの結果です:
tail -n +2 file.txt | tr -d \"
$ time tail -n +2 file.txt | tr -d \" 1> /dev/null
real 0m0,032s
user 0m0,020s
sys 0m0,028s
sed '1d;s/"//g' file.txt
$ time sed '1d;s/"//g' file.txt 1> /dev/null
real 0m0,410s
user 0m0,399s
sys 0m0,011s
Perl -ne ' { s/"//g; print if $. > 1 }' file.txt
$ time Perl -ne ' { s/"//g; print if $. > 1 }' file.txt 1> /dev/null
real 0m0,379s
user 0m0,367s
sys 0m0,013s
私は何度もテストを繰り返しましたが、常に同じような数を取得しています。ご覧のように、 tail -n +2 file.txt | tr -d \"
は他のものよりはるかに速いです。どうして?
要するに、行われている作業の量です。
きみの tail | tr
コマンドは、次のことを実行します。
tail
:tr
で、改行を気にせずに読み取り、「」(固定文字)以外のすべてを出力します。sed
コマンドは、指定されたスクリプトを解釈した後、次のようになります。
与えられたスクリプトを解釈した後、Perlコマンドは次のようになります。
改行を探すことは、大量の入力に対して高価になります。
主な理由は、Perlとsedが各行を個別に処理するためです。
Perlがより大きなブロックで入力を処理し、それを少し簡略化すると(注を参照)、はるかに高速にすることができますが、trほど高速ではありません。
time Perl -ne ' { s/"//g; print if $. > 1 }' file.txt 1> /dev/null
real 0m0.617s
user 0m0.612s
sys 0m0.005s
time Perl -pe 'BEGIN{<>;$/=\40960} s/"//g' file.txt >/dev/null
real 0m0.186s
user 0m0.177s
sys 0m0.009s
time tail -n +2 file.txt | tr -d \" 1> /dev/null
real 0m0.033s
user 0m0.031s
sys 0m0.023s
注:Perl -ne '... if $. > 1'
またはawk 'NR == 1 { ... } /foo/ { ... }'
は使用しないでください。
代わりにBEGIN{<>}
およびBEGIN{getline}
を使用してください。
最初の行を読んだら、後続の行が最初の行にならないことを確信できます。何度も確認する必要はありません。
tail.cのtail_lines():
_ /* Use file_lines only if FD refers to a regular file for
which lseek (... SEEK_END) works. */
if ( ! presume_input_pipe
&& S_ISREG (stats.st_mode)
&& (start_pos = lseek (fd, 0, SEEK_CUR)) != -1
&& start_pos < (end_pos = lseek (fd, 0, SEEK_END)))
_
このend_pos = lseek (fd, 0, SEEK_END)
は、ファイルの内容がスキップされる場所です。 file_lines()には、改行を数える逆方向スキャンがあります。
lseek()は、読み取り/書き込み用にファイルオフセットを再配置するための非常に単純なシステムコールです。
ああ、このQでは微妙なところを見逃してしまったようです;)それは、ラインワイズとブロックワイズの両方を読むことです。通常、複数のパスを1つの複雑なパスに結合することをお勧めします。しかし、ここではアルゴリズムは最初の改行だけを必要とします。
Oleのsysread()
を使用した2部構成のPerlスクリプトは、最初の改行の検索から最大ブロックの読み取りに切り替える方法を示しています。
tail
が通常の逆方向に機能する場合、最後のブロックを読み取り、改行を数えます。そこから印刷するか、最後から2番目のブロックを読み取ります。
Perl
を使用したい気がしますが、遅すぎます。
Perl
は一般的なツールであり、tr
のような特殊なツールほど高速ではありません。ただし、近づくことができます。
$ tail -n +2 file.txt | tr -d \" >/dev/null;
real 0m0.040s
user 0m0.030s
sys 0m0.032s
$ Perl -e 'while(sysread(STDIN,$b,1)) {$b eq "\n" and last}
while(sysread(STDIN,$b,131072)) {
$b=~tr/\"//d; print $b
}' < file.txt > /dev/null;
real 0m0.049s
user 0m0.045s
sys 0m0.004s
あなたはtail
を避けてさらに速く進むことができます:
$ time (read; tr -d \") < file.txt >/dev/null
real 0m0.033s
user 0m0.021s
sys 0m0.012s