Mana yang lebih cepat untuk menghapus baris pertama dalam file ... sed atau tail?

14

Dalam jawaban ini ( Bagaimana saya bisa menghapus baris pertama file dengan sed? ) Ada dua cara untuk menghapus catatan pertama dalam file:

sed '1d' $file >> headerless.txt

** ---------------- ATAU ----------------**

tail -n +2 $file >> headerless.txt

Secara pribadi saya pikir tailpilihan secara kosmetik lebih menyenangkan dan lebih mudah dibaca tetapi mungkin karena saya tertantang.

Metode mana yang tercepat?

WinEunuuchs2Unix
sumber
5
Bukan jawaban, tetapi kemungkinan pertimbangannya adalah yang sedlebih portabel: "+2" untuk tailberfungsi dengan baik di Ubuntu, yang menggunakan GNU tail, tetapi tidak akan berfungsi pada BSD tail.
John N
@JohnN terima kasih telah berbagi tailkurangnya kompatibilitas lintas platform.
WinEunuuchs2Unix
3
@John N "+2" untuk tail berfungsi dengan baik pada Mei Mac menjalankan Sierra yang mengklaim menggunakan perintah tail BSD
Nick Sillito
Urgh, Anda benar - Saya baru saja menjalankannya dan kali ini memeriksa input. Yang seharusnya saya lakukan pertama kali. Ini juga POSIX. Aku menyelinap pergi, malu-malu.
John N
2
@ JohnN Kamu tidak sepenuhnya salah. Di masa lalu, UNIX tidak memberikan -nopsi, dan menggunakan sintaksis tail +2 $file. Lihat freebsd.org/cgi/… Mungkin Anda memikirkannya daripada salah satu BSD modern.
hvd

Jawaban:

28

Kinerja sedvs. tailuntuk menghapus baris pertama file

TL; DR

  • sed sangat kuat dan serbaguna, tetapi inilah yang membuatnya lambat, terutama untuk file besar dengan banyak baris.

  • tail tidak hanya satu hal sederhana, tetapi yang dilakukannya dengan baik dan cepat, bahkan untuk file yang lebih besar dengan banyak baris.

Untuk file berukuran kecil dan menengah, seddan tailberkinerja sama cepat (atau lambat, tergantung pada harapan Anda). Namun, untuk file input yang lebih besar (beberapa MB), perbedaan kinerja tumbuh secara signifikan (urutan besarnya untuk file dalam kisaran ratusan MB), dengan tailkinerja yang jelas lebih baik sed.

Percobaan

Persiapan Umum:

Perintah kami untuk menganalisis adalah:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

Perhatikan bahwa saya mem-piping output ke /dev/nullsetiap kali untuk menghilangkan output terminal atau file menulis sebagai bottleneck kinerja.

Mari kita mengatur disk RAM untuk menghilangkan disk I / O sebagai hambatan potensial. Saya pribadi sudah tmpfsmemasang di /tmpjadi saya hanya menempatkan saya di testfilesana untuk percobaan ini.

Kemudian saya pernah membuat file uji acak yang berisi jumlah baris tertentu $numoflinesdengan panjang garis acak dan data acak menggunakan perintah ini (perhatikan bahwa itu pasti tidak optimal, itu menjadi sangat lambat untuk sekitar> 2M baris, tetapi siapa yang peduli, itu bukan hal yang kami analisis):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

Oh, btw. laptop uji saya menjalankan Ubuntu 16.04, 64 bit pada CPU Intel i5-6200U. Hanya untuk perbandingan.

Mengatur waktu file besar:

Menyiapkan besar testfile:

Menjalankan perintah di atas dengan numoflines=10000000menghasilkan file acak yang berisi 10M baris, menempati sedikit lebih dari 600 MB - ini cukup besar, tetapi mari kita mulai dengan itu, karena kita dapat:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

Lakukan lari berjangka waktu dengan besar kami testfile:

Sekarang mari kita lakukan hanya menjalankan satu waktu dengan kedua perintah terlebih dahulu untuk memperkirakan dengan apa yang kita kerjakan.

$ 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

Kami sudah melihat hasil yang sangat jelas untuk file besar, tailbesarnya lebih cepat dari sed. Tapi hanya untuk bersenang-senang dan untuk memastikan tidak ada efek samping acak yang membuat perbedaan besar, mari kita lakukan 100 kali:

$ 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

Kesimpulannya tetap sama, sedtidak efisien untuk menghapus baris pertama file besar, tailharus digunakan di sana.

Dan ya, saya tahu konstruksi loop Bash lambat, tapi kami hanya melakukan iterasi yang relatif sedikit di sini dan waktu yang dibutuhkan loop polos tidak signifikan dibandingkan dengan sed/ tailruntimes.

Pengaturan waktu file kecil:

Menyiapkan kecil testfile:

Sekarang untuk kelengkapan, mari kita lihat kasus yang lebih umum bahwa Anda memiliki file input kecil dalam kisaran kB. Mari kita buat file input acak dengan numoflines=100, tampak seperti ini:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

Lakukan lari waktunya dengan kecil kami testfile:

Karena kita dapat mengharapkan pengaturan waktu untuk file kecil seperti itu berada dalam kisaran beberapa milidetik dari pengalaman, mari kita lakukan 1000 iterasi segera:

$ 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

Seperti yang Anda lihat, waktunya sangat mirip, tidak ada banyak untuk menafsirkan atau bertanya-tanya. Untuk file kecil, kedua alat sama-sama cocok.

Komandan Byte
sumber
+1 untuk menjawab terima kasih. Saya mengedit pertanyaan asli (maaf) berdasarkan komentar dari Serg yang awkdapat melakukan ini juga. Pertanyaan asli saya didasarkan pada tautan yang saya temukan di tempat pertama. Setelah semua kerja keras Anda, mohon saran jika saya harus menghapus awksebagai kandidat solusi dan kembali fokus ke lingkup proyek asli hanya seddan tail.
WinEunuuchs2Unix
Sistem apa ini? Di mac saya (jadi alat BSD), pengujian pada / usr / share / dict / words memberi saya 0,09 untuk sed dan 0,19 untuk ekor (dan awk 'NR > 1', yang menarik).
Kevin
5

Berikut alternatif lain, hanya menggunakan bash builtins dan cat:

{ read ; cat > headerless.txt; } < $file

$filediarahkan ke { }pengelompokan perintah. The readhanya membaca dan membuang baris pertama. Sisa dari aliran ini kemudian disalurkan ke catyang menuliskannya ke file tujuan.

Di Ubuntu 16.04 saya, kinerja ini dan tailsolusinya sangat mirip. Saya membuat file uji largish dengan seq:

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail larutan:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/ solusi brace:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

Saya hanya memiliki Ubuntu VM berguna sekarang, dan melihat variasi yang signifikan dalam pengaturan waktu dari keduanya, meskipun mereka semua di ballpark yang sama.

Trauma Digital
sumber
1
+1 untuk jawaban, terima kasih. Itu solusi yang sangat menarik dan saya suka kawat gigi dan membaca kanan ke kiri melalui urutan hierarki bash. (tidak yakin apakah saya mengucapkannya dengan benar). Apakah mungkin untuk memperbarui jawaban Anda dengan ukuran file input dan hasil benchmark waktu jika itu cukup mudah dilakukan?
WinEunuuchs2Unix
@ WinEunuuchs2Unix Timings menambahkan, meskipun mereka tidak terlalu dapat diandalkan karena ini pada VM. Saya tidak memiliki instalasi Ubuntu bare-metal berguna saat ini.
Digital Trauma
Saya tidak berpikir VM vs Bare Metal penting ketika Anda membandingkan VM dengan VM. Terima kasih atas bukti waktunya. Saya mungkin akan pergi dengan tailtetapi masih berpikir readopsi ini sangat keren.
WinEunuuchs2Unix
4

Mencoba di sistem saya, dan mengawali setiap perintah dengan timesaya mendapat hasil berikut:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

dan ekor:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

yang menyarankan bahwa, pada sistem saya setidaknya AMD FX 8250 yang menjalankan Ubuntu 16.04, tail secara signifikan lebih cepat. File tes memiliki 10.000 baris dengan ukuran 540k. File itu dibaca dari HDD.

Nick Sillito
sumber
+1 untuk menjawab terima kasih. Dalam tes terpisah di AU Chatroom satu pengguna menunjukkan ekor 10 kali lebih cepat (2,31 detik) daripada sed (21,86 detik) menggunakan RAMDisk dengan file 61 MB. Saya memang mengedit jawaban Anda untuk menerapkan blok kode tetapi Anda mungkin ingin mengeditnya juga dengan ukuran file yang Anda gunakan.
WinEunuuchs2Unix
@Erg Benar-benar adil bahwa ini hanya jawaban anekdotal, dan berpotensi Anda akan mendapatkan hasil yang berbeda dengan konfigurasi perangkat keras yang berbeda, file uji yang berbeda, dll.
Nick Sillito
2
File yang tidak berada dalam cache, ketika menggunakan sedmungkin memainkan faktor dalam hasil ini, itulah urutan Anda mengujinya.
Minix
sistem apa? Ketika saya mengomentari posting lain di sini, pada mac saya sedsekitar dua kali lebih cepat.
Kevin
1

Tidak ada cara obyektif untuk mengatakan mana yang lebih baik, karena seddan tailbukan satu-satunya hal yang berjalan pada sistem selama eksekusi program. Banyak faktor seperti disk i / o, jaringan i / o, CPU menyela proses prioritas lebih tinggi - semua itu mempengaruhi seberapa cepat program Anda akan berjalan.

Keduanya ditulis dalam bahasa C, jadi ini bukan masalah bahasa, tetapi lebih masalah lingkungan. Sebagai contoh, saya memiliki SSD dan pada sistem saya ini akan membutuhkan waktu dalam mikrodetik, tetapi untuk file yang sama pada hard drive akan membutuhkan lebih banyak waktu karena HDD secara signifikan lebih lambat. Jadi perangkat keras juga berperan dalam hal ini.

Ada beberapa hal yang mungkin perlu Anda ingat ketika mempertimbangkan perintah mana yang harus dipilih:

  • Apa tujuanmu ? sedadalah editor aliran untuk mengubah teks. tailadalah untuk menghasilkan baris teks tertentu. Jika Anda ingin berurusan dengan garis dan hanya mencetaknya, gunakan tail. Jika Anda ingin mengedit teks, gunakan sed.
  • tailmemiliki sintaks yang jauh lebih sederhana daripada itu sed, jadi gunakan apa yang bisa Anda baca sendiri dan apa yang orang lain bisa baca.

Faktor penting lainnya adalah jumlah data yang Anda proses. File kecil tidak akan memberi Anda perbedaan kinerja. Gambar menjadi menarik saat Anda berurusan dengan file besar. Dengan 2GB BIGFILE.txt, kita dapat melihat bahwa sedmemiliki lebih banyak panggilan sistem daripada tail, dan berjalan jauh lebih lambat.

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
Sergiy Kolodyazhnyy
sumber
+1 untuk menjawab terima kasih. Tapi saya tidak yakin komentar ini membantu saya memutuskan perintah mana yang harus saya gunakan ....
WinEunuuchs2Unix
@ WinEunuuchs2Unix Yah, Anda bertanya perintah mana yang lebih baik, jadi saya menjawab pertanyaan itu dengan tepat. Perintah mana yang harus dipilih, terserah Anda. Jika Anda bisa membaca taillebih baik daripada sed- gunakan itu. Saya pribadi akan menggunakan pythonatau awkbukan sedkarena bisa rumit. Selain itu, jika Anda khawatir tentang kinerja, mari kita hadapi kenyataan - Anda melihat hasil dalam mikrodetik di sini. Anda tidak akan merasakan perbedaan kecuali itu file besar freakin dalam kisaran gigabyte yang ingin Anda baca
Sergiy Kolodyazhnyy
Oh, saya juga akan menghargai awkjawaban:) ... Pertanyaan saya didasarkan pada T&J AU lain (di tautan) dan di sana mereka tidak pernah menyebutkan awk. Saya setuju perbedaan waktu nominal pada file kecil. Saya hanya mencoba mengembangkan beberapa kebiasaan baik.
WinEunuuchs2Unix
1
@ WinEunuuchs2Unix Tentu, ini dia: awk 'NR!=1' input_file.txt . Ini memberi saya hasil yang sama, sekitar 150 milidetik, jumlah yang sama untuk keduanya taildan sed. Tapi agian, saya menggunakan SSD, jadi saya akan mengatakan itu adalah hard drive dan CPU yang penting, bukan perintah.
Sergiy Kolodyazhnyy
1
@Serg bahkan dengan hanya file 60 MB yang berisi 1M baris, 1000 berjalan dengan sedwaktu lebih dari 3 menit, sedangkan tailhanya membutuhkan sekitar 20 detik. Itu tidak terlalu besar namun sebenarnya, pasti tidak dalam kisaran GB.
Byte Commander
1

Jawaban teratas tidak memasukkan disk ke dalam akun > /dev/null

jika Anda memiliki file besar dan tidak ingin membuat duplikat sementara pada disk Anda, cobalah 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

Sunting: jika file lebih besar dari memori yang tersedia vim -ctidak berfungsi, sepertinya itu tidak cukup pintar untuk melakukan penambahan file

StevenWernerCS
sumber
0

Jawaban lain menunjukkan dengan baik apa yang lebih baik untuk membuat file baru dengan baris pertama hilang. Jika Anda ingin mengedit file daripada membuat file baru, saya yakin edakan lebih cepat karena seharusnya tidak membuat file baru sama sekali. Tetapi Anda harus mencari cara menghapus garis dengan edkarena saya hanya menggunakannya sekali.

akostadinov
sumber