it-swarm.com.de

unix - Kopf UND Ende der Datei

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. 

109
toop

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.

176

ed ist der standard text editor

$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
17
kev

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;'
10
RantingNerd

head -10 file.txt; tail -10 file.txt

Abgesehen davon müssen Sie Ihr eigenes Programm/Skript schreiben.

3
mah
(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.

2
guest

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:

  • live-Ausgabe für Kopf (offensichtlich ist das für Schwanz nicht möglich)
  • keine Verwendung externer Dateien
  • fortschrittsbalken einen Punkt für jede Zeile nach MAX_LINES, sehr nützlich für lange laufende Aufgaben.
  • fortschrittsbalken auf stderr, um sicherzustellen, dass die Fortschrittspunkte vom Kopf + Ende getrennt werden (sehr praktisch, wenn Sie stdout per Pipe leiten möchten)
  • vermeidet mögliche falsche Protokollierungsreihenfolge durch Pufferung (stdbuf)
  • vermeiden Sie das Duplizieren der Ausgabe, wenn die Gesamtanzahl der Zeilen kleiner ist als head + tail.
2
sorin

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
2
Samus_

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

1
Paul

Ich habe dazu eine einfache Python-App geschrieben: https://Gist.github.com/garyvdm/9970522

Es verarbeitet Pipes (Streams) sowie Dateien.

1

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.

1
S.R.I

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 
1
modular

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.

0

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
0
Jitsusama
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 .

0
mark_infinite

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
0
Script Wolf

So drucken Sie die ersten 10 und die letzten 10 Zeilen einer Datei:

cat <(head -n10 file.txt) <(tail -n10 file.txt) | less

0
mariana.ft

ideen aufbauen (getestet bash & zsh) 

aber mit einem Alias ​​'Hut' Head and Tails

alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '


hat large.sql
0
zzapper

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] }' ; }
0
Michael Blahay

Warum nicht sed für diese Aufgabe verwenden?

sed -n -e 1,+9p -e 190,+9p textfile.txt

0
lik