Ich muss die erste Zeile wiederholt mit einem Bash-Skript aus einer riesigen Textdatei entfernen.
Im Moment verwende ich sed -i -e "1d" $FILE
- aber das Löschen dauert ungefähr eine Minute.
Gibt es einen effizienteren Weg, um dies zu erreichen?
Ich muss die erste Zeile wiederholt mit einem Bash-Skript aus einer riesigen Textdatei entfernen.
Im Moment verwende ich sed -i -e "1d" $FILE
- aber das Löschen dauert ungefähr eine Minute.
Gibt es einen effizienteren Weg, um dies zu erreichen?
Antworten:
Versuchen Sie Schwanz :
tail -n +2 "$FILE"
-n x
: Drucken Sie einfach die letzten x
Zeilen aus. tail -n 5
würde Ihnen die letzten 5 Zeilen der Eingabe geben. Das +
Zeichen kehrt das Argument um und lässt tail
alles andere als die ersten x-1
Zeilen drucken . tail -n +1
würde die ganze Datei drucken, tail -n +2
alles außer der ersten Zeile usw.
GNU tail
ist viel schneller als sed
. tail
ist auch für BSD verfügbar und das -n +2
Flag ist für beide Tools konsistent. Weitere Informationen finden Sie in den FreeBSD- oder OS X- Manpages.
Die BSD-Version kann jedoch viel langsamer sein als sed
. Ich frage mich, wie sie das geschafft haben. tail
sollte nur eine Datei Zeile für Zeile lesen, während sed
ziemlich komplexe Vorgänge ausgeführt werden, bei denen ein Skript interpretiert, reguläre Ausdrücke angewendet werden und dergleichen.
Hinweis: Sie könnten versucht sein, zu verwenden
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
Dies gibt Ihnen jedoch eine leere Datei . Der Grund ist, dass die Umleitung ( >
) erfolgt, bevor sie tail
von der Shell aufgerufen wird:
$FILE
tail
tail
Prozesses an weiter$FILE
tail
liest aus dem jetzt leer $FILE
Wenn Sie die erste Zeile in der Datei entfernen möchten, sollten Sie Folgendes verwenden:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
Dadurch &&
wird sichergestellt, dass die Datei bei einem Problem nicht überschrieben wird.
-r
Option verwendet wird. Vielleicht gibt es irgendwo im System eine Puffereinstellung? Oder -n
ist eine 32-Bit-Nummer signiert?
tail
funktioniert dies für jede Dateigröße.
-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Sie können -i verwenden, um die Datei zu aktualisieren, ohne den Operator '>' zu verwenden. Der folgende Befehl löscht die erste Zeile aus der Datei und speichert sie in der Datei.
sed -i '1d' filename
unterminated transform source string
sed -i '1,2d' filename
tail -n +2
. Ich bin mir nicht sicher, warum es nicht die beste Antwort ist.
Für diejenigen, die unter SunOS arbeiten, das kein GNU ist, hilft der folgende Code:
sed '1d' test.dat > tmp.dat
Nein, das ist ungefähr so effizient, wie Sie es sich vorstellen können. Sie könnten ein C-Programm schreiben, das die Arbeit etwas schneller erledigen könnte (weniger Startzeit und weniger Verarbeitungsargumente), aber es tendiert wahrscheinlich zu der gleichen Geschwindigkeit wie sed, wenn Dateien groß werden (und ich gehe davon aus, dass sie groß sind, wenn es eine Minute dauert ).
Ihre Frage leidet jedoch unter dem gleichen Problem wie so viele andere, dass sie die Lösung voraussetzt. Wenn Sie uns im Detail mitteilen, was Sie versuchen und nicht wie , können wir Ihnen möglicherweise eine bessere Option vorschlagen.
Wenn dies beispielsweise eine Datei A ist, die von einem anderen Programm B verarbeitet wird, besteht eine Lösung darin, die erste Zeile nicht zu entfernen, sondern Programm B so zu ändern, dass sie anders verarbeitet wird.
Angenommen, alle Ihre Programme hängen an diese Datei A an, und Programm B liest und verarbeitet derzeit die erste Zeile, bevor sie gelöscht wird.
Sie können Programm B so umgestalten, dass es nicht versucht, die erste Zeile zu löschen, sondern einen dauerhaften (wahrscheinlich dateibasierten) Offset in der Datei A beibehält, sodass es bei der nächsten Ausführung nach diesem Offset-Prozess suchen kann die Linie dort, und aktualisieren Sie den Versatz.
Dann könnte es zu einer ruhigen Zeit (Mitternacht?) Eine spezielle Verarbeitung von Datei A durchführen, um alle aktuell verarbeiteten Zeilen zu löschen und den Versatz auf 0 zurückzusetzen.
Es wird sicherlich schneller für ein Programm sein, eine Datei zu öffnen und zu suchen, als sie zu öffnen und neu zu schreiben. Diese Diskussion setzt natürlich voraus, dass Sie die Kontrolle über Programm B haben. Ich weiß nicht, ob dies der Fall ist, aber es kann andere mögliche Lösungen geben, wenn Sie weitere Informationen bereitstellen.
awk FNR-1 *.csv
ist wahrscheinlich schneller.
Sie können die vorhandenen Dateien bearbeiten: Verwenden Sie einfach das Perl- -i
Flag wie folgt:
perl -ni -e 'print unless $. == 1' filename.txt
Dadurch verschwindet die erste Zeile, wenn Sie fragen. Perl muss die gesamte Datei lesen und kopieren, sorgt jedoch dafür, dass die Ausgabe unter dem Namen der Originaldatei gespeichert wird.
Wie Pax sagte, werden Sie wahrscheinlich nicht schneller werden. Der Grund dafür ist, dass es fast keine Dateisysteme gibt, die das Abschneiden vom Anfang der Datei unterstützen. Dies ist also eine O ( n
) -Operation, bei der n
die Größe der Datei angegeben ist. Was Sie jedoch viel schneller tun können, ist, die erste Zeile mit der gleichen Anzahl von Bytes (möglicherweise mit Leerzeichen oder einem Kommentar) zu überschreiben, was für Sie möglicherweise funktioniert, je nachdem, was Sie genau versuchen (was ist das übrigens?).
Das sponge
Util vermeidet das Jonglieren einer temporären Datei:
tail -n +2 "$FILE" | sponge "$FILE"
sponge
ist in der Tat viel sauberer und robuster als die akzeptierte Lösung ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
)
sponge
puffert die gesamte Datei im Speicher? Das wird nicht funktionieren, wenn es Hunderte von GB sind.
sponge
nimmt es auf, da es eine / tmp- Datei als Zwischenschritt verwendet, die anschließend verwendet wird, um das Original zu ersetzen.
Wenn Sie die Datei an seinem Platz ändern möchten, können Sie immer die Original verwenden ed
statt dessen s treaming Nachfolger sed
:
ed "$FILE" <<<$'1d\nwq\n'
Der ed
Befehl war der ursprüngliche UNIX-Texteditor, bevor es überhaupt Vollbild-Terminals gab, geschweige denn grafische Workstations. Der ex
Editor, am besten bekannt als das, was Sie verwenden , wenn die Eingabe an dem Doppelpunkt prompt in vi
, ist eine ex neigten Version ed
, so viele der gleichen Befehle zu arbeiten. Während ed
es interaktiv verwendet werden soll, kann es auch im Batch-Modus verwendet werden, indem eine Reihe von Befehlen an ihn gesendet wird, was diese Lösung tut.
Die Sequenz <<<$'1d\nwq\n'
nutzt Unterstützung des Schlages - für-strings hier ( <<<
) und POSIX Anführungszeichen ( $'
... '
) einzuspeisen Eingabe in den ed
Befehl , bestehend aus zwei Leitungen: 1d
, die d eletes Linie 1 , und dann wq
, die w Riten die Datei wieder heraus Scheibe und dann q UITS die Bearbeitungssitzung.
sollte die Zeilen mit Ausnahme der ersten Zeile anzeigen:
cat textfile.txt | tail -n +2
Könnte vim verwenden, um dies zu tun:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
Dies sollte schneller sein, da vim beim Prozess nicht die gesamte Datei liest.
+wq!
wenn Ihre Shell Bash ist. Wahrscheinlich nicht, da das !
nicht am Anfang eines Wortes steht, aber die Gewohnheit, Dinge zu zitieren, ist wahrscheinlich überall gut. (Und wenn Sie Super-Effizienz anstreben, indem Sie nicht unnötig zitieren, brauchen Sie auch keine Anführungszeichen 1d
.)
Da es sich so anhört, als könnte ich das Löschen nicht beschleunigen, könnte ein guter Ansatz darin bestehen, die Datei in Stapeln wie diesen zu verarbeiten:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
Der Nachteil davon ist, dass, wenn das Programm in der Mitte beendet wird (oder wenn es eine schlechte SQL darin gibt - was dazu führt, dass der "Prozess" -Teil stirbt oder blockiert), Zeilen vorhanden sind, die entweder übersprungen oder zweimal verarbeitet werden .
(Datei1 enthält Zeilen mit SQL-Code)
Würde es funktionieren, Tail in N-1-Zeilen zu verwenden und diese in eine Datei zu leiten, gefolgt vom Entfernen der alten Datei und dem Umbenennen der neuen Datei in den alten Namen?
Wenn ich dies programmgesteuert tun würde, würde ich die Datei durchlesen und mich nach dem Lesen jeder Zeile an den Dateiversatz erinnern, damit ich an diese Position zurückkehren könnte, um die Datei mit einer Zeile weniger darin zu lesen.