Entfernen Sie die letzte Zeile aus einer Datei in Bash

330

Ich habe eine Datei foo.txtmit den folgenden Zeilen:

a
b
c

Ich möchte einen einfachen Befehl, der zum Inhalt des foo.txtSeins führt:

a
b
Fragsworth
quelle

Antworten:

406

Verwenden von GNU sed:

sed -i '$ d' foo.txt

Die -iOption ist in GNU sedVersionen älter als 3.95 nicht vorhanden. Sie müssen sie daher als Filter für eine temporäre Datei verwenden:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

In diesem Fall könnten Sie natürlich auch head -n -1anstelle von verwenden sed.

Mac OS:

Unter Mac OS X (ab 10.7.4) entspricht dies dem sed -iobigen Befehl

sed -i '' -e '$ d' foo.txt
thkala
quelle
56
Unter Mac OS X (ab 10.7.4) entspricht dies dem sed -iobigen Befehl sed -i '' -e '$ d' foo.txt. Funktioniert head -n -1derzeit auch nicht auf einem Mac.
mklement0
26
Können Sie erklären, was '$ d' Regex bewirkt? Es geht um das Entfernen der letzten Zeile, daher denke ich, dass dies der wichtigste Teil für alle ist, die diese Frage sehen. Danke :)
Kravemir
38
@Miro: Auf keinen Fall ist $ deine Regex. Es ist ein sed Befehl. dist der Befehl zum Löschen einer Zeile, während $"die letzte Zeile in der Datei" bedeutet. Wenn Sie vor einem Befehl einen Ort angeben (in sed lingo als "Bereich" bezeichnet), wird dieser Befehl nur auf den angegebenen Ort angewendet. Dieser Befehl lautet also explizit "Löschen Sie im Bereich der letzten Zeile einer Datei diese". Ganz glatt und direkt auf den Punkt, wenn Sie mich fragen.
Daniel Kamil Kozar
1
Wie entfernen Sie die letzte Zeile, aber nur, wenn es sich um eine leere Zeile handelt?
Skan
1
@Alex: aktualisiert für GNU sed; Keine Ahnung von den verschiedenen BSD / UNIX / MacOS-Versionen ...
Thkala
279

Dies ist bei weitem die schnellste und einfachste Lösung, insbesondere bei großen Dateien:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

Wenn Sie die oberste Zeile löschen möchten, verwenden Sie Folgendes:

tail -n +2 foo.txt

Dies bedeutet, dass die Ausgangsleitungen ab Zeile 2 beginnen.

Nicht sedzum Löschen von Zeilen am oberen oder unteren Rand einer Datei verwenden - es ist sehr, sehr langsam, wenn die Datei groß ist.

John
quelle
16
head -n -1 foo.txtist genug
Apr МАЛИКОВ
20
head -n -1 funktioniert nicht auf dem Kopf von bdsutils. Zumindest nicht in der Version, die macos verwendet, also funktioniert das nicht.
johannes_lalala
23
@johannes_lalala Auf dem Mac können Sie brew install coreutils(GNU Core Utilities) und gheadstattdessen verwenden head.
Interessanterweise gibt es
Hier ist ein einfacher Bash - Skript , dass automates es in Fall , dass Sie mehrere Dateien mit unterschiedlicher Anzahl von Linien am unteren Rande haben zu löschen: Katze TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Führt sie zum Löschen 4 Zeilen zum Beispiel von Meinedat als: ./TAILfixer myfile 4natürlich zunächst durch ausführbar machenchmod +x TAILfixer
FatihSarigol
Daumen hoch, weil es funktioniert hat, aber bei allem Vergleich sedist dieser Befehl auch verdammt langsam
Jeff Diederiks
121

Ich hatte Probleme mit allen Antworten hier, weil ich mit einer RIESIGEN Datei (~ 300 GB) arbeitete und keine der Lösungen skaliert war. Hier ist meine Lösung:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

Mit Worten: Ermitteln Sie die Länge der Datei, mit der Sie enden möchten (Länge der Datei minus Länge der Länge der letzten Zeile, mit bc), und legen Sie diese Position als Ende der Datei fest (indem ddSie ein Byte auf /dev/nulleingeben es).

Dies ist schnell, da das tailLesen am Ende beginnt und dddie Datei an Ort und Stelle überschreibt, anstatt jede Zeile der Datei zu kopieren (und zu analysieren), wie dies bei den anderen Lösungen der Fall ist.

HINWEIS: Dadurch wird die Zeile aus der vorhandenen Datei entfernt! Erstellen Sie ein Backup oder testen Sie eine Dummy-Datei, bevor Sie sie in Ihrer eigenen Datei ausprobieren!

Yossi Farjoun
quelle
3
Ich wusste nicht einmal über dd. Dies sollte die beste Antwort sein. Ich danke dir sehr! Es ist eine Schande, dass die Lösung für (blockierte) komprimierte Dateien nicht funktioniert.
Tommy.carstensen
4
Sehr, sehr kluger Ansatz! Nur eine Anmerkung: Es erfordert und arbeitet mit einer realen Datei, sodass es nicht für Pipes, Befehlsersetzungen, Umleitungen und solche Ketten verwendet werden kann.
MestreLion
3
Dies sollte die beste Antwort sein. Arbeitete sofort für meine ~ 8 GB Datei.
Peter Cogan
8
Mac-Benutzer: dd if = / dev / null von = <Dateiname> bs = 1 seek = $ (echo $ (stat -f =% z <Dateiname> | cut -c 2-) - $ (tail -n1 <Dateiname> | wc -c) | bc)
Igor
2
Dieser Ansatz ist der richtige Weg für große Dateien!
Thomas.han
61

Sie können verwenden, um die letzte Zeile aus einer Datei zu entfernen, ohne die gesamte Datei zu lesen oder etwas neu zu schreiben

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Um die letzte Zeile zu entfernen und sie auch auf stdout zu drucken ("pop" it), können Sie diesen Befehl kombinieren mit tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

Diese Befehle können eine sehr große Datei effizient verarbeiten. Dies ähnelt der Antwort von Yossi und ist von dieser inspiriert, vermeidet jedoch die Verwendung einiger zusätzlicher Funktionen.

Wenn Sie diese wiederholt verwenden und Fehlerbehandlung und einige andere Funktionen wünschen, können Sie den poptailBefehl hier verwenden: https://github.com/donm/evenmoreutils

ohspite
quelle
3
Kurzer Hinweis: truncateIst unter OS X standardmäßig nicht verfügbar. Die Installation mit Brew ist jedoch einfach : brew install truncate.
Guillaume Boudreau
3
Sehr schön. Viel besser ohne dd. Ich hatte Angst davor, dd als einzelne falsch platzierte Ziffer oder Buchstaben zu verwenden, was eine Katastrophe
bedeuten
1
(WITZWARNUNG) Tatsächlich erfordert ("dd" -> "Katastrophe") 1 Ersetzung und 6 Einfügungen; kaum "eine einzelne falsch platzierte Ziffer oder ein falsch platzierter Buchstabe". :)
Kyle
29

Mac-Benutzer

Wenn Sie nur die letzte Zeile löschen möchten, ohne die Datei selbst zu ändern, tun Sie dies

sed -e '$ d' foo.txt

Wenn Sie die letzte Zeile der Eingabedatei selbst löschen möchten, tun Sie dies

sed -i '' -e '$ d' foo.txt

manpreet singh
quelle
Das funktioniert bei mir, aber ich verstehe nicht. Jedenfalls hat es funktioniert.
Melvin
Das Flag -i ''weist sed an, die Datei an Ort und Stelle zu ändern, während ein Backup in einer Datei mit der als Parameter angegebenen Erweiterung gespeichert wird. Da der Parameter eine leere Zeichenfolge ist, wird keine Sicherungsdatei erstellt. -eweist sed an, einen Befehl auszuführen. Der Befehl $ dbedeutet: Finde die letzte Zeile ( $) und lösche sie ( d).
Krzysztof Szafranek
24

Für Mac-Benutzer:

Auf dem Mac funktioniert head -n -1 nicht. Und ich habe versucht, eine einfache Lösung zu finden [ohne mir Gedanken über die Verarbeitungszeit zu machen], um dieses Problem nur mit den Befehlen "head" und / oder "tail" zu lösen.

Ich habe die folgende Befehlsfolge ausprobiert und war froh, dass ich sie nur mit dem Befehl "tail" [mit den auf dem Mac verfügbaren Optionen] lösen konnte. Wenn Sie also auf einem Mac arbeiten und nur "tail" verwenden möchten, um dieses Problem zu lösen, können Sie den folgenden Befehl verwenden:

cat file.txt | Schwanz -r | Schwanz -n +2 | Schwanz -r

Erklärung:

1> tail -r: kehrt einfach die Reihenfolge der Zeilen in seiner Eingabe um

2> tail -n +2: Hiermit werden alle Zeilen ab der zweiten Zeile in der Eingabe gedruckt

Sarfraaz Ahmed
quelle
2
Wenn Sie Coreutils mit Homebrew installieren, erhalten Sie den GNU-Kopf als "Ghead", was Sie tun könnenghead -n -1
etwas
13
echo -e '$d\nw\nq'| ed foo.txt
lhf
quelle
2
Verwenden Sie für eine Filterlösung, die die Datei nicht an Ort und Stelle bearbeitet sed '$d'.
lhf
8
awk 'NR>1{print buf}{buf = $0}'

Im Wesentlichen sagt dieser Code Folgendes:

Drucken Sie für jede Zeile nach der ersten die gepufferte Zeile

Setzen Sie für jede Zeile den Puffer zurück

Der Puffer ist um eine Zeile verzögert, sodass Sie am Ende die Zeilen 1 bis n-1 drucken

Foo Bah
quelle
2
Diese Lösung ist ein Filter und bearbeitet die Datei nicht an Ort und Stelle.
lhf
0
awk "NR != `wc -l < text.file`" text.file |> text.file

Dieses Snippet macht den Trick.

Michael Kingski
quelle
Dadurch wird nur die gesamte Datei für mich gelöscht.
Kartik Chugh
0

Beide Lösungen gibt es hier in anderen Formen. Ich fand diese etwas praktischer, klarer und nützlicher:

Verwenden von dd:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Verwenden von Truncate:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}
virtuelles Licht
quelle
Autsch. Viel zu kompliziert.
Robert
0

Linux

$ ist die letzte Zeile, d zum Löschen:

sed '$d' ~/path/to/your/file/name

Mac OS

Äquivalent des sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name
jasonleonhard
quelle
0

So können Sie es manuell machen (ich persönlich verwende diese Methode häufig, wenn ich die letzte Zeile in einer Datei schnell entfernen muss):

vim + [FILE]

Dieses +Zeichen bedeutet, dass beim Öffnen der Datei im vim-Texteditor der Cursor in der letzten Zeile der Datei positioniert wird.

Drücken dSie jetzt einfach zweimal auf Ihrer Tastatur. Dies macht genau das, was Sie wollen - entfernen Sie die letzte Zeile. Drücken Sie danach :gefolgt von xund dann Enter. Dadurch werden die Änderungen gespeichert und Sie kehren zur Shell zurück. Jetzt wurde die letzte Zeile erfolgreich entfernt.

Michael Rybkin
quelle
2
Der Schwerpunkt auf Einfachheit ging hier verloren
Peter M
-4

Rubin (1,9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
kurumi
quelle