この回答( sedでファイルの最初の行を削除するにはどうすればよいですか? )では、ファイルの最初のレコードを削除する2つの方法があります。
sed '1d' $file >> headerless.txt
** ---------------- OR ---------------- **
tail -n +2 $file >> headerless.txt
個人的には、tail
オプションは見た目が美しく、読みやすいと思いますが、おそらく私はセドに挑戦しているからでしょう。
どの方法が最速ですか?
sed
とtail
のパフォーマンスsed
は非常に強力で汎用性がありますが、特に多くの行を含む大きなファイルの場合、これが遅くなる原因です。
tail
は単純なことを1つだけ実行しますが、その1つは、多くの行がある大きなファイルであっても、適切かつ高速に実行します。
中小規模のファイルの場合、sed
およびtail
は同様に高速に実行されます(または、期待に応じて低速になります)。ただし、大きな入力ファイル(複数のMB)の場合、パフォーマンスの違いは大幅に大きくなり(数百MBの範囲のファイルでは1桁)、tail
は明らかにsed
を上回ります。
分析するコマンドは次のとおりです。
sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null
パフォーマンスのボトルネックとしての端末出力またはファイル書き込みを排除するために、毎回/dev/null
に出力をパイプしていることに注意してください。
潜在的なボトルネックとしてのディスクI/Oを排除するために、RAMディスクをセットアップしましょう。個人的にtmpfs
を/tmp
にマウントしているので、この実験のためにtestfile
をそこに置いただけです。
次に、このコマンドを使用して、ランダムな行の長さとランダムなデータを含む指定量の行$numoflines
を含むランダムなテストファイルを作成します(それは明らかに最適ではないことに注意してください。気にしています、それは私たちが分析しているものではありません):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
ああ、ところで。私のテスト用ラップトップは、Intel i5-6200U CPUでUbuntu 16.04、64ビットを実行しています。比較のためだけに。
testfile
のセットアップ:numoflines=10000000
で上記のコマンドを実行すると、600 MBを少し超える10M行を含むランダムファイルが作成されました。
$ wc -l testfile
10000000 testfile
$ du -h testfile
611M testfile
$ head -n 3 testfile
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO
testfile
で時限実行を実行します。では、まず両方のコマンドを使用して1回だけ実行し、どの程度の大きさで作業しているかを推定しましょう。
$ time sed '1d' testfile > /dev/null
real 0m2.104s
user 0m1.944s
sys 0m0.156s
$ time tail -n +2 testfile > /dev/null
real 0m0.181s
user 0m0.044s
sys 0m0.132s
大きなファイルの場合、すでに明確な結果が得られています。tail
は、sed
よりも高速です。ただし、楽しみのために、また大きな違いをもたらすランダムな副作用がないことを確認するために、100回行ってみましょう。
$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real 3m36.756s
user 3m19.756s
sys 0m15.792s
$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real 0m14.573s
user 0m1.876s
sys 0m12.420s
結論は変わりません。sed
は大きなファイルの最初の行を削除するには非効率的であり、tail
を使用する必要があります。
そして、はい、Bashのループ構造が遅いことは知っていますが、ここでは比較的少数の反復しか行っておらず、とにかくsed
/tail
ランタイムに比べて単純なループにかかる時間は重要ではありません。
testfile
のセットアップ:完全を期すために、kBの範囲に小さな入力ファイルがあるというより一般的なケースを見てみましょう。次のように、numoflines=100
を使用してランダムな入力ファイルを作成しましょう。
$ wc -l testfile
100 testfile
$ du -h testfile
8,0K testfile
$ head -n 3 testfile
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly
testfile
で時限実行を実行します。このような小さなファイルのタイミングは経験から数ミリ秒の範囲にあると予想できるため、すぐに1000回の反復を実行してみましょう。
$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real 0m7.811s
user 0m0.412s
sys 0m7.020s
$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real 0m7.485s
user 0m0.292s
sys 0m6.020s
ご覧のとおり、タイミングは非常に似ており、解釈したり、疑問に思うことはあまりありません。小さいファイルの場合、両方のツールが同様に適しています。
Bashビルトインとcat
のみを使用する別の代替方法を次に示します。
{ read ; cat > headerless.txt; } < $file
$file
は{ }
コマンドグループにリダイレクトされます。 read
は、単に最初の行を読み取って破棄します。次に、ストリームの残りの部分はcat
にパイプされ、宛先ファイルに書き込まれます。
Ubuntu 16.04では、これとtail
ソリューションのパフォーマンスは非常に似ています。 seq
を使用して大きなテストファイルを作成しました。
$ seq 100000000 > 100M.txt
$ ls -l 100M.txt
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$
tail
ソリューション:$ time tail -n +2 100M.txt > headerless.txt
real 0m1.469s
user 0m0.052s
sys 0m0.784s
$
cat
/braceソリューション:$ time { read ; cat > headerless.txt; } < 100M.txt
real 0m1.877s
user 0m0.000s
sys 0m0.736s
$
しかし、今のところ便利なのはUbuntu VMだけで、両方とも同じ球場にいるにもかかわらず、両方のタイミングに大きな違いが見られます。
私のシステムを試して、各コマンドの前にtime
を付けると、次の結果が得られました。
sed:
real 0m0.129s
user 0m0.012s
sys 0m0.000s
と尾:
real 0m0.003s
user 0m0.000s
sys 0m0.000s
これは、Ubuntu 16.04を実行している少なくともAMD FX 8250のシステムでは、tailが大幅に高速であることを示唆しています。テストファイルには、540kのサイズで10,000行ありました。ファイルはHDDから読み取られました。
プログラムの実行中にシステムで実行されるのはsed
とtail
だけではないため、どちらが優れているかを客観的に示す方法はありません。ディスクI/O、ネットワークI/O、優先度の高いプロセスのCPU割り込みなど、多くの要素がすべて、プログラムの実行速度に影響します。
どちらもCで書かれているため、これは言語の問題ではなく、環境の問題です。たとえば、SSDを使用しているため、システム上ではマイクロ秒単位で時間がかかりますが、HDDが大幅に遅いため、ハードドライブ上の同じファイルの場合は時間がかかります。したがって、これでもハードウェアが役割を果たします。
どのコマンドを選択するかを検討する際に留意すべきことがいくつかあります。
sed
は、テキストを変換するためのストリームエディターです。 tail
は、特定のテキスト行を出力するためのものです。行を処理してそれらを出力するだけの場合は、tail
を使用します。テキストを編集する場合は、sed
を使用します。tail
はsed
よりもはるかに単純な構文なので、自分で読めるものと他の人が読めるものを使用してください。もう1つの重要な要素は、処理しているデータの量です。小さなファイルを使用しても、パフォーマンスに違いはありません。大きなファイルを処理している場合、この画像は興味深いものになります。 2 GBのBIGFILE.txtを使用すると、sed
にはtail
よりもはるかに多くのシステムコールがあり、実行速度がかなり遅いことがわかります。
bash-4.3$ du -sh BIGFILE.txt
2.0G BIGFILE.txt
bash-4.3$ strace -c sed '1d' ./BIGFILE.txt > /dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
59.38 0.079781 0 517051 read
40.62 0.054570 0 517042 write
0.00 0.000000 0 10 1 open
0.00 0.000000 0 11 close
0.00 0.000000 0 10 fstat
0.00 0.000000 0 19 mmap
0.00 0.000000 0 12 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 2 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 1 1 ioctl
0.00 0.000000 0 7 7 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 getrlimit
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 Arch_prctl
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00 0.134351 1034177 11 total
bash-4.3$ strace -c tail -n +2 ./BIGFILE.txt > /dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
62.30 0.148821 0 517042 write
37.70 0.090044 0 258525 read
0.00 0.000000 0 9 3 open
0.00 0.000000 0 8 close
0.00 0.000000 0 7 fstat
0.00 0.000000 0 10 mmap
0.00 0.000000 0 4 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 1 1 ioctl
0.00 0.000000 0 3 3 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 Arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00 0.238865 775615 7 total
一番上の答えは、> /dev/null
を行う際にディスクを考慮していませんでした
大きなファイルがあり、ディスク上に一時的な複製を作成したくない場合は、vim -c
を試してください
$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile
real 0m59.053s
user 0m9.625s
sys 0m48.952s
$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'
real 0m8.259s
user 0m3.640s
sys 0m3.093s
編集:ファイルが利用可能なメモリよりも大きい場合vim -c
が機能しない場合、ファイルの増分ロードを行うには十分に賢くないように見えます
他の答えは、最初の行が欠落している新しいファイルを作成する方が良いことをよく示しています。ただし、新しいファイルを作成するのではなく、ファイルを編集する場合は、ed
のほうが新しいファイルを作成する必要がないため、より高速になるはずです。しかし、ed
を使用して行を削除する方法を検索する必要があります。これは、一度だけ使用したためです。