Rimuovi l'ultima riga da un file in Bash


Risposte:


408

Utilizzando GNU sed:

sed -i '$ d' foo.txt

L' -iopzione non esiste nelle GNU sedversioni precedenti alla 3.95, quindi è necessario utilizzarla come filtro con un file temporaneo:

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

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

Mac OS:

Su Mac OS X (a partire dal 10.7.4), l'equivalente del sed -icomando sopra è

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

56
Su Mac OS X (a partire dal 10.7.4), l'equivalente del sed -icomando sopra è sed -i '' -e '$ d' foo.txt. Inoltre, head -n -1attualmente non funziona su un Mac.
mklement0

26
Potresti spiegare cosa fa '$ d' regex? È una domanda sulla rimozione dell'ultima riga, quindi penso che questa sia la parte più importante per tutti coloro che visualizzano questa domanda. Grazie :)
kravemir

38
@Miro: non è affatto $ duna regex. È un comando sed. dè il comando per eliminare una riga, mentre $significa "l'ultima riga nel file". Quando si specifica una posizione (chiamata "range" in sed lingo) prima di un comando, quel comando viene applicato solo alla posizione specificata. Quindi, questo comando dice esplicitamente "nell'intervallo dell'ultima riga in un file, eliminalo". Abbastanza lucido e dritto al punto, se me lo chiedi.
Daniel Kamil Kozar,

1
Come si rimuove l'ultima riga ma solo se si tratta di una riga vuota?
skan

1
@Alex: aggiornato per GNU sed; nessuna idea delle varie versioni di BSD / UNIX / MacOS sed ...
thkala,

279

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

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

se vuoi cancellare la prima riga usa questo:

tail -n +2 foo.txt

che significa linee di uscita che iniziano alla linea 2.

Non utilizzare sedper eliminare le righe dalla parte superiore o inferiore di un file: è molto lento se il file è di grandi dimensioni.


16
head -n -1 foo.txtè abbastanza
ДМИТРИЙ МАЛИКОВ,

20
head -n -1 non funziona sulla testa di bdsutils. almeno non nella versione utilizzata da macos, quindi non funziona.
johannes_lalala,

23
@johannes_lalala Su Mac, puoi brew install coreutils(utility GNU core) e usare gheadinvece di head.
interessante il

Ecco un semplice script bash che lo automatizza nel caso in cui tu abbia più file con un diverso numero di righe in basso da eliminare: cat TAILfixer Eseguilo FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}per cancellare 4 linee ad esempio da myfile come: ./TAILfixer myfile 4ovviamente prima chmod +x TAILfixer
rendilo

Complimenti perché ha funzionato, ma nonostante tutto il confronto sed, questo comando è anche piuttosto lento
Jeff Diederiks,

121

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

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

In parole: scopri la lunghezza del file che vuoi finire (lunghezza del file meno la lunghezza della sua ultima riga, usando bc) e, imposta quella posizione come la fine del file ( ddinserendo un byte di /dev/null in esso).

Questo è veloce perché tailinizia a leggere dalla fine e ddsovrascriverà il file in atto anziché copiare (e analizzare) ogni riga del file, che è ciò che fanno le altre soluzioni.

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


3
Non sapevo nemmeno di dd. Questa dovrebbe essere la risposta migliore. Grazie mille! È un peccato che la soluzione non funzioni per (bloccare) i file compressi con gzip.
tommy.carstensen,

4
Approccio molto, molto intelligente! Solo una nota: richiede e funziona su un file reale, quindi non può essere utilizzato su pipe, sostituzioni di comandi, reindirizzamenti e tali catene.
MestreLion,

3
Questa dovrebbe essere la risposta migliore. Ha funzionato all'istante per il mio file ~ 8 GB.
Peter Cogan,

8
utenti mac: dd if = / dev / null di = <nomefile> bs = 1 seek = $ (echo $ (stat -f =% z <nomefile> | cut -c 2-) - $ (tail -n1 <nomefile> | wc -c) | bc)
Igor,

2
Questo approccio è la strada da percorrere per file di grandi dimensioni!
Thomas.

61

Per rimuovere l'ultima riga da un file senza leggere l'intero file o riscrivere qualsiasi cosa , è possibile 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 quel 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 hai intenzione di utilizzarli ripetutamente e desideri la gestione degli errori e alcune altre funzionalità, puoi utilizzare il poptailcomando qui: https://github.com/donm/evenmoreutils


3
Nota rapida: truncatenon è disponibile su OS X per impostazione predefinita. Ma è facile da installare utilizzando Brew: brew install truncate.
Guillaume Boudreau,

3
Molto bella. Molto meglio senza dd. Ero terrorizzato all'utilizzo di dd come una singola cifra o lettera fuori posto in grado di sillabare il disastro .. +1
Yossi Farjoun

1
(JOKE WARNING) In realtà, ("dd" -> "disaster") richiede 1 sostituzione e 6 inserimenti; difficilmente "una singola cifra o lettera fuori posto". :)
Kyle,

29

Utenti Mac

se si desidera solo l'output dell'ultima riga eliminato 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


Questo funziona per me ma non capisco. comunque, ha funzionato.
Melvin,

Il flag -i ''dice a sed di modificare il file sul posto, mantenendo un backup in un file con l'estensione fornita come parametro. Poiché il parametro è una stringa vuota, non viene creato alcun file di backup. -edice a sed di eseguire un comando. Il comando $ dsignifica: trova l'ultima riga ( $) ed eliminala ( d).
Krzysztof Szafranek,

24

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 "head" e / o "tail".

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

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

Spiegazione :

1> tail -r: inverte semplicemente l'ordine delle linee nel suo input

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


2
Installare coreutils usando Homebrew ti darà la testa GNU come 'ghead', permettendoti di farloghead -n -1
un po

13
echo -e '$d\nw\nq'| ed foo.txt

2
Per una soluzione di filtro che non modifica il file in atto, utilizzare sed '$d'.
lhf,

8
awk 'NR>1{print buf}{buf = $0}'

In sostanza, questo codice dice quanto segue:

Per ogni riga successiva alla prima, stampare la riga bufferizzata

per ogni riga, ripristinare il buffer

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


2
Questa soluzione è un filtro e non modifica il file in atto.
lhf,


0

Entrambe queste soluzioni sono qui in altre forme. Ho trovato questi un po 'più pratici, chiari e utili:

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}

Utilizzando troncare:

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

Ahia. Troppo complicato.
Robert

0

Linux

$ è l'ultima riga, d per eliminare:

sed '$d' ~/path/to/your/file/name

Mac OS

Equivalente della sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name

0

Ecco come puoi farlo manualmente (personalmente uso molto questo metodo quando devo rimuovere rapidamente l'ultima riga di un file):

vim + [FILE]

Quello + segno lì significa che quando il file viene aperto nell'editor di testo vim, il cursore si posizionerà sull'ultima riga del file.

Ora basta premere ddue volte sulla tastiera. Questo farà esattamente quello che vuoi: rimuovere l'ultima riga. Successivamente, premere :seguito da, xquindi premere Enter. Ciò salverà le modifiche e ti riporterà alla shell. Ora, l'ultima riga è stata rimossa correttamente.


2
L'enfasi sulla semplicità è andata persa qui
Peter M,

-4

Rubino (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
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.