Angenommen, Sie haben eine TXT-Datei. Wie lautet der Befehl, um die oberen 10 Zeilen und die unteren 10 Zeilen der Datei gleichzeitig anzuzeigen?
d.h. wenn die Datei 200 Zeilen lang ist, betrachten Sie die Zeilen 1-10 und 190-200 auf einmal.
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: Es werden doppelte Zeilen gedruckt, wenn die Anzahl der Zeilen in file.txt kleiner als die Standardzeilen von Kopfzeile + Standardzeilen von Ende ist.
ed
ist der standard text editor
$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
Bei einem reinen Stream (z. B. Ausgabe eines Befehls) können Sie den Stream mit "tee" verzweigen und einen Stream an den Kopf und einen an das Ende 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 Weiterleitung:
( ( 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
(Keine davon funktioniert 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;'
head -10 file.txt; tail -10 file.txt
Abgesehen davon müssen Sie Ihr eigenes Programm/Skript schreiben.
(sed -u 10q; echo ...; tail) < file.txt
Nur eine weitere Variante des (head;tail)
-Themes, jedoch das anfängliche Pufferfüllproblem für kleine Dateien vermeiden.
Es dauerte eine Weile, bis diese Lösung endete, die scheinbar die einzige war, die alle Anwendungsfälle abdeckte (bisher):
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]}
}'
Feature-Liste:
das Problem hierbei ist, dass streamorientierte Programme die Länge der Datei nicht im Voraus kennen (weil es möglicherweise keine gibt, wenn es sich um einen echten Stream handelt).
werkzeuge wie tail
puffern die letzten n Zeilen und warten auf das Ende des Streams. Drucken Sie dann.
wenn Sie dies in einem einzigen Befehl ausführen möchten (und es mit jedem Versatz arbeiten lassen soll und Zeilen nicht wiederholen, wenn sich diese überlappen), müssen Sie dieses Verhalten nachahmen, das ich erwähnt habe.
versuchen Sie diese Ahnung:
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
Zuerst 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 anderer Stelle weiterleiten:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
Ich habe dazu eine einfache Python-App geschrieben: https://Gist.github.com/garyvdm/9970522
Es verarbeitet Pipes (Streams) sowie Dateien.
Nun, Sie können sie immer miteinander verketten. 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
}
Und rufen Sie es später von Ihrer Shell-Eingabeaufforderung aus auf: head_and_tail filename_foo
.
Basierend auf dem Kommentar von J. F. 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, was für die Arbeit mit CSV-Daten hilfreich ist:
{ 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
Um Pipes (Streams) sowie Dateien zu verarbeiten, fügen Sie dies der Datei .bashrc oder .profile hinzu:
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 falsche Leerzeilen hinzu, wenn 10 die Länge der Eingabe überschreitet, im Gegensatz zu normalem a.out | (head; tail)
. Vielen Dank, die vorherigen Antworter.)
Hinweis: headtail 10
, nicht headtail -10
.
Ich würde sagen, dass je nach Größe der Datei das aktive Einlesen des Inhalts möglicherweise nicht wünschenswert ist. In diesem Fall sollte ein einfaches Shell-Scripting ausreichen.
So habe ich kürzlich eine Reihe sehr großer CSV-Dateien behandelt, die ich analysierte:
$ 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 und gleichzeitig den Dateinamen und einige Ellipsis vor und nach dem Ausdruck.
Für eine einzelne große Datei können Sie einfach Folgendes ausführen, um denselben Effekt zu erzielen:
$ head somefile.csv && echo ... && tail somefile.csv
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
NOTE: Die aFile -Variable enthält den vollständigen Pfad der Datei .
Aufbauend auf dem, was @Samus_ erklärt hat hier darüber, wie der Befehl von @Aleksandra Zalcman funktioniert, ist diese Variante praktisch, wenn Sie nicht schnell erkennen können, wo der Schwanz beginnt, ohne Zeilen zu zählen.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
Wenn Sie anfangen, mit anderen als 20 Zeilen zu arbeiten, kann eine Zeilenanzahl sogar hilfreich sein.
{ head -n 18; tail -n 14; } < file.txt | cat -n
So drucken Sie die ersten 10 und die letzten 10 Zeilen einer Datei:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
ideen aufbauen (getestet bash & zsh)
aber mit einem Alias 'Hut' Head and Tails
alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '
hat large.sql
Ich habe schon lange nach dieser Lösung gesucht. Ich habe es mit sed probiert, aber das Problem, die Länge der Datei/des Streams nicht zu kennen, war unüberwindbar. Von allen oben verfügbaren Optionen mag ich die awk-Lösung von Camille Goudeseune. Er notierte sich, dass seine Lösung bei einem ausreichend kleinen Datensatz zusätzliche leere Zeilen in der Ausgabe hinterließ. Hier füge ich eine Modifikation seiner Lösung hinzu, 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] }' ; }
Warum nicht sed
für diese Aufgabe verwenden?
sed -n -e 1,+9p -e 190,+9p textfile.txt