Was ist schneller, um die erste Zeile in der Datei zu löschen ... sed oder tail?

14

In dieser Antwort ( Wie kann ich die erste Zeile einer Datei mit sed entfernen? ) Gibt es zwei Möglichkeiten, den ersten Datensatz in einer Datei zu löschen:

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

** ---------------- ODER ----------------**

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

Persönlich denke ich, dass die tailOption kosmetisch ansprechender und lesbarer ist, aber wahrscheinlich, weil ich herausgefordert bin.

Welche Methode ist am schnellsten?

WinEunuuchs2Unix
quelle
5
Keine Antwort, aber eine mögliche Überlegung ist, dass sedes portabler ist: "+2" für tailfunktioniert gut unter Ubuntu, das GNU verwendet tail, aber unter BSD nicht funktioniert tail.
John N
@JohnN vielen Dank für die tailfehlende plattformübergreifende Kompatibilität.
WinEunuuchs2Unix
3
@ John N "+2" für Tail funktioniert gut auf Mai Mac mit Sierra, der behauptet, den BSD-Tail-Befehl zu verwenden
Nick Sillito
Urgh, Sie haben völlig recht - ich habe es gerade noch einmal ausgeführt und diesmal die Eingabe überprüft. Was ich beim ersten Mal hätte tun sollen. Es ist auch POSIX. Ich schleiche verlegen davon.
John N
2
@ JohnN Du liegst nicht ganz falsch. In der Vergangenheit hat UNIX diese -nOption nicht bereitgestellt und die Syntax verwendet tail +2 $file. Siehe freebsd.org/cgi/… Möglicherweise haben Sie eher daran gedacht als an eines der modernen BSDs.
HDV

Antworten:

28

Leistung von sedvs. tail, um die erste Zeile einer Datei zu entfernen

TL; DR

  • sed ist sehr leistungsfähig und vielseitig, aber das macht es langsam, besonders für große Dateien mit vielen Zeilen.

  • tail macht nur eine einfache Sache, aber diese macht es gut und schnell, auch für größere Dateien mit vielen Zeilen.

Für kleine und mittlere Dateien sedund tailmit ähnlich hoher Geschwindigkeit (oder geringer Geschwindigkeit, je nach Ihren Erwartungen). Bei größeren Eingabedateien (mehrere MB) nimmt der Leistungsunterschied jedoch erheblich zu (eine Größenordnung für Dateien im Bereich von Hunderten von MB), wobei eine taildeutliche Outperformance erzielt wird sed.

Experiment

Allgemeine Vorbereitungen:

Unsere zu analysierenden Befehle sind:

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

Beachten Sie, dass ich die Ausgabe /dev/nulljedes Mal weiterleite, um die Terminalausgabe oder die Dateischreibvorgänge als Leistungsengpass zu beseitigen.

Richten wir eine RAM-Disk ein, um die Festplatten-E / A als potenziellen Engpass zu beseitigen. Ich persönlich habe ein tmpfsReittier bei, /tmpalso habe ich es einfach testfilefür dieses Experiment hingestellt.

Dann erstelle ich $numoflinesmit diesem Befehl einmal eine zufällige Testdatei mit einer festgelegten Anzahl von Zeilen mit zufälliger Zeilenlänge und zufälligen Daten (beachten Sie, dass dies definitiv nicht optimal ist, da es für über 2 Millionen Zeilen sehr langsam wird, aber wen interessiert das, es ist nicht das was wir analysieren):

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

Oh, übrigens. Auf meinem Test-Laptop läuft Ubuntu 16.04, 64-Bit auf einer Intel i5-6200U-CPU. Nur zum Vergleich.

Timing großer Dateien:

Riesen aufbauen testfile:

Wenn Sie den obigen Befehl mit numoflines=10000000einer zufälligen Datei ausführen, die 10 Millionen Zeilen enthält und etwas mehr als 600 MB belegt - das ist ziemlich umfangreich, aber fangen wir damit an, denn wir können:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

Führen Sie den zeitgesteuerten Lauf mit unserem riesigen testfile:

Lassen Sie uns nun zunächst mit beiden Befehlen einen einzigen zeitgesteuerten Lauf durchführen, um abzuschätzen, mit welchen Größen wir arbeiten.

$ 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

Wir sehen bereits ein wirklich eindeutiges Ergebnis für große Dateien, das tailum eine Größenordnung schneller ist als sed. Aber nur zum Spaß und um sicherzugehen, dass es keine zufälligen Nebenwirkungen gibt, die einen großen Unterschied machen, machen wir es 100 Mal:

$ 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

Die Schlussfolgerung bleibt gleich, sedist ineffizient, um die erste Zeile einer großen Datei zu entfernen, tailsollte dort verwendet werden.

Und ja, ich weiß, dass die Loop-Konstrukte von Bash langsam sind, aber wir machen hier nur relativ wenige Iterationen, und die Zeit, die eine einfache Loop benötigt, ist im Vergleich zu den sed/ tail-Laufzeiten sowieso nicht signifikant .

Timing kleiner Dateien:

Ein kleines einrichten testfile:

Betrachten wir der Vollständigkeit halber den häufigeren Fall, dass Sie eine kleine Eingabedatei im kB-Bereich haben. Lass uns eine zufällige Eingabedatei erstellen numoflines=100, die so aussieht:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

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

Führen Sie den zeitgesteuerten Lauf mit unserem kleinen testfile:

Da wir aus Erfahrung damit rechnen können, dass das Timing für solche kleinen Dateien im Bereich einiger Millisekunden liegt, lassen Sie uns gleich 1000 Iterationen durchführen:

$ 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

Wie Sie sehen, sind die Timings ziemlich ähnlich, es gibt nicht viel zu interpretieren oder sich darüber zu wundern. Für kleine Dateien sind beide Tools gleich gut geeignet.

Byte Commander
quelle
+1 für die Beantwortung danke. Ich habe die ursprüngliche Frage bearbeitet (sorry), basierend auf einem Kommentar von Serg, der awkdies auch kann. Meine ursprüngliche Frage basierte auf dem Link, den ich an erster Stelle gefunden hatte. Bitte geben Sie nach all Ihrer harten Arbeit an, ob ich awkals Lösungskandidat entfernen und den Fokus wieder auf den ursprünglichen Projektumfang von nur sedund legen soll tail.
WinEunuuchs2Unix
Welches System ist das? Wenn ich auf meinem Mac (also BSD-Tools) / usr / share / dict / words teste, erhalte ich 0,09s für sed und 0,19s für tail (und awk 'NR > 1'interessanterweise).
Kevin
5

Hier ist eine weitere Alternative, bei der nur Bash-Builtins verwendet werden cat:

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

$filewird in die { }Befehlsgruppierung umgeleitet . Der readliest und verwirft einfach die erste Zeile. Der Rest des Streams wird dann weitergeleitet catund in die Zieldatei geschrieben.

Auf meinem Ubuntu 16.04 sind die Leistung und die tailLösung sehr ähnlich. Ich habe eine große Testdatei erstellt mit seq:

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

tail Lösung:

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

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

cat/ Klammerlösung:

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

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

Im Moment habe ich allerdings nur eine Ubuntu-VM zur Hand, und ich sah signifikante Unterschiede in den Timings beider, obwohl sie sich alle im selben Baseballstadion befinden.

Digitales Trauma
quelle
1
+1 für die Antwort danke. Das ist eine sehr interessante Lösung, und ich mag die geschweiften Klammern und das Lesen von rechts nach links über die Hierarchie der Bash. (nicht sicher, ob ich das richtig formuliert habe). Ist es möglich, Ihre Antwort mit der Größe der Eingabedatei und den Timing-Benchmark-Ergebnissen zu aktualisieren, wenn dies einfach genug ist?
WinEunuuchs2Unix
@ WinEunuuchs2Unix Timings hinzugefügt, obwohl sie nicht sehr zuverlässig sind, da dies auf einer VM ist. Ich habe momentan keine Bare-Metal-Ubuntu-Installation zur Hand.
Digitales Trauma
Ich denke nicht, dass VM gegen Bare Metal von Bedeutung ist, wenn Sie VM mit VM vergleichen. Danke für den Timing-Beweis. Ich würde wahrscheinlich mitgehen tail, denke aber immer noch, dass die readOption sehr cool ist.
WinEunuuchs2Unix
4

Wenn ich mich auf meinem System versuche und jedem Befehl ein Präfix voranstelle, erhalte timeich die folgenden Ergebnisse:

sed:

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

und Schwanz:

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

was darauf hindeutet, dass auf meinem System, auf dem mindestens AMD FX 8250 mit Ubuntu 16.04 läuft, Tail deutlich schneller ist. Die Testdatei hatte 10.000 Zeilen mit einer Größe von 540k. Die Datei wurde von einer Festplatte gelesen.

Nick Sillito
quelle
+1 für die Beantwortung danke. In einem separaten Test im AU Chatroom zeigte ein Benutzer, dass der Schwanz 10-mal schneller (2,31 Sekunden) ist als sed (21,86 Sekunden), wenn eine RAMDisk mit einer 61-MB-Datei verwendet wird. Ich habe Ihre Antwort bearbeitet, um Codeblöcke anzuwenden, aber Sie möchten sie möglicherweise auch mit der von Ihnen verwendeten Dateigröße bearbeiten.
WinEunuuchs2Unix
@ Serg Absolut fair, dass dies nur eine anekdotische Antwort ist, und möglicherweise würden Sie unterschiedliche Ergebnisse mit unterschiedlichen Hardwarekonfigurationen, unterschiedlichen Testdateien usw. erhalten
Nick Sillito
2
Die Datei, die sich nicht im Cache befindet, spielt bei Verwendung sedmöglicherweise eine Rolle für dieses Ergebnis. Dies ist die Reihenfolge, in der Sie sie getestet haben.
Minix
Was für ein System? Wie ich hier auf einem anderen Beitrag kommentierte, war auf meinem Mac sedetwa doppelt so schnell.
Kevin
1

Es gibt keine objektive Art und Weise zu sagen , was besser ist, da sedund tailsind nicht die einzigen Dinge , die laufen auf einem System während der Programmausführung. Viele Faktoren wie Festplatten-E / A, Netzwerk-E / A, CPU-Interrupts für Prozesse mit höherer Priorität - all diese Faktoren beeinflussen, wie schnell Ihr Programm ausgeführt wird.

Beide sind in C geschrieben, es handelt sich also nicht um ein Sprachproblem, sondern eher um ein Umweltproblem. Zum Beispiel habe ich eine SSD und auf meinem System dauert dies einige Zeit in Mikrosekunden, aber für dieselbe Datei auf der Festplatte dauert es länger, da die Festplatten erheblich langsamer sind. Auch hier spielt Hardware eine Rolle.

Es gibt ein paar Dinge, die Sie beachten sollten, wenn Sie überlegen, welchen Befehl Sie auswählen sollen:

  • Was ist dein Zweck ? sedist ein Stream-Editor zum Transformieren von Text. taildient zur Ausgabe bestimmter Textzeilen. Wenn Sie sich mit Zeilen befassen und diese nur ausdrucken möchten, verwenden Sie tail. Wenn Sie den Text bearbeiten möchten, verwenden Sie sed.
  • tailhat eine weitaus einfachere Syntax als sed, verwenden Sie also, was Sie selbst lesen können und was andere lesen können.

Ein weiterer wichtiger Faktor ist die Datenmenge, die Sie verarbeiten. Kleine Dateien bieten keinen Leistungsunterschied. Das Bild wird interessant, wenn Sie mit großen Dateien arbeiten. Mit einer BIGFILE.txt von 2 GB können wir feststellen, dass seddiese Datei weitaus mehr Systemaufrufe als tailund erheblich langsamer ausgeführt wird.

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
quelle
+1 für die Beantwortung danke. Aber ich bin mir nicht sicher, ob dieser Kommentar mir bei der Entscheidung hilft, welchen Befehl ich verwenden soll ...
WinEunuuchs2Unix
@ WinEunuuchs2Unix Nun, du hast gefragt, welcher Befehl besser ist, also beantworte ich genau diese Frage. Welcher Befehl zu wählen ist, liegt bei Ihnen. Wenn du tailbesser lesen kannst als sed- benutze das. Ich persönlich würde verwenden pythonoder awkeher als sedweil es komplex werden kann. Wenn Sie sich Sorgen um die Leistung machen, lassen Sie uns die Realität betrachten - Sie sehen hier Ergebnisse in Mikrosekunden. Sie werden keinen Unterschied spüren, es sei denn, es ist eine verdammt große Datei im Gigabyte-Bereich, die Sie zu lesen versuchen
Sergiy Kolodyazhnyy
Oh, ich würde mich auch über eine awkAntwort freuen:) ... Meine Frage basierte auf einer anderen AU-Frage und wurde dort nie erwähnt awk. Ich bin damit einverstanden, dass der Zeitunterschied bei kleinen Dateien nominal ist. Ich habe nur versucht, ein paar gute Gewohnheiten zu entwickeln.
WinEunuuchs2Unix
1
@ WinEunuuchs2Unix Sicher, hier ist es: awk 'NR!=1' input_file.txt . Es gibt mir das gleiche Ergebnis, etwa 150 Millisekunden, die gleiche Anzahl für beide tailund sed. Aber agian, ich verwende SSD, also würde ich sagen, dass es auf die Festplatte und die CPU ankommt, nicht auf den Befehl.
Sergiy Kolodyazhnyy
1
@Serg selbst mit einer Datei von nur 60 MB, die 1 MB Zeilen enthält, läuft 1000 mit sedweit über 3 Minuten, wohingegen tailnur etwa 20 Sekunden benötigt werden. Das ist noch nicht so groß, definitiv nicht im GB-Bereich.
Byte Commander
1

Die beste Antwort berücksichtigte das nicht > /dev/null

Wenn Sie eine große Datei haben und kein temporäres Duplikat auf Ihrer Festplatte erstellen möchten, versuchen Sie es 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

Bearbeiten: Wenn die Datei größer als der verfügbare Speicher ist, vim -cfunktioniert dies nicht. Dies scheint nicht intelligent genug zu sein, um die Datei inkrementell zu laden

StevenWernerCS
quelle
0

Andere Antworten zeigen gut, was besser ist, um eine neue Datei mit fehlender erster Zeile zu erstellen. Wenn Sie eine Datei bearbeiten möchten, anstatt eine neue Datei zu erstellen, ist dies wahrscheinlich edschneller, da keine neue Datei erstellt werden sollte. Aber man muss suchen, wie man eine Zeile mit entfernt, edweil ich sie nur einmal verwendet habe.

akostadinov
quelle