-n x: Drucken Sie einfach die letzten xZeilen aus. tail -n 5würde Ihnen die letzten 5 Zeilen der Eingabe geben. Das +Zeichen kehrt das Argument um und lässt tailalles andere als die ersten x-1Zeilen drucken . tail -n +1würde die ganze Datei drucken, tail -n +2alles außer der ersten Zeile usw.
GNU tailist viel schneller als sed. tailist auch für BSD verfügbar und das -n +2Flag 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. tailsollte nur eine Datei Zeile für Zeile lesen, während sedziemlich 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 tailvon der Shell aufgerufen wird:
Shell schneidet Datei ab $FILE
Shell erstellt einen neuen Prozess für tail
Shell leitet stdout des tailProzesses 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:
Laut dieser ss64.com/bash/tail.html beträgt der typische Puffer standardmäßig 32 KB , wenn BSD 'tail' mit der -rOption verwendet wird. Vielleicht gibt es irgendwo im System eine Puffereinstellung? Oder -nist eine 32-Bit-Nummer signiert?
Yzmir Ramirez
41
@Eddie: user869097 sagte, dass es nicht funktioniert, wenn eine einzelne Zeile 15 MB oder mehr ist. Solange die Zeilen kürzer sind, tailfunktioniert dies für jede Dateigröße.
Aaron Digulla
6
Könntest du diese Argumente erklären?
Dreampuf
17
@ Dreampuf - von der Manpage:-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Will Sheppard
11
Ich wollte @JonaChristopherSahnwaldt zustimmen - Schwanz ist um eine Größenordnung viel, viel langsamer als die sed-Variante. Ich teste es in einer Datei mit 500.000.000 Zeilen (nicht mehr als 50 Zeichen pro Zeile). Dann wurde mir jedoch klar, dass ich die FreeBSD-Version von tail verwendete (die standardmäßig mit OS X geliefert wird). Als ich zu GNU Tail wechselte, war der Tail Call zehnmal schneller als der Sed Call (und auch der GNU Sed Call). AaronDigulla ist hier richtig, wenn Sie GNU verwenden.
Dan Nguyen
179
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.
Ich bekomme eine Fehlermeldung:unterminated transform source string
Daniel Kobe
10
Dies funktioniert jedes Mal und sollte wirklich die beste Antwort sein!
xtheking
4
Zur Erinnerung: Für den Mac muss ein Suffix angegeben werden, wenn sed mit direkten Änderungen verwendet wird. Führen Sie die obigen Schritte mit -i.bak
mjp
3
Nur eine Notiz - um mehrere Zeilen zu entfernen, verwenden Siesed -i '1,2d' filename
The Godfather
4
Diese Version ist wirklich viel lesbarer und universeller als tail -n +2. Ich bin mir nicht sicher, warum es nicht die beste Antwort ist.
Luke Davis
74
Für diejenigen, die unter SunOS arbeiten, das kein GNU ist, hilft der folgende Code:
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.
Ich denke, das OP versucht zu erreichen, warum ich diese Frage gefunden habe. Ich habe 10 CSV-Dateien mit jeweils 500.000 Zeilen. Jede Datei hat dieselbe Kopfzeile wie die erste Zeile. Ich fasse diese Dateien in eine Datei zusammen und importiere sie dann in eine Datenbank, damit die Datenbank Spaltennamen aus der ersten Zeile erstellen kann. Natürlich möchte ich nicht, dass diese Zeile in Datei 2-10 wiederholt wird.
DB
1
@db In diesem Fall awk FNR-1 *.csvist wahrscheinlich schneller.
Jinawee
10
Sie können die vorhandenen Dateien bearbeiten: Verwenden Sie einfach das Perl- -iFlag 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 ndie 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?).
Betreff "... fast keine Dateisysteme, die das Abschneiden unterstützen ..." : das ist interessant; Bitte fügen Sie eine Anmerkung in Klammern hinzu, in der ein solches Dateisystem genannt wird.
Agc
1
@agc: Jetzt irrelevant, aber mein erster Job in den 70ern war bei Quadex, einem kleinen Startup (jetzt weg und unabhängig von den beiden Unternehmen, die jetzt diesen Namen verwenden). Sie hatten ein Dateisystem, das das Hinzufügen oder Entfernen am Anfang oder Ende einer Datei ermöglichte und hauptsächlich zum Implementieren der Bearbeitung in weniger als 3 KB verwendet wurde, indem Dateien über und unter Fenster eingefügt wurden. Es hatte keinen eigenen Namen, es war nur ein Teil von QMOS, dem Quadex Multiuser-Betriebssystem. ('Multi' war normalerweise 2-3 auf einem LSI-11/02 mit weniger als 64 KB RAM und normalerweise ein paar 8 "-Disketten vom Typ RX01 mit jeweils 250 KB.) :-)
dave_thompson_085
9
Das spongeUtil vermeidet das Jonglieren einer temporären Datei:
spongeist in der Tat viel sauberer und robuster als die akzeptierte Lösung ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE")
Jealie
1
Es sollte klargestellt werden, dass für "Schwamm" das Paket "moreutils" installiert werden muss.
FedFranzoni
Dies ist die einzige Lösung, mit der ich eine Systemdatei (auf einem Debian-Docker-Image) ändern konnte. Andere Lösungen sind aufgrund des Fehlers "Gerät oder Ressource ausgelastet" beim Versuch, die Datei zu schreiben, fehlgeschlagen.
FedFranzoni
Aber spongepuffert die gesamte Datei im Speicher? Das wird nicht funktionieren, wenn es Hunderte von GB sind.
OrangeDog
@OrangeDog, Solange das Dateisystem es speichern kann, spongenimmt es auf, da es eine / tmp- Datei als Zwischenschritt verwendet, die anschließend verwendet wird, um das Original zu ersetzen.
Agc
8
Wenn Sie die Datei an seinem Platz ändern möchten, können Sie immer die Original verwenden edstatt dessen s treaming Nachfolger sed:
ed "$FILE"<<<$'1d\nwq\n'
Der edBefehl war der ursprüngliche UNIX-Texteditor, bevor es überhaupt Vollbild-Terminals gab, geschweige denn grafische Workstations. Der exEditor, 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 edes 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 edBefehl , 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.
@niglesiais Ich bin mit der "nutzlosen Verwendung von Katze" nicht einverstanden, da klar ist, dass diese Lösung für weitergeleitete Inhalte und nicht nur für Dateien in Ordnung ist.
Titou
5
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.
Möglicherweise müssen Sie das zitieren, +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.)
Mark Reed
vim muss die gesamte Datei lesen. Wenn die Datei größer als der Speicher ist, wie in dieser Frage beschrieben, liest vim die gesamte Datei und schreibt sie (oder den größten Teil davon) in eine temporäre Datei. Nach dem Bearbeiten wird alles zurückgeschrieben (in die permanente Datei). Ich weiß nicht, wie Sie denken, dass es ohne dies möglicherweise funktionieren könnte .
Diese Syntax würde auch funktionieren, aber nur zwei Ausgabedateien anstelle von drei generieren : csplit file /^.*$/1. Oder einfacher : csplit file //1. Oder noch einfacher : csplit file 2.
Marco Roy
1
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 .
Was enthält die erste Zeile? Kannst du es einfach mit einem SQL-Kommentar überschreiben, wie ich es in meinem Beitrag vorgeschlagen habe?
Robert Gamble
0
Wenn Sie nach einem Fehler eine Wiederherstellung durchführen möchten, können Sie einfach eine Datei erstellen, die das enthält, was Sie bisher getan haben.
if[[-f $tmpf ]];then
rm -f $tmpf
fi
cat $srcf |while read line ;do# process line
echo "$line">> $tmpf
done
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.
Die erste Lösung ist im Wesentlichen identisch mit der, die Brent jetzt macht. Ich verstehe Ihren programmatischen Ansatz nicht, nur die erste Zeile muss gelöscht werden. Sie würden einfach die erste Zeile lesen und verwerfen und den Rest in eine andere Datei kopieren, die wiederum mit den Ansätzen sed und tail identisch ist.
Robert Gamble
Die zweite Lösung hat zur Folge, dass die Datei nicht jedes Mal um die erste Zeile verkleinert wird. Das Programm verarbeitet es einfach so, als wäre es geschrumpft, beginnt aber jedes Mal in der nächsten Zeile
EvilTeach
Ich verstehe immer noch nicht, was Ihre zweite Lösung ist.
Antworten:
Versuchen Sie Schwanz :
-n x
: Drucken Sie einfach die letztenx
Zeilen aus.tail -n 5
würde Ihnen die letzten 5 Zeilen der Eingabe geben. Das+
Zeichen kehrt das Argument um und lässttail
alles andere als die erstenx-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 alssed
.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ährendsed
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
Dies gibt Ihnen jedoch eine leere Datei . Der Grund ist, dass die Umleitung (
>
) erfolgt, bevor sietail
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:
Dadurch
&&
wird sichergestellt, dass die Datei bei einem Problem nicht überschrieben wird.quelle
-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.
quelle
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:
quelle
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.
quelle
awk FNR-1 *.csv
ist wahrscheinlich schneller.Sie können die vorhandenen Dateien bearbeiten: Verwenden Sie einfach das Perl-
-i
Flag wie folgt: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.
quelle
Sie können dies leicht tun mit:
in der Kommandozeile; oder um die erste Zeile einer Datei dauerhaft zu entfernen, verwenden Sie den In-Place-Modus von sed mit dem
-i
Flag:quelle
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 dern
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?).quelle
Das
sponge
Util vermeidet das Jonglieren einer temporären Datei:quelle
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 Nachfolgersed
:Der
ed
Befehl war der ursprüngliche UNIX-Texteditor, bevor es überhaupt Vollbild-Terminals gab, geschweige denn grafische Workstations. Derex
Editor, am besten bekannt als das, was Sie verwenden , wenn die Eingabe an dem Doppelpunkt prompt invi
, ist eine ex neigten Versioned
, so viele der gleichen Befehle zu arbeiten. Währended
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 dened
Befehl , bestehend aus zwei Leitungen:1d
, die d eletes Linie 1 , und dannwq
, die w Riten die Datei wieder heraus Scheibe und dann q UITS die Bearbeitungssitzung.quelle
sollte die Zeilen mit Ausnahme der ersten Zeile anzeigen:
quelle
Könnte vim verwenden, um dies zu tun:
Dies sollte schneller sein, da vim beim Prozess nicht die gesamte Datei liest.
quelle
+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ührungszeichen1d
.)Wie wäre es mit csplit?
quelle
csplit file /^.*$/1
. Oder einfacher :csplit file //1
. Oder noch einfacher :csplit file 2
.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:
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)
quelle
Wenn Sie nach einem Fehler eine Wiederherstellung durchführen möchten, können Sie einfach eine Datei erstellen, die das enthält, was Sie bisher getan haben.
quelle
Dieser eine Liner reicht aus:
Es funktioniert, da
tail
es vor ausgeführt wirdecho
und dann die Datei entsperrt wird, sodass keine temporäre Datei erforderlich ist.quelle
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.
quelle