Come usare sed per rimuovere le ultime n righe di un file


148

Voglio rimuovere alcune n righe dalla fine di un file. Questo può essere fatto usando sed?

Ad esempio, per rimuovere le linee da 2 a 4, posso usare

$ sed '2,4d' file

Ma non conosco i numeri di riga. Posso cancellare l'ultima riga usando

$sed $d file

ma voglio sapere come rimuovere n righe dalla fine. Per favore fatemi sapere come farlo usando sed o qualche altro metodo.


2
@arashkordi: che utilizza un conteggio di riga dalla parte superiore del file, non dalla parte inferiore.
AMS

Domanda correlata sui superutente .
Thor,

//, potresti considerare di riformulare la domanda per renderla più generale della semplice sed ?
Nathan Basanese,

1
sed $d filerestituisce un errore. Invece, $ d dovrebbe essere tra virgolette, in questo modo:sed '$d' file
Akronix l'

Risposte:


221

Non lo so sed, ma si può fare con head:

head -n -2 myfile.txt

19
+1 per semplicità. Il comando sed equivalente è brutto: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5per le ultime 5 righe).
Marc B,

62
Nota che funziona con alcune versioni di head, ma non è standard. In effetti, lo standard per gli headstati:The application shall ensure that the number option-argument is a positive decimal integer.
William Pursell,

21
Per aggiungere alla risposta di @ WilliamPursell ... Sulla shell predefinita di Mac OS, questo non funziona.
JDS

7
Né su BSD, ma la domanda è taggata Linux.
AMS

3
meno 1, perché, oltre a osx, questo non funziona per me su Ubuntu14 -head: illegal line count -- -2
Michael Durrant,

30

Se hardcoding n è un'opzione, è possibile utilizzare le chiamate sequenziali per sed. Ad esempio, per eliminare le ultime tre righe, elimina l'ultima riga tre volte:

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

2
Questo, più -i per modificare il file in atto piuttosto che scaricarlo su stdout, ha funzionato per me.
WAF,

27

Dai sed one-liner :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Sembra essere quello che stai cercando.


@Thor è possibile modificare il numero di righe rimosse dal file modificando 10in'$d;N;2,10ba'
Alexej Magura,

1
@AlexejMagura: stavo commentando una versione precedente della risposta.
Thor,

22

Un divertente e semplice sede tacla soluzione:

n=4
tac file.txt | sed "1,$n{d}" | tac

NOTA

  • le virgolette doppie "sono necessarie affinché la shell valuti la $nvariabile in sedcomando. Tra virgolette singole, non verrà eseguita alcuna interpolazione.
  • tacè al catcontrario, vediman 1 tac
  • gli {}in sedsono lì per separare $n& d(in caso contrario, la shell tenta di interpolare la $ndvariabile inesistente )

7
fyi no tacsu osx
Michael Durrant,

1
@MichaelDurrant port info coreutils|| brew info coreutils;
Voci del

19

Usa sed, ma lascia che la shell faccia i calcoli, con l'obiettivo di usare il dcomando dando un intervallo (per rimuovere le ultime 23 righe):

sed -i "$(($(wc -l < file)-22)),\$d" file

Per rimuovere le ultime 3 righe, dall'interno verso l'esterno:

$(wc -l < file)

Fornisce il numero di righe del file: diciamo 2196

Vogliamo rimuovere le ultime 23 linee, quindi per il lato sinistro o l'intervallo:

$((2196-22))

Dà: 2174 Quindi l'interpretazione originale di sed dopo shell è:

sed -i '2174,$d' file

Con la -imodifica sul posto, il file ora è 2173 righe!

Se si desidera salvarlo in un nuovo file, il codice è:

sed -i '2174,$d' file > outputfile

15

Puoi usare la testa per questo.

Uso

$ head --lines=-N file > new_file

dove N è il numero di righe che si desidera rimuovere dal file.

I contenuti del file originale meno le ultime N righe sono ora in new_file


8

Solo per completezza vorrei aggiungere la mia soluzione. Ho finito per farlo con lo standard ed:

ed -s sometextfile <<< $'-2,$d\nwq'

Questo elimina le ultime 2 linee utilizzando la modifica sul posto (anche se non utilizza un file temporaneo nella /tmp!!)


5

Per troncare file di dimensioni molto grandi sul posto, abbiamo il truncatecomando. Non conosce le linee, ma tail+ wcpuò convertire le linee in byte:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

Esiste un'ovvia condizione di competizione se il file viene scritto contemporaneamente. In questo caso potrebbe essere meglio usarlo head- conta i byte dall'inizio del file (mind disk IO), quindi tronceremo sempre il limite della linea (probabilmente più righe del previsto se il file è scritto attivamente):

truncate -s $(head -n -$lines $file | wc -c) $file

Handy one-liner in caso di errore, tentare di inserire la password al posto del nome utente:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure

4

Questo potrebbe funzionare per te (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file

Bello. Hai perso l'apostrofo finale ( ').
Thor,

scusate il commento così in ritardo ma ci provo e (adattato per il mio AIX / posix) non è riuscito a stampare tutto tranne l'ultima riga. Leggendo il codice, non capisco come vengano rimosse le ultime quattro righe, il ciclo esplicito si trova su 4 prime righe, quindi sicuramente dopo un d, Do Pcon GNU sed e non sulla versione posix. Sembra che le righe non vengano stampate dopo il Dsimbolo e rimangano nel buffer di lavoro per un nuovo loop senza passare alla "fine" della riga delle azioni.
NeronLeVelu,

@NeronLeVelu il programma legge in una finestra di 4 righe nello spazio del motivo, quindi aggiunge la riga successiva e stampa la prima fino a raggiungere la fine del file dove elimina le righe rimanenti.
potong

@potong sì, collaudo su una GNU sed e mantiene il buffer tra le linee in cui posix lo scarica dopo aver Dfatto l'effetto opposto.
NeronLeVelu,

4

La maggior parte delle risposte precedenti sembra richiedere comandi / estensioni GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Per una soluzione leggermente più porabile:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

O

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

O

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Dove "10" è "n".


2

Con le risposte qui avresti già imparato che sed non è lo strumento migliore per questa applicazione.

Comunque penso che ci sia un modo per farlo usando sed; l'idea è di aggiungere N righe per contenere lo spazio fino a quando non si è in grado di leggere senza colpire EOF. Quando viene colpito EOF, stampa il contenuto dello spazio di attesa ed esci.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

Il comando sed sopra ometterà le ultime 5 righe.


2

Prova il seguente comando:

n = line number
tail -r file_name | sed '1,nd' | tail -r

Per favore, spiega come funziona. Cordiali saluti - per inizializzare le variabili della shell, non dovremmo avere uno spazio intorno =.
codeforester,

@codeforester La prima riga era un commento immagino. Usa solo tail -rdove altri hanno usato tacper invertire l'ordine delle linee e fare in modo che le ultime nlinee diventino le nprime.
Nicolas Melay,

2

Può essere fatto in 3 passaggi:

a) Contare il numero di righe nel file che si desidera modificare:

 n=`cat myfile |wc -l`

b) Sottrai da quel numero il numero di righe da cancellare:

 x=$((n-3))

c) Di 'a sed di cancellare da quel numero di riga ( $x) fino alla fine:

 sed "$x,\$d" myfile

Cattiva matematica. Se devi eliminare 3 righe, sottrai 3 ma AGGIUNGI 1. Con la tua matematica, se il file ha 10 righe, 10 - 3 = 7 e stai usando sedper eliminare le righe da 7 a 10, ovvero 4 righe, non 3.
mathguy,

1

È possibile ottenere il conteggio totale delle righe wc -l <file>e utilizzare

head -n <total lines - lines to remove> <file>


1

Questo rimuoverà le ultime 3 righe da file:

for i in $(seq 1 3); do sed -i '$d' file; done;


1
Questo è troppo costoso per file di grandi dimensioni: l'intero file deve essere letto e riscritto n volte! E altrettante forchette per sed.
codeforester,

anche se questa potrebbe non essere la soluzione più efficiente, è di gran lunga la più semplice e facile da capire
Dharam

0

Preferisco questa soluzione;

head -$(gcalctool -s $(cat file | wc -l)-N) file

dove N è il numero di righe da rimuovere.


0
sed -n ':pre
1,4 {N;b pre
    }
:cycle
$!{P;N;D;b cycle
  }' YourFile

versione posix


0

Per eliminare le ultime 4 righe:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   

un po 'pesante (soprattutto nelle risorse) rispetto a una testa. Almeno tac file | sed '1,4d' | tacperché l'ordinamento rimuove quindi il prefisso costa molto risorse.
NeronLeVelu,

@NeronLeVelu hai ragione ma non c'è alcun taccomando: alcuni sistemi (ad esempio FreeBSD?)
mstafreshi,

0

Ho pensato a questo, dove n è il numero di righe che si desidera eliminare:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

È una piccola rotonda, ma penso che sia facile da seguire.

  1. Conta il numero di righe nel file principale
  2. Sottrai il numero di righe che desideri rimuovere dal conteggio
  3. Stampa il numero di righe che desideri conservare e memorizza in un file temporaneo
  4. Sostituisci il file principale con il file temporaneo
  5. Rimuovi il file temporaneo

0

Per eliminare le ultime N righe di un file, è possibile utilizzare lo stesso concetto di

$ sed '2,4d' file

Puoi usare una combo con il comando tail per invertire il file: se N è 5

$ tail -r file | sed '1,5d' file | tail -r > file

E in questo modo funziona anche dove il head -n -5 filecomando non viene eseguito (come su un Mac!).


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.