it-swarm.com.de

Kopf, um alle außer der letzten Zeile einer Datei anzuzeigen: Befehlsersetzung und Standard-E/A-Umleitung

Ich habe versucht, mit dem Dienstprogramm head alle außer der letzten Zeile der Standardeingabe anzuzeigen. Der eigentliche Code, den ich brauchte, entspricht cat myfile.txt | head -n $(($(wc -l)-1)). Das hat aber nicht geklappt. Ich mache dies auf Darwin/OS X, das nicht die Nice-Semantik von head -n -1 hat, die mir eine ähnliche Ausgabe gebracht hätte.

Keine dieser Variationen funktioniert auch.

cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')

Ich habe mehr Variationen ausprobiert und fand das besonders gut:

cat <<EOF | echo $(($(wc -l)-1))
>Hola
>Raul
>Como Esta
>Bueno?
>EOF
3

Hier ist etwas Einfacheres, das auch funktioniert.

echo "hello world" | echo $(($(wc -w)+10))

Diese gibt mir verständlicherweise einen ungültigen Zeilenzählfehler. Aber es sagt mir wenigstens aus, dass das Programm head die Standardeingabe nicht verbraucht, bevor es Sachen an die Subshell/Befehlssubstitution weitergibt, eine entfernte Möglichkeit, die ich aber trotzdem ausschließen wollte.

echo "hello" | head -n $(cat && echo 1)

Was erklärt hier das Verhalten von head und wc und ihre Interaktion durch Subshells? Danke für Ihre Hilfe.

32
gkb0986

head ist das falsche Werkzeug. Wenn Sie alle bis auf die letzte Zeile sehen möchten, verwenden Sie:

sed \$d

Der Grund dass 

# Sample of incorrect code:
echo "hello" | head -n $(wc -l | sed -E -e 's/\s//g')

fehler ist, dass wc die gesamte Eingabe verbraucht und head nichts mehr zu sehen ist. wc erbt seinen Standardwert von der Subshell, in der sie ausgeführt wird, und liest die Ausgabe der echo. Sobald es die Eingabe verbraucht hat, kehrt es zurück und versucht head, die Daten zu lesen ... aber es ist alles weg. Wenn Sie die Eingabe zweimal lesen möchten, müssen die Daten irgendwo gespeichert werden.

39
William Pursell

head -n -1 gibt Ihnen alle außer der letzten Zeile der Eingabe.

55
dunc123

Mit sed:

sed '$d' filename

löscht die letzte Zeile der Datei.

$ seq 1 10 | sed '$d' 
1
2
3
4
5
6
7
8
9
10
devnull

Speziell für Mac OS X habe ich eine Antwort aus einem Kommentar auf diese Fragen & Antworten gefunden .

Wenn Sie davon ausgehen, dass Sie Homebrew verwenden, führen Sie brew install coreutils aus und verwenden Sie den Befehl ghead:

cat myfile.txt | ghead -n -1

Oder gleichwertig:

ghead -n -1 myfile.txt

Unter brew info coreutils erfahren Sie, ob Sie die Befehle ohne das Präfix g verwenden möchten (z. B. head anstelle von ghead).

6
Jimothy
cat myfile.txt | echo $(($(wc -l)-1))

Das funktioniert. Es ist zu kompliziert: Sie könnten einfach echo $(($(wc -l)-1)) <myfile.txt oder echo $(($(wc -l <myfile.txt)-1)) schreiben. Das Problem ist die Art, wie Sie es verwenden.

cat myfile.txt | head -n $(wc -l | sed -E -e 's/\s//g')

wc verbraucht die gesamte Eingabe, während die Zeilen gezählt werden. Es sind also keine Daten mehr in der Pipe zu lesen, wenn head gestartet wird.

Wenn Ihre Eingabe aus einer Datei stammt, können Sie sowohl wc als auch head aus dieser Datei umleiten.

head -n $(($(wc -l <myfile.txt) - 1)) <myfile.txt

Wenn Ihre Daten möglicherweise von einer Pipe stammen, müssen Sie sie duplizieren. Das übliche Werkzeug zum Duplizieren eines Streams ist tee, aber das reicht hier nicht aus, da die beiden Ausgaben von tee mit der gleichen Rate erzeugt werden, wohingegen wc ihre Ausgabe vollständig verbrauchen muss, bevor head starten kann. Stattdessen müssen Sie ein einziges Tool verwenden, das die letzte Zeile erkennen kann, was ohnehin ein effizienterer Ansatz ist.

Praktischerweise bietet sed eine Möglichkeit, die letzte Zeile anzupassen. Entweder das Drucken aller Zeilen bis auf die letzte oder das Unterdrücken der letzten Ausgabezeile funktioniert:

sed -n '$! p'
sed '$ d'
4
Gilles
awk 'NR>1{print p}{p=$0}'

Für diesen Job ist ein awk one-liner etwas länger als ein sed.

0
Kent