Come ottenere un comportamento inverso per `coda` e` testa`?


55

C'è un modo per head/ tailun documento e ottenere l'output inverso; perché non sai quante righe ci sono in un documento?

Vale a dire, voglio solo ottenere tutto tranne le prime 2 righe foo.txtda aggiungere a un altro documento.

Risposte:


57

Puoi usarlo per eliminare le prime due righe:

tail -n +3 foo.txt

e questo per eliminare le ultime due righe:

head -n -2 foo.txt

(supponendo che il file finisca con \nquest'ultimo)


Proprio come per l'uso standard di taile headqueste operazioni non sono distruttive. Utilizzare >out.txtse si desidera reindirizzare l'output su un nuovo file:

tail -n +3 foo.txt >out.txt

Nel caso out.txtesista già, sovrascriverà questo file. Utilizzare >>out.txtinvece di >out.txtse si preferisce aggiungere l'output out.txt.


3
re "head, quando il file termina con \n" .. Funziona con tutti gli interi negativi a parte i -n -0quali non restituisce nulla , proprio come -n 0farebbe (usando: head (GNU coreutils) 7.4) ... Comunque quando \nè presente un trailing , -n -0stampa come ci si potrebbe aspettare dal -. stampa l'intero file ... Quindi funziona per tutti i valori negativi diversi da zero .. ma -0fallisce quando non c'è nessun trailing\n
Peter.O

@fred: Strano davvero ... (lo stesso con l'8.12 qui).
Stéphane Gimenez,

Questa operazione è distruttiva? Come voglio copiare l'inverso delle prime due righe del documento su un'altra?
chrisjlee,

@ Chris: No, stampano semplicemente il risultato sul loro "output standard", che di solito è collegato al terminale. Ho aggiunto alcuni dettagli su come reindirizzare l'output su alcuni file.
Stéphane Gimenez,

7
head -n -2non è compatibile con POSIX .
l0b0

9

Se vuoi tutte le linee tranne la prima N-1, chiama tailcon il numero di linee +N. (Il numero è il numero della prima riga che si desidera conservare, a partire da 1, ovvero +1 significa iniziare in alto, +2 significa saltare una riga e così via).

tail -n +3 foo.txt >>other-document

Non esiste un modo semplice e portatile per saltare le ultime N linee. GNU headconsente head -n +Ncome controparte di tail -n +N. Altrimenti, se hai tac(ad esempio GNU o Busybox), puoi combinarlo con tail:

tac | tail -n +3 | tac

Portabilmente, puoi usare un filtro awk (non testato):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Se si desidera rimuovere le ultime righe da un file di grandi dimensioni, è possibile determinare l'offset di byte del pezzo da troncare, quindi eseguire il troncamento con dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

All'inizio non è possibile troncare un file, anche se è necessario rimuovere le prime righe di un file di grandi dimensioni, è possibile spostare i contenuti .


Su alcuni sistemi (come il moderno Linux), puoi troncare (comprimere) un file in atto all'inizio, ma di solito solo per un importo che è multiplo della dimensione del blocco FS (quindi non molto utile in questo caso).
Stéphane Chazelas,

3

Dalla tailpagina man (GNU tail, cioè):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Pertanto, quanto segue dovrebbe aggiungere tutto tranne le prime 2 righe di somefile.txta anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt

3

Per rimuovere le prime n righe è possibile utilizzare GNU sed. Ad esempio se n = 2

sed -n '1,2!p' input-file

La !media "esclude questo intervallo". Come puoi immaginare, ad esempio puoi ottenere risultati più complicati

sed -n '3,5p;7p'

che mostrerà la riga 3,4,5,7. Più potere viene dall'uso delle espressioni regolari anziché dagli indirizzi.

Il limite è che i numeri delle linee devono essere conosciuti in anticipo.


1
Perché non solo sed 1,2d? Di solito è più semplice. Inoltre, nulla nei tuoi esempi è specifico di GNU Sed; tutti i tuoi comandi utilizzano le funzionalità POSIX standard di Sed .
Carattere jolly

1

È possibile utilizzare diffper confrontare l'output di head/ tailcon il file originale e quindi rimuovere ciò che è lo stesso, ottenendo quindi l'inverso.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)

1

Mentre tail -n +4l'output del file a partire dalla 4a riga (tutte tranne le prime 3 righe) è standard e portatile, la sua headcontroparte ( head -n -3tutte tranne le ultime 3 righe) non lo è.

Portabilmente, faresti:

sed '$d' | sed '$d' | sed '$d'

O:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(attenzione che su alcuni sistemi in cui sedè presente uno spazio modello di dimensioni limitate, che non si adatta a grandi valori di n).

O:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'

1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Se <infileè un lseek()file normale, in grado, allora sì , sicuramente, sentiti libero. Quanto sopra è un costrutto completamente POSIX supportato.


0

Il mio approccio è simile a Gilles, ma invece invertisco il file con cat and pipe che con il comando head.

tac -r thefile.txt | head thisfile.txt (sostituisce i file)


0

Spero di aver capito chiaramente il tuo bisogno.

Hai diversi modi per completare la tua richiesta:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Dove / etc / passwd è il tuo file

La seconda soluzione può essere utile se si dispone di file di grandi dimensioni:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"

0

Soluzione per BSD (macOS):

Rimuovi le prime 2 righe:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Rimuovi le ultime 2 righe:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... non molto elegante ma fa il lavoro!

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.