web-dev-qa-db-de.com

Wie kann ich die erste Zeile einer Textdatei mit dem Bash/Sed-Skript entfernen?

Ich muss die erste Zeile mit einem Bash-Skript wiederholt aus einer großen 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?

449
Brent

Probiere GNU tail :

tail -n +2 "$FILE"

-n x: Drucken Sie einfach die letzten x Zeilen. tail -n 5 würde Ihnen die letzten 5 Zeilen der Eingabe geben. Das Zeichen + invertiert das Argument und macht tail etwas anderes als die ersten x-1-Zeilen. tail -n +1 würde die gesamte Datei drucken, tail -n +2 alles außer der ersten Zeile usw.

GNU tail ist viel schneller als sed. tail ist auch in BSD verfügbar und das -n +2-Flag ist in beiden Tools konsistent. Weitere Informationen finden Sie in den Manpages FreeBSD oder OS X

Die BSD-Version kann jedoch viel langsamer als sed sein. Ich frage mich, wie sie das geschafft haben. tail sollte eine Datei nur Zeile für Zeile lesen, während sed ziemlich komplexe Vorgänge ausführt, bei denen ein Skript interpretiert, reguläre Ausdrücke und dergleichen angewendet werden.

Hinweis: Sie könnten versucht sein, es zu benutzen

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

dies gibt Ihnen jedoch ein leere Datei. Der Grund ist, dass die Umleitung (>) erfolgt, bevor tail von der Shell aufgerufen wird:

  1. Shell schneidet die Datei $FILE ab
  2. Shell erstellt einen neuen Prozess für tail
  3. Shell leitet stdout des tail-Prozesses an $FILE weiter
  4. tail liest aus dem jetzt leeren $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"

&& sorgt dafür, dass die Datei bei Problemen nicht überschrieben wird.

866
Aaron Digulla

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
119
amit

Für diejenigen, die sich auf SunOS, einem anderen als GNU, befinden, hilft der folgende Code:

sed '1d' test.dat > tmp.dat 
67
Nasri Najib

Nein, das ist ungefähr so ​​effizient, wie Sie es bekommen werden. Sie könnten ein C-Programm schreiben, das den Job etwas schneller erledigen könnte (weniger Anlaufzeit und Verarbeitungsargumente). Wahrscheinlich tendiert es jedoch zur gleichen Geschwindigkeit wie sed, da die Dateien groß werden (und ich gehe davon aus, dass sie groß sind, wenn es eine Minute dauert) ).

Aber Ihre Frage hat das gleiche Problem wie viele andere, da sie die Lösung voraussetzt. Wenn Sie uns im Detail sagen würden - was Sie eher als wie versuchen, können wir 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 das Programm B so zu ändern, dass es anders verarbeitet wird.

Angenommen, alle Ihre Programme hängen an dieser 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 nicht versucht wird, die erste Zeile zu löschen, sondern ein dauerhafter (wahrscheinlich dateibasierter) Versatz in der Datei A beibehalten wird, so dass er beim nächsten Ausführen nach diesem Versatzprozess suchen kann die Zeile dort und aktualisieren Sie den Offset.

In einer ruhigen Zeit (Mitternacht?) Kann dann eine spezielle Verarbeitung der Datei A durchgeführt werden, um alle gerade bearbeiteten Zeilen zu löschen und den Versatz auf 0 zurückzusetzen.

Es ist sicherlich schneller für ein Programm, eine Datei zu öffnen und zu suchen, anstatt 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 gibt möglicherweise andere Lösungen, wenn Sie weitere Informationen angeben.

16
paxdiablo

Sie können bearbeiten die Dateien an Ort und Stelle: Verwenden Sie einfach Perls -i -Flag, wie folgt:

Perl -ni -e 'print unless $. == 1' filename.txt

Dadurch verschwindet die erste Zeile, wenn Sie danach fragen. Perl muss die gesamte Datei lesen und kopieren, sorgt jedoch dafür, dass die Ausgabe unter dem Namen der Originaldatei gespeichert wird.

10
alexis

Wie Pax sagte, werden Sie wahrscheinlich nicht schneller sein. Der Grund dafür ist, dass es so gut wie keine Dateisysteme gibt, die das Abschneiden vom Dateianfang unterstützen. Dies wird also eine O-Operation (n) sein, wobei n die Größe der Datei ist. Sie können jedoch viel schneller die erste Zeile mit der gleichen Anzahl von Bytes überschreiben (möglicherweise mit Leerzeichen oder einem Kommentar), was für Sie möglicherweise von dem abhängt, was Sie tun möchten (was ist das?) das übrigens?).

9
Robert Gamble

Die sponge util vermeidet das Jonglieren einer temporären Datei:

tail -n +2 "$FILE" | sponge "$FILE"
6
agc

Wenn Sie die Datei an Ort und Stelle ändern möchten, können Sie immer die ursprüngliche ed anstelle ihres s treaming-Nachfolgers sed verwenden:

ed "$FILE" <<<$'1d\nwq\n'
4
Mark Reed

Wie wäre es mit csplit?

man csplit
csplit -k file 1 '{1}'
3
crydo

Könnte vim dazu verwenden:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

Dies sollte schneller sein, da vim beim Prozess nicht die gesamte Datei liest.

3
Hongbo Liu

sollte die Zeilen mit Ausnahme der ersten Zeile anzeigen:

cat textfile.txt | tail -n +2
2
serup

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
0
Tim

Da es sich so anhört, als könnte ich den Löschvorgang nicht beschleunigen, denke ich, ein guter Ansatz wäre, die Datei in Batches wie folgt 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 abgetötet wird (oder wenn dort ein fehlerhafter SQL-Code vorhanden ist - wodurch der Prozessteil stirbt oder blockiert wird), es Zeilen gibt, die entweder übersprungen oder zweimal verarbeitet werden .

(Datei1 enthält Zeilen mit SQL-Code)

0
Brent

Sie können dies ganz einfach mit:

cat filename | sed 1d > filename_without_first_line

in der Kommandozeile; Um die erste Zeile einer Datei dauerhaft zu entfernen, verwenden Sie den Direktmodus von sed mit dem Flag -i:

sed -i 1d <filename>
0
Ingo Baab