it-swarm.it

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

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

Cioè Voglio solo ottenere tutto tranne le prime 2 righe di foo.txt da aggiungere a un altro documento.

57
chrisjlee

Puoi usarlo per rimuovere le due righe prima:

tail -n +3 foo.txt

e questo per eliminare le due righe last:

head -n -2 foo.txt

(supponendo che il file finisca con \n per quest'ultimo)


Proprio come per l'uso standard di tail e head queste operazioni non sono distruttive. Uso >out.txt se desideri reindirizzare l'output su un nuovo file:

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

Nel caso out.txt esiste già, sovrascriverà questo file. Uso >>out.txt invece di >out.txt se preferisci che l'output venga aggiunto a out.txt.

59

Se vuoi tutte tranne le prime N-1 linee, chiama tail con 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 head consente head -n +N come 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 in giro .

Dalla pagina man tail (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 tutte tranne le prime 2 righe di somefile.txt per anotherfile.txt:

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

Per rimuovere le prime n righe GNU sed può essere usato. Ad esempio se n = 2

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

Il ! significa "escludi 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.

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

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

2
mikeserv

Mentre tail -n +4 per generare il file a partire dalla 4a riga (tutte tranne le prime 3 righe) è standard e portatile, la sua controparte head (head -n -3, tutto tranne le ultime 3 righe) no.

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 ha uno spazio modello di dimensioni limitate, che non si ridimensiona a valori elevati di n).

O:

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

Puoi usare diff per confrontare l'output di head/tail con il file originale e quindi rimuovere lo stesso, ottenendo quindi l'inverso.

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

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
Abe

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!

0
spectrum

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
Mathieu Coavoux