Rimuovi l’ultima riga da un file in Bash

Ho un file, foo.txt , contenente le seguenti linee:

 a b c 

Voglio un semplice comando che si foo.txt nel contenuto di foo.txt essere:

 a b 

Usando GNU sed :

 sed -i '$ d' foo.txt 

L’opzione -i non esiste nelle versioni di GNU sed precedenti a 3.95, quindi devi usarlo come filtro con un file temporaneo:

 cp foo.txt foo.txt.tmp sed '$ d' foo.txt.tmp > foo.txt rm -f foo.txt.tmp 

Naturalmente, in quel caso potresti usare anche head -n -1 invece di sed .

Questa è di gran lunga la soluzione più rapida e semplice, specialmente su file di grandi dimensioni:

 head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt 

se vuoi cancellare la riga superiore usa questo:

 tail -n +2 foo.txt 

che significa linee di uscita a partire dalla linea 2.

Non usare sed per cancellare le righe dall’alto o dal basso di un file – è molto molto lento se il file è grande.

Ho avuto problemi con tutte le risposte qui perché stavo lavorando con un file ENORME (~ 300 Gb) e nessuna delle soluzioni è stata ridimensionata. Ecco la mia soluzione:

 dd if=/dev/null of= bs=1 seek=$(echo $(stat --format=%s  ) - $( tail -n1  | wc -c) | bc ) 

In parole: trova la lunghezza del file con cui vuoi finire (lunghezza del file meno la lunghezza della sua ultima riga, usando bc ) e, imposta quella posizione come la fine del file ( dd un byte di dd di /dev/null su di esso).

Questo è veloce perché tail inizia a leggere dalla fine, e dd sovrascriverà il file invece di copiare (e analizzare) ogni riga del file, che è ciò che fanno le altre soluzioni.

NOTA: questo rimuove la linea dal file in posizione! Crea un backup o prova su un file fittizio prima di provarlo sul tuo file!

Per rimuovere l’ultima riga da un file senza leggere l’intero file o riscrivere nulla , puoi utilizzare

 tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{} 

Per rimuovere l’ultima riga e stamparla anche su stdout (“pop”), puoi combinare tale comando con tee :

 tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{}) 

Questi comandi possono elaborare in modo efficiente un file molto grande. Questo è simile e ispirato alla risposta di Yossi, ma evita di usare alcune funzioni extra.

Se li usi ripetutamente e vuoi la gestione degli errori e alcune altre funzionalità, puoi usare il comando poptail qui: https://github.com/donm/evenmoreutils

Utenti Mac

se si desidera solo l’output dell’ultima riga cancellata senza modificare il file stesso

sed -e '$ d' foo.txt

se si desidera eliminare l’ultima riga del file di input stesso

sed -i '' -e '$ d' foo.txt

Per utenti Mac:

Su Mac, head -n -1 non funzionerà. E, stavo cercando di trovare una soluzione semplice [senza preoccuparmi del tempo di elaborazione] per risolvere questo problema usando solo i comandi “testa” e / o “coda”.

Ho provato la seguente sequenza di comandi ed ero felice di poterlo risolvere usando il comando “tail” [con le opzioni disponibili su Mac]. Quindi, se sei su Mac e vuoi usare solo “coda” per risolvere questo problema, puoi usare questo comando:

cat file.txt | coda -r | coda -n +2 | coda -r

Spiegazione :

1> tail -r: inverte semplicemente l’ordine delle righe nel suo input

2> tail -n +2: stampa tutte le righe a partire dalla seconda riga nel suo input

 echo -e '$d\nw\nq'| ed foo.txt 
 awk 'NR>1{print buf}{buf = $0}' 

In sostanza, questo codice dice quanto segue:

Per ogni riga dopo la prima, stampa la linea bufferizzata

per ogni riga, resettare il buffer

Il buffer è ritardato di una riga, quindi si finisce per stampare le righe da 1 a n-1

 awk "NR != `wc -l < text.file`" text.file |> text.file 

Questo snippet fa il trucco.

Entrambe queste soluzioni sono qui in altre forms. Ho trovato questi un po ‘più pratico, chiaro e utile:

Usando dd:

 BADLINESCOUNT=1 ORIGINALFILE=/tmp/whatever dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) /bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE} 

Usando truncate:

 BADLINESCOUNT=1 ORIGINALFILE=/tmp/whatever truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE} 

Rubino (1.9+)

 ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file