Hangisi dosyadaki ilk satırı silmek için daha hızlıdır? Sed veya tail?


14

Bu cevapta ( sed ile bir dosyanın ilk satırını nasıl kaldırabilirim? ) Bir dosyadaki ilk kaydı silmenin iki yolu vardır:

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

** ---------------- VEYA ---------------- **

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

Şahsen bu tailseçenek kozmetik olarak daha hoş ve daha okunabilir olduğunu düşünüyorum ama muhtemelen zorlandığım için.

Hangi yöntem en hızlı?


5
Bir cevap değil, ancak olası bir husus seddaha taşınabilir olmasıdır: tailGNU kullanan tailancak BSD üzerinde çalışmayan Ubuntu üzerinde iyi çalışmalar için "+2" tail.
John N

@JohnN tailplatformlar arası uyumluluk eksikliğini paylaştığınız için teşekkür ederiz .
WinEunuuchs2Unix

3
@John N "+2" kuyrukta çalışıyor, Sierra'yı çalıştıran ve BSD kuyruk komutunu kullandığını iddia eden Mac
Nick Sillito

Urgh, oldukça haklısın - sadece tekrar çalıştırdım ve bu sefer girişi kontrol ettim. Bunu ilk defa yapmalıydım. O da POSIX. / utanıyor.
John N

2
@JohnN Tamamen yanlış değilsin. Geçmişte, UNIX -nseçeneği sunmamış ve sözdizimini kullanmıştı tail +2 $file. Bkz. Freebsd.org/cgi/… Modern BSD'lerden biri yerine bunu düşünüyor olabilirsiniz.
HVD

Yanıtlar:


28

Bir dosyanın ilk satırını kaldırmak için sedvs.'nin performansıtail

TL; DR

  • sed çok güçlü ve çok yönlüdür, ancak özellikle çok satırlı büyük dosyalar için onu yavaşlatan şey budur.

  • tail sadece tek bir basit şey yapar, ama bunu çok satırlı daha büyük dosyalar için bile iyi ve hızlı yapar.

Küçük ve orta boyutlu dosyalar için sedve tailbenzer şekilde hızlı (veya beklentilerinize bağlı olarak yavaş) performans gösteriyorlar. Bununla birlikte, daha büyük giriş dosyaları (çoklu MB'ler) için, performans farkı belirgin tailşekilde daha iyi performans göstererek önemli ölçüde büyür (yüzlerce MB aralığındaki dosyalar için bir büyüklük sırası) sed.

Deney

Genel Hazırlıklar:

Analiz etme komutlarımız:

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

Ben çıkış boru ediyorum unutmayın /dev/nullperformans darboğaz olarak, terminal çıkış veya dosya yazar ortadan kaldırmak için her zaman.

Potansiyel darboğaz olarak disk G / Ç'sini ortadan kaldırmak için bir RAM diski ayarlayalım. Şahsen birtmpfs monte var, /tmpbu yüzden sadece testfilebu deney için oraya yerleştirdim .

Sonra bir kez belirtilen miktarda satır içeren rastgele bir test dosyası oluşturuyorum $numoflines bu komutu kullanarak rastgele çizgi uzunluğu ve rastgele veri ile (kesinlikle optimal olmadığını unutmayın, yaklaşık> 2M hatları için gerçekten yavaş olur, ama kimin umurunda değil, analiz ettiğimiz şey):

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

Oh, btw. test dizüstü bilgisayarım Intel i5-6200U CPU'da Ubuntu 16.04, 64 bit çalıştırıyor. Sadece karşılaştırma için.

Büyük dosyaların zamanlaması:

Devasa bir kurulum testfile :

Yukarıdaki komutu numoflines=1000000010M satırları içeren rastgele bir dosya üretti, 600 MB'tan biraz işgal etti - oldukça büyük, ancak başlayalım, çünkü şunları yapabiliriz:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

Zamanlamalı koşuyu büyük testfile :

Şimdi ne büyüklükte çalıştığımızı tahmin etmek için önce her iki komutla da tek bir zamanlı çalışma yapalım.

$ 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

Büyük dosyalar için zaten çok net bir sonuç görüyoruz, taildaha hızlı bir büyüklük sed. Ancak sadece eğlence için ve büyük bir fark yaratan rastgele bir yan etki olmadığından emin olmak için, 100 kez yapalım:

$ 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

Sonuç aynı kalır, sedbüyük bir dosyanın ilk satırını kaldırmak için yetersiz, tailorada kullanılmalıdır.

Ve evet, Bash'ın döngü yapılarının yavaş olduğunu biliyorum, ancak burada sadece nispeten az sayıda yineleme yapıyoruz ve düz bir döngünün aldığı zaman, sed /tail runtimes'a .

Küçük dosyaların zamanlaması:

Küçük bir kurulum testfile :

Şimdi tamlık için, kB aralığında küçük bir giriş dosyanız olduğu daha yaygın olan duruma bakalım. Şu şekilde numoflines=100görünen rastgele bir girdi dosyası oluşturalım :

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

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

Zamanlı çalışmayı küçük testfile :

Bu tür küçük dosyaların zamanlamalarının deneyimden birkaç milisaniye aralığında olmasını beklediğimizden, hemen 1000 yineleme yapalım:

$ 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

Gördüğünüz gibi, zamanlamalar oldukça benzer, yorumlanacak veya merak edilecek çok şey yok. Küçük dosyalar için, her iki araç da eşit derecede uygundur.


Cevap verdiğiniz için +1 teşekkür ederiz. Bunu awkyapabilecek Serg'in yorumuna dayanarak orijinal soruyu (üzgünüm) düzenledim . Orijinal sorum ilk etapta bulduğum bağlantıya dayanıyordu. Ben kaldırmalısınız eğer bütün yoğun çalışmalardan sonra tavsiye memnun awkyalnızca orijinal proje kapsamında bir çözüm aday ve dönüş odak noktası olarak sedve tail.
WinEunuuchs2Unix

Bu hangi sistem? Mac bilgisayarımda (böylece BSD araçları), / usr / share / dict / words üzerinde test yapmak bana sed için 0.09s ve tail için 0.19s (ve awk 'NR > 1'ilginç bir şekilde) verir.
Kevin

5

İşte sadece bash yerleşiklerini kullanan başka bir alternatif cat:

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

$file{ }komut grubuna yönlendirilir . readBasitçe okur ve ilk satırı atar. Akışın geri kalanı cat, hedef dosyaya yazan boruya bağlanır .

Ubuntu 16.04'ümde bunun ve tailçözümün performansı çok benzer. Ben ile büyük bir test dosyası oluşturdum seq:

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

tail çözüm:

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

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

cat/ küme ayracı çözümü:

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

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

Şu anda sadece bir Ubuntu VM'im var ve her ikisi de zamanlamalarında önemli değişiklikler gördüler, ancak hepsi aynı ballparkta.


1
Cevap için +1 teşekkür ederim. Bu çok ilginç bir çözüm ve parantezleri ve bash'ın hiyerarşi düzeni yoluyla sağdan sola okumayı seviyorum. (doğru ifade ettiğimden emin değilim). Cevabınızı, yeterince kolaysa giriş dosyasının boyutu ve zamanlama kıyaslama sonuçları ile güncellemek mümkün müdür?
WinEunuuchs2Unix

@ WinEunuuchs2Unix Zamanlamalar eklendi, ancak bu bir sanal makinede olduğu için çok güvenilir değiller. Şu anda kullanışlı bir çıplak metal Ubuntu kurulumum yok.
Dijital Travma

Zaten VM'yi VM ile karşılaştırdığınızda VM vs Bare Metal'in önemli olduğunu düşünmüyorum. Zamanlama kanıtı için teşekkürler. Muhtemelen devam ediyorum tailama yine de readseçenek çok güzel olduğunu düşünüyorum .
WinEunuuchs2Unix

4

Sistemimi denemek ve her komutun önüne time aşağıdaki sonuçları aldım:

sed:

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

ve kuyruk:

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

Bu da, sistemimde Ubuntu 16.04 çalıştıran en azından AMD FX 8250'nin kuyruğunun çok daha hızlı olduğunu gösteriyor. Test dosyası, 540k boyutunda 10.000 satıra sahipti. Dosya bir HDD'den okundu.


Cevap verdiğiniz için +1 teşekkür ederiz. AU Sohbet Odasındaki ayrı bir testte, bir kullanıcı kuyruğunun 61 MB dosyaya sahip bir RAMDisk kullanarak sed'den (21.86 saniye) 10 kat daha hızlı (2.31 saniye) olduğunu gösterdi. Yanıtınızı kod bloklarını uygulamak için düzenledim, ancak kullandığınız dosya boyutuyla da düzenlemek isteyebilirsiniz.
WinEunuuchs2Unix

@Serg Kesinlikle adil, bu sadece anekdot bir cevap ve potansiyel olarak farklı donanım yapılandırmaları, farklı test dosyaları vb.
İle

2
Dosya önbellekte değil, kullanırken sedbu sonuçta bir faktör olabilir, bu onları test ettiğiniz sıradır.
Minix

ne tür bir sistem? Burada başka bir yazıya yorum yaparken, mac'umda sedyaklaşık iki kat daha hızlıydı.
Kevin

1

Hangisinin daha iyi olduğunu söylemenin nesnel bir yolu yoktur, çünkü sedvetail tek şey değildir program yürütülürken bir sistemde kaçak olduğunu. Disk i / o, ağ i / o, yüksek öncelikli işlemler için CPU kesintileri gibi birçok faktör - bunların hepsi programınızın ne kadar hızlı çalışacağını etkiler.

Her ikisi de C dilinde yazılmıştır, bu yüzden bu dil meselesi değil, daha çok çevresel meseledir. Örneğin, SSD'im var ve sistemimde bu mikrosaniye olarak zaman alacak, ancak sabit diskteki aynı dosya için HDD'ler önemli ölçüde yavaş olduğu için daha fazla zaman alacak. Yani donanım da bunda rol oynar.

Hangi komutu seçeceğinizi düşünürken aklınızda bulundurmanız gereken birkaç şey vardır:

  • Amacınız ne ? sedmetin dönüştürmek için akış editörüdür. tailmetnin belirli satırlarının çıktısını almak içindir. Çizgilerle uğraşmak ve sadece bunları yazdırmak istiyorsanız kullanın tail. Metni düzenlemek istiyorsanız, tuşunu kullanın sed.
  • tailsözdiziminden çok daha basit bir sözdizimine sahiptir sed, bu nedenle kendinizi okuyabileceğinizi ve başkalarının okuyabileceğini kullanın.

Bir diğer önemli faktör, işlediğiniz veri miktarıdır. Küçük dosyalar size herhangi bir performans farkı vermez. Büyük dosyalarla uğraşırken resim ilginçleşir. 2 GB BIGFILE.txt ile, bunun sedçok daha fazla sistem çağrısı olduğunu tailve oldukça yavaş çalıştığını görebiliriz.

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

Cevap verdiğiniz için +1 teşekkür ederiz. Ama bu yorumun hangi komutu kullanmam gerektiğine karar
vermeme

@ WinEunuuchs2Unix Peki, hangi komutun daha iyi olduğunu sordunuz, bu yüzden tam olarak bu soruyu cevaplıyorum. Hangi komutu seçeceğiniz size kalmış. Daha tailiyi okuyabiliyorsanız sed- bunu kullanın. Şahsen kullanacağım pythonya da karmaşıklaşabileceğinden awkziyade sed. Ayrıca, performans konusunda endişeleriniz varsa, gerçeğe bakalım - burada mikrosaniye cinsinden sonuçlar görüyorsunuz. Okumaya çalıştığınız gigabaytlık büyük bir dosya olmadığı sürece fark hissetmeyeceksiniz
Sergiy Kolodyazhnyy

Oh ben de bir awkcevap takdir : :) ... Sorum başka bir AU Soru-cevap (link) dayanıyordu ve orada hiç bahsetmediler awk. Zaman farkının küçük dosyalarda nominal olduğunu kabul ediyorum. Sadece iyi alışkanlıklar geliştirmeye çalışıyordum.
WinEunuuchs2Unix

1
WinEunuuchs2Unix Sure @, işte burada: awk 'NR!=1' input_file.txt . Bu, bana yaklaşık 150 milisaniye, her ikisi için de aynı sayıda eşit aynı sonucu verir tailve sed. Ama agian, SSD kullanıyorum, bu yüzden komut değil, sabit sürücü ve CPU olduğunu söyleyebilirim.
Sergiy Kolodyazhnyy

1
@ 1M satırları içeren sadece 60 MB'lık bir dosyayla bile 1000, sed3 dakikadan fazla sürüyor ve tailsadece 20 saniye gerekiyor. Bu o kadar büyük değil aslında, kesinlikle GB aralığında değil.
Bayt Komutanı

1

En iyi yanıt diski yaparken dikkate almadı > /dev/null

büyük bir dosyanız varsa ve diskinizde geçici bir kopya oluşturmak istemiyorsanız 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

Düzenleme: dosya kullanılabilir bellekten büyükse, dosyanın vim -cartımlı bir yükünü yapacak kadar akıllı değil gibi görünüyor


0

Diğer yanıtlar, ilk satır eksik olan yeni bir dosya oluşturmak için neyin daha iyi olduğunu gösterir. Bir dosyayı yeni bir dosya oluşturmak yerine düzenlemek isterseniz, yeni bir dosya edoluşturmaması gerektiğinden bahse girerim . Ama bir satırı nasıl kaldıracağımı araştırmalısınız edçünkü onu sadece bir kez kullandım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.