Wie erhält man ein inverses Verhalten für "Schwanz" und "Kopf"?

55

Gibt es eine Möglichkeit zu head/ tailein Dokument und erhält den umgekehrten Ausgang; weil Sie nicht wissen, wie viele Zeilen ein Dokument enthält?

Das heißt, ich möchte nur alles außer den ersten zwei Zeilen foo.txtzum Anhängen an ein anderes Dokument erhalten.

chrisjlee
quelle

Antworten:

57

Hiermit können Sie die ersten beiden Zeilen entfernen:

tail -n +3 foo.txt

und dies, um die letzten beiden Zeilen zu entfernen:

head -n -2 foo.txt

(vorausgesetzt die Datei endet mit \nfür letztere)


Genau wie bei der Standardnutzung von tailund sind headdiese Vorgänge nicht destruktiv. Verwenden >out.txtSie diese Option, wenn Sie die Ausgabe in eine neue Datei umleiten möchten:

tail -n +3 foo.txt >out.txt

Falls out.txtbereits vorhanden, wird diese Datei überschrieben. Verwenden Sie >>out.txtanstelle von, >out.txtwenn die Ausgabe lieber angehängt werden soll out.txt.

Stéphane Gimenez
quelle
3
re „Kopf, wenn die Datei mit der Endung \n .. Es funktioniert für alle negativen ganzen Zahlen außer -n -0denen gibt gar nichts , so wie -n 0würde (mit: Kopf (GNU coreutils) 7.4) ... Wenn jedoch ein nachlauf \nvorhanden ist, -n -0macht Druck wie von der zu erwarten ist -, dh. er druckt die gesamte Datei ... So ist es für alle Nicht-Null negative Werte funktioniert .. aber -0schlägt fehl , wenn es keine Hinter ist\n
Peter.O
@fred: In der Tat seltsam ... (dasselbe gilt für 8.12 hier).
Stéphane Gimenez
Ist diese Operation destruktiv? Wie möchte ich, dass es die Umkehrung der ersten beiden Zeilen des Dokuments in eine andere kopiert?
Chrisjlee
@Chris: Nein, sie drucken das Ergebnis einfach auf ihre "Standardausgabe", die normalerweise mit dem Terminal verbunden ist. Ich habe einige Details hinzugefügt, wie man die Ausgabe zu einigen Dateien umleitet.
Stéphane Gimenez
7
head -n -2ist nicht POSIX-kompatibel .
10.
9

Wenn Sie alle außer den ersten N-1-Leitungen möchten, rufen Sie tailmit der Anzahl der Leitungen an +N. (Die Nummer ist die Nummer der ersten Zeile, die Sie beibehalten möchten, beginnend mit 1, dh +1 bedeutet, dass oben begonnen wird, +2 bedeutet, dass eine Zeile übersprungen wird usw.).

tail -n +3 foo.txt >>other-document

Es gibt keine einfache, tragbare Möglichkeit, die letzten N Zeilen zu überspringen. GNU headerlaubt head -n +Nals Gegenstück von tail -n +N. Andernfalls, wenn Sie tac(zB GNU oder Busybox) haben, können Sie es mit Tail kombinieren:

tac | tail -n +3 | tac

Portabel können Sie einen awk-Filter verwenden (ungetestet):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Wenn Sie die letzten Zeilen aus einer großen Datei entfernen möchten, können Sie den Byte-Versatz des zu schneidenden Teils bestimmen und anschließend die Kürzung mit ausführen dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

Sie können eine Datei am Anfang nicht abschneiden. Wenn Sie jedoch die ersten Zeilen einer großen Datei entfernen müssen, können Sie den Inhalt verschieben .

Gilles 'SO - hör auf böse zu sein'
quelle
Auf einigen Systemen (wie modernen Linux) können Sie eine Datei am Anfang abschneiden (komprimieren), in der Regel jedoch nur um ein Vielfaches der FS-Blockgröße (in diesem Fall also nicht wirklich nützlich).
Stéphane Chazelas
3

Aus der tailManpage (also GNU tail):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Daher sollten im Folgenden alle bis auf die ersten zwei Zeilen von somefile.txtan angehängt werden anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Steven Montag
quelle
3

Zum Entfernen der ersten n Zeilen kann GNU sed verwendet werden. Zum Beispiel, wenn n = 2 ist

sed -n '1,2!p' input-file

Der !Mittelwert "dieses Intervall ausschließen". Wie Sie sich vorstellen können, kann zum Beispiel ein komplizierteres Ergebnis erzielt werden

sed -n '3,5p;7p'

das wird Linie 3,4,5,7 zeigen. Die Verwendung regulärer Ausdrücke anstelle von Adressen erhöht die Leistung.

Die Einschränkung besteht darin, dass die Zeilennummern im Voraus bekannt sein müssen.

Enzotib
quelle
1
Warum nicht einfach sed 1,2d? Einfacher ist normalerweise besser. Auch ist nichts in Ihren Beispielen spezifisch für GNU Sed; Ihre Befehle verwenden alle die POSIX- Standardfunktionen von Sed .
Wildcard
1

Sie können diffdie Ausgabe von head/ tailmit der Originaldatei vergleichen und dann das Gleiche entfernen, um die Umkehrung zu erhalten.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
Sokoban
quelle
1

Während tail -n +4die Ausgabe der Datei ab der 4. Zeile (alle bis auf die ersten 3 Zeilen) Standard und portabel ist, ist dies nicht das headGegenstück ( head -n -3alle bis auf die letzten 3 Zeilen).

Portabel, würden Sie tun:

sed '$d' | sed '$d' | sed '$d'

Oder:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(Beachten Sie, dass auf einigen Systemen, auf denen sedein Musterbereich von begrenzter Größe vorhanden ist, dieser nicht auf große Werte von skaliert n).

Oder:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
Stéphane Chazelas
quelle
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Wenn <infilees sich um eine reguläre, lseek()-fähige Datei handelt, fühlen Sie sich auf jeden Fall frei. Das obige ist ein vollständig POSIX-unterstütztes Konstrukt.

mikeserv
quelle
0

Mein Ansatz ist ähnlich wie bei Gilles, aber ich kehre die Datei mit cat und pipe einfach mit dem Befehl head um.

tac -r thefile.txt | head thisfile.txt (ersetzt Dateien)

Abe
quelle
0

Hoffe, ich habe Ihr Bedürfnis klar verstanden.

Sie haben verschiedene Möglichkeiten, Ihre Anfrage zu bearbeiten:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Wo / etc / passwd ist Ihre Datei

Die 2. Lösung kann nützlich sein, wenn Sie eine große Datei haben:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Mathieu Coavoux
quelle
0

Lösung für BSD (macOS):

Entfernen Sie die ersten 2 Zeilen:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Letzte 2 Zeilen entfernen:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... nicht sehr elegant, aber erledigt den Job!

Spektrum
quelle