Come leggere la prima e l'ultima riga dall'output di gatto?


64

Ho un file di testo. Attività: ottenere la prima e l'ultima riga dal file dopo

$ cat file | grep -E "1|2|3|4" | commandtoprint

$ cat file
1
2
3
4
5

Hai bisogno di questo senza uscita cat (solo 1 e 5).

~$ cat file | tee >(head -n 1) >(wc -l)
1
2
3
4
5
5
1

Forse esiste una soluzione awk e più breve ...


Nel tuo secondo esempio, wc -lnon ha nulla a che fare con l'output dell'ultima riga di un file.
David Richerby,

Risposte:


115

soluzione sed :

sed -e 1b -e '$!d' file

Quando leggi da stdinif sarebbe simile a questo (ad esempio ps -ef):

ps -ef | sed -e 1b -e '$!d'
UID        PID  PPID  C STIME TTY          TIME CMD
root      1931  1837  0 20:05 pts/0    00:00:00 sed -e 1b -e $!d

soluzione testa e coda :

(head -n1 && tail -n1) <file

Quando i dati provengono da un comando ( ps -ef):

ps -ef 2>&1 | (head -n1 && tail -n1)
UID        PID  PPID  C STIME TTY          TIME CMD
root      2068  1837  0 20:13 pts/0    00:00:00 -bash

awk Soluzione:

awk 'NR==1; END{print}' file

E anche l'esempio con pipe con ps -ef:

ps -ef | awk 'NR==1; END{print}'
UID        PID  PPID  C STIME TTY          TIME CMD
root      1935  1837  0 20:07 pts/0    00:00:00 awk NR==1; END{print}

1
Grazie! È la risposta migliore perché non posso farlo, (head -n1 file;tail -n1 file)ho un comando e una pipe molto grandi come ultimo simbolo. Così | sed '1p;$!d'più breve.
dmgl,

Scusate il mio inglese, se non mi capite - è un mio problema - parlamene e preparo una presentazione migliore per voi.
dmgl,

7
Funziona head && taildavvero per te? solo seq 10 | (head -n1 && tail -n1)stampe 1.
Glenn Jackman,

1
@DavidConrad, per approfondire ciò che diceva il caos, -e 1b- sulla prima riga, diramare fino alla fine della sceneggiatura sed, a quel punto accade la "stampa" implicita. Se l'ingresso è lungo solo una riga, sed termina. Altrimenti, per tutte le righe tranne l'ultima, eliminare. Nell'ultima riga, la "stampa" implicita si verifica nuovamente.
Glenn Jackman,

1
@mikeserv: ;-) controllalo quando ne avrai la possibilità. Su una singola linea impedisce efficacemente di raddoppiarlo sull'output, ma su un caso di lavoro su più righe, conserva solo l'ultima riga ... almeno su GNU sed4.2.2. Saluti.
Cbhihe,

23

sed -n '1p;$p' file.txt stamperà la prima e ultima riga di file.txt.


10
Se l'input ha solo una riga, verrà stampato due volte. Puoi preferire sed -e 1b -e '$!d'se non lo desideri.
Stéphane Chazelas,

10

Un modo divertente e puro Bash≥4:

cb() { (($1-1>0)) && unset "ary[$1-1]"; }
mapfile -t -C cb -c 1 ary < file

Dopodiché, avrai una matrice arycon il primo campo (ovvero, con indice 0) che è la prima riga di filee il suo ultimo campo è l'ultima riga di file. Il callback cb(facoltativo se si desidera slurp tutte le linee nell'array) disinserisce tutte le linee intermedie in modo da non ingombrare la memoria. Come sottoprodotto gratuito, avrai anche il numero di righe nel file (come l'ultimo indice dell'array + 1).

demo:

$ mapfile -t -C cb -c 1 ary < <(printf '%s\n' {a..z})
$ declare -p ary
declare -a ary='([0]="a" [25]="z")'
$ # With only one line
$ mapfile -t -C cb -c 1 ary < <(printf '%s\n' "only one line")
$ declare -p ary
declare -a ary='([0]="only one line")'
$ # With an empty file
$ mapfile -t -C cb -c 1 ary < <(:)
declare -a ary='()'

7

Con sedsi potrebbe delete righe se non la 1st quella AND NOT il la $t uno.
Utilizzare !per NOT(annullare) una condizione e il X{Y..}costrutto per combinare condizioni X AND Y :

cmd | sed '1!{$!d;}'

oppure potresti usare un intervallo - da 2nd a $t - ed eliminare tutte le linee in quell'intervallo tranne la $linea t:

cmd | sed '2,${$!d;}'

6

Utilizzando Perl:

$ seq 10 |  perl -ne 'print if 1..1 or eof'
1
10

Quanto sopra stampa il primo elemento nell'output di seq 10tramite il if 1..1, mentre or eofverrà stampato anche l'ultimo elemento.


3
Potresti spiegare come funziona? Si prega di evitare di dare risposte di questo tipo senza una spiegazione di cosa fanno e come.
terdon

1
cosa fanno /etc abrte yum.repos.ddevono fare questa domanda?
drs

@drs Ho modificato la risposta per evitare di usare 'ls' come input di esempio.
dolmen,

1
@dolmen: potrebbe essere una buona idea terminare la modifica eliminando tutti i riferimenti a / etc. Immagino che questo sia stato aggiunto dopo la prima modifica, ma, comunque, la risposta non ha alcun senso. Solo un suggerimento.
Cbhihe,

4

Senza gatto:

$ cat file |tee >(head -n1) >(tail -n1) >/dev/null
1
5

o

$ (head -n1 file;tail -n1 file)
1
5

8
Nel primo, l'ordine delle linee non è garantito.
Stéphane Chazelas,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.