Supponi di avere un file txt, qual è il comando per visualizzare contemporaneamente le prime 10 righe e le ultime 10 righe del file?
vale a dire se il file è lungo 200 righe, quindi visualizzare le righe 1-10 e 190-200 in una volta sola.
Supponi di avere un file txt, qual è il comando per visualizzare contemporaneamente le prime 10 righe e le ultime 10 righe del file?
vale a dire se il file è lungo 200 righe, quindi visualizzare le righe 1-10 e 190-200 in una volta sola.
Risposte:
Puoi semplicemente:
(head; tail) < file.txt
E se hai bisogno di usare le pipe per qualche motivo, allora in questo modo:
cat file.txt | (head; tail)
Nota: stamperà le linee duplicate se il numero di linee in file.txt è inferiore alle linee di testa predefinite + linee di coda predefinite.
head
ha consumato le prime 10 righe del file. (Confronta questo con head < file.txt; tail < file.txt
un file con meno di 20 righe). Solo un punto molto piccolo da tenere a mente. (Ma ancora +1.)
head
solo le prime 10 righe del suo input, non è garantito che non ne abbia consumato più per trovare la fine della decima riga, lasciando meno input per la visualizzazione. less
seq 100 | (head; tail)
mi dà solo i primi 10 numeri. Solo su dimensioni di input molto più grandi (come seq 2000
) la coda ottiene un input.
Per un flusso puro (ad es. Output da un comando), puoi usare 'tee' per fork il flusso e inviare un flusso alla testa e uno alla coda. Ciò richiede l'utilizzo della funzione '> (elenco)' di bash (+ / dev / fd / N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
o usando / dev / fd / N (o / dev / stderr) più subshells con reindirizzamento complicato:
( ( 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
(Nessuno di questi funzionerà in csh o tcsh.)
Per qualcosa con un controllo un po 'migliore, puoi usare questo comando perl:
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
lo risolve:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
head
e tail
comandi: \ ...
head -10 file.txt; tail -10 file.txt
A parte questo, dovrai scrivere il tuo programma / script.
cat
e head
o tail
convogliato, bello sapere che posso usare singolarmente!
{ head file; tail file; } | prog
(sono necessari lo spazio all'interno delle parentesi graffe e il punto e virgola finale)
Basato sul commento di JF Sebastian :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
In questo modo è possibile elaborare la prima riga e il resto in modo diverso in un'unica pipe, utile per lavorare con i dati CSV:
{ 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
il problema qui è che i programmi orientati al flusso non conoscono in anticipo la lunghezza del file (perché potrebbe non essercene uno, se è un flusso reale).
strumenti come tail
bufferizzare le ultime n righe visualizzate e attendere la fine del flusso, quindi stampare.
se vuoi farlo in un solo comando (e farlo funzionare con qualsiasi offset, e non ripetere le righe se si sovrappongono) dovrai emulare questo comportamento che ho citato.
prova questo 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 ...
Ci è voluto molto tempo per finire con questa soluzione che, a quanto pare, è l'unica che ha coperto tutti i casi d'uso (finora):
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]}
}'
Elenco delle caratteristiche:
Ho cercato questa soluzione per un po '. Ho provato io stesso con sed, ma il problema di non conoscere in anticipo la lunghezza del file / stream era insormontabile. Di tutte le opzioni disponibili sopra, mi piace la soluzione awk di Camille Goudeseune. Ha fatto notare che la sua soluzione ha lasciato righe vuote in più nell'output con un set di dati sufficientemente piccolo. Qui fornisco una modifica della sua soluzione che rimuove le righe extra.
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] }' ; }
Bene, puoi sempre metterli insieme. In questo modo,
head fiename_foo && tail filename_foo
. Se ciò non è sufficiente, potresti scrivere una funzione bash nel tuo file .profile o in qualsiasi file di accesso che usi:
head_and_tail() {
head $1 && tail $1
}
E, poi richiamarlo dalla shell di richiesta: head_and_tail filename_foo
.
Prime 10 righe di file.ext, quindi le sue ultime 10 righe:
cat file.ext | head -10 && cat file.ext | tail -10
Ultime 10 righe del file, quindi le prime 10:
cat file.ext | tail -10 && cat file.ext | head -10
È quindi possibile reindirizzare l'output anche altrove:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
tail
e head
o una funzione alias-ing esso.
Ho scritto una semplice app Python per fare questo: https://gist.github.com/garyvdm/9970522
Gestisce pipe (stream) e file.
Per gestire pipe (flussi) e file, aggiungilo al tuo file .bashrc o .profile:
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] }' ; }
Quindi non puoi solo
headtail 10 < file.txt
ma anche
a.out | headtail 10
(Questo aggiunge ancora righe vuote spurie quando 10 supera la lunghezza dell'input, a differenza del vecchio normale a.out | (head; tail)
. Grazie, precedenti risponditori.)
Nota: headtail 10
no headtail -10
.
Basandosi su ciò che @Samus_ ha spiegato qui su come funziona il comando di @Aleksandra Zalcman, questa variazione è utile quando non puoi individuare rapidamente dove inizia la coda senza contare le linee.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
O se inizi a lavorare con qualcosa di diverso da 20 righe, un conteggio delle righe potrebbe persino essere d'aiuto.
{ head -n 18; tail -n 14; } < file.txt | cat -n
Per stampare le prime 10 e ultime 10 righe di un file, puoi provare questo:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
NOTA : la variabile aFile contiene il percorso completo del file .
Direi che a seconda della dimensione del file, potrebbe non essere desiderabile leggere attivamente nel suo contenuto. In quella circostanza, penso che alcuni semplici script di shell dovrebbero essere sufficienti.
Ecco come di recente ho gestito questo per un numero di file CSV molto grandi che stavo analizzando:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
Questo stampa le prime 10 righe e le ultime 10 righe di ciascun file, stampando anche il nome del file e alcuni puntini di sospensione prima e dopo.
Per un singolo file di grandi dimensioni, è possibile semplicemente eseguire quanto segue per lo stesso effetto:
$ head somefile.csv && echo ... && tail somefile.csv
Consuma stdin, ma semplice e funziona per il 99% dei casi d'uso
#!/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