Angenommen, Sie haben eine txt-Datei. Wie lautet der Befehl, um die oberen 10 Zeilen und die unteren 10 Zeilen der Datei gleichzeitig anzuzeigen?
Wenn die Datei 200 Zeilen lang ist, können Sie die Zeilen 1-10 und 190-200 auf einmal anzeigen.
Angenommen, Sie haben eine txt-Datei. Wie lautet der Befehl, um die oberen 10 Zeilen und die unteren 10 Zeilen der Datei gleichzeitig anzuzeigen?
Wenn die Datei 200 Zeilen lang ist, können Sie die Zeilen 1-10 und 190-200 auf einmal anzeigen.
Antworten:
Sie können einfach:
(head; tail) < file.txt
Und wenn Sie aus irgendeinem Grund Rohre verwenden müssen, dann wie folgt:
cat file.txt | (head; tail)
Hinweis: Gibt doppelte Zeilen aus, wenn die Anzahl der Zeilen in file.txt kleiner ist als die Standardzeilen des Kopfes + die Standardzeilen des Endes.
head
die ersten 10 Zeilen der Datei verbraucht wurden. (Vergleichen Sie dies mit head < file.txt; tail < file.txt
einer Datei mit weniger als 20 Zeilen). Nur ein sehr kleiner Punkt zu beachten. (Aber immer noch +1.)
head
nur die ersten 10 Zeilen der Eingabe angezeigt werden, kann nicht garantiert werden, dass nicht mehr davon verbraucht wurden, um das Ende der 10. Zeile zu finden, sodass weniger Eingabe für less
die Anzeige übrig bleibt .
seq 100 | (head; tail)
gibt mir nur die ersten 10 Zahlen. Nur bei viel größeren Eingaben (wie seq 2000
) erhält der Schwanz eine Eingabe.
Für einen reinen Stream (z. B. Ausgabe eines Befehls) können Sie 'tee' verwenden, um den Stream zu teilen und einen Stream an head und einen an tail zu senden. Dies erfordert die Verwendung der Funktion '> (Liste)' von bash (+ / dev / fd / N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
oder mit / dev / fd / N (oder / dev / stderr) plus Subshells mit komplizierter Umleitung:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1
(Beides funktioniert nicht in csh oder tcsh.)
Für etwas mit etwas besserer Kontrolle können Sie diesen Perl-Befehl verwenden:
COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
COMMAND | { tee >(head >&2) | tail; } |& other_commands
cat >/dev/null
behebt es:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
head
und tail
Befehle: \ ...
head -10 file.txt; tail -10 file.txt
Ansonsten müssen Sie Ihr eigenes Programm / Skript schreiben.
cat
und head
oder tail
gepfeift, gut zu wissen, dass ich sie einzeln verwenden kann!
{ head file; tail file; } | prog
(Abstand innerhalb der Klammern und das
Basierend auf dem Kommentar von JF Sebastian :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
Auf diese Weise können Sie die erste Zeile und den Rest in einer Pipe unterschiedlich verarbeiten. Dies ist nützlich für die Arbeit mit CSV-Daten:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 2 4 6
Das Problem hierbei ist, dass Stream-orientierte Programme die Länge der Datei nicht im Voraus kennen (da es möglicherweise keine gibt, wenn es sich um einen echten Stream handelt).
Tools wie tail
Puffern Sie die letzten n Zeilen und warten Sie auf das Ende des Streams. Drucken Sie dann.
Wenn Sie dies in einem einzigen Befehl tun möchten (und es mit einem beliebigen Versatz arbeiten lassen und keine Zeilen wiederholen, wenn sie sich überschneiden), müssen Sie dieses erwähnte Verhalten emulieren.
versuche das awk:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
a.out | awk -v ...
Es hat viel Zeit gekostet, diese Lösung zu finden, die (bisher) die einzige zu sein scheint, die alle Anwendungsfälle abdeckt:
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
'{
if (NR <= offset) print;
else {
a[NR] = $0;
delete a[NR-offset];
printf "." > "/dev/stderr"
}
}
END {
print "" > "/dev/stderr";
for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
{ print a[i]}
}'
Funktionsliste:
Ich habe eine Weile nach dieser Lösung gesucht. Ich habe es selbst mit sed versucht, aber das Problem, die Länge der Datei / des Streams vorher nicht zu kennen, war unüberwindbar. Von allen oben verfügbaren Optionen gefällt mir die awk-Lösung von Camille Goudeseune. Er machte sich eine Notiz, dass seine Lösung zusätzliche Leerzeilen in der Ausgabe mit einem ausreichend kleinen Datensatz hinterließ. Hier stelle ich eine Modifikation seiner Lösung zur Verfügung, die die zusätzlichen Zeilen entfernt.
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
Nun, Sie können sie immer miteinander verketten. Wie so ,
head fiename_foo && tail filename_foo
. Wenn dies nicht ausreicht, können Sie sich eine Bash-Funktion in Ihre .profile-Datei oder eine von Ihnen verwendete Anmeldedatei schreiben:
head_and_tail() {
head $1 && tail $1
}
Rufen Sie es später über Ihre Shell-Eingabeaufforderung auf : head_and_tail filename_foo
.
Erste 10 Zeilen von file.ext, dann die letzten 10 Zeilen:
cat file.ext | head -10 && cat file.ext | tail -10
Letzte 10 Zeilen der Datei, dann die ersten 10:
cat file.ext | tail -10 && cat file.ext | head -10
Sie können die Ausgabe dann auch an eine andere Stelle weiterleiten:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
tail
und / head
oder eine Funktion übergibt .
Ich habe dazu eine einfache Python-App geschrieben: https://gist.github.com/garyvdm/9970522
Es behandelt sowohl Pipes (Streams) als auch Dateien.
Fügen Sie Folgendes zu Ihrer .bashrc- oder .profile-Datei hinzu, um Pipes (Streams) sowie Dateien zu verarbeiten:
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }
Dann kannst du nicht nur
headtail 10 < file.txt
aber auch
a.out | headtail 10
(Dies fügt immer noch unechte Leerzeilen hinzu, wenn 10 die Länge der Eingabe überschreitet, im Gegensatz zu einfach alt a.out | (head; tail)
. Vielen Dank, frühere Antwortende.)
Hinweis: headtail 10
nicht headtail -10
.
Aufbauend auf was @Samus_ erklärt hier , wie @Aleksandra Zalcman der Befehl funktioniert, ist diese Variante praktisch , wenn Sie nicht schnell erkennen können , wo der Schwanz ohne Zähllinien beginnt.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
Wenn Sie mit etwas anderem als 20 Zeilen arbeiten, kann eine Zeilenanzahl sogar hilfreich sein.
{ head -n 18; tail -n 14; } < file.txt | cat -n
Gehen Sie folgendermaßen vor, um die ersten 10 und letzten 10 Zeilen einer Datei zu drucken:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
HINWEIS : Die Variable aFile enthält den vollständigen Pfad der Datei .
Ich würde sagen, dass es abhängig von der Größe der Datei möglicherweise nicht wünschenswert ist, den Inhalt aktiv einzulesen. Unter diesen Umständen denke ich, dass ein einfaches Shell-Scripting ausreichen sollte.
So habe ich dies kürzlich für eine Reihe sehr großer CSV-Dateien gehandhabt, die ich analysiert habe:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
Dies druckt die ersten 10 Zeilen und die letzten 10 Zeilen jeder Datei aus, während gleichzeitig der Dateiname und einige Auslassungspunkte vorher und nachher ausgedruckt werden.
Für eine einzelne große Datei können Sie einfach Folgendes ausführen, um den gleichen Effekt zu erzielen:
$ head somefile.csv && echo ... && tail somefile.csv
Verbraucht Standard, aber einfach und funktioniert für 99% der Anwendungsfälle
#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT
$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100