Rimozione efficiente dell'intestazione sul posto per file di grandi dimensioni con sed?


24

I seguenti comandi possono richiedere alcuni minuti a seconda della dimensione del file. Esiste un metodo più efficace?

sed -i 1d large_file 

Risposte:


34

Prova edinvece:

ed <<< $'1d\nwq' large_file

Se quel "grande" significa circa 10 milioni di linee o più, un uso migliore tail. Non è in grado di eseguire modifiche sul posto, ma le sue prestazioni rendono la mancanza perdonabile:

tail -n +2 large_file > large_file.new

Modifica per mostrare alcune differenze temporali:

( awkcodice di Jaypal aggiunto per avere tempi di esecuzione sulla stessa macchina (CPU 2.2GHz).)

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s

In caso di tail, avrei preferito contare il tempo di fare entrambe rimuovere la prima linea e sostituirlo bigfile.txtcon bigfile.new.
rozcietrzewiacz,

@rozcietrzewiacz, il tuo punto è corretto. Grazie. Aggiornato.
arte

Questo è davvero fantastico! Ho fatto lo stesso con awke ho ottenuto il seguente risultato -[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
jaypal singh,

1
@Jaypal, ho aggiunto il tuo codice all'elenco di alternative. Sulla mia macchina era ancora più veloce. Strano, mi aspettavo che awkle prestazioni fossero più vicine a quelle sed. (Nota per me: non aspettarti mai - prova invece.)
Manatwork

Questa è stata la soluzione migliore nel mio caso: tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;sto usando un singolo file con un lucchetto per tenere traccia di un unico elenco di attività utilizzato da più processi. Ho iniziato con quello che il manifesto iniziale utilizzato: sed -i 1d large_file . Ciò causava il blocco del file per 1-2 secondi. La tail/mvcombo si completa quasi istantaneamente. Grazie!
Chris Adams,

6

Non è possibile rimuovere in modo efficiente le cose dall'inizio di un file. La rimozione dei dati dall'inizio richiede la riscrittura dell'intero file.

Il troncamento dalla fine di un file può essere molto rapido (il sistema operativo deve solo modificare le informazioni sulla dimensione del file, eventualmente eliminando i blocchi ora inutilizzati). Questo non è generalmente possibile quando si tenta di rimuovere dalla testa di un file.

Teoricamente potrebbe essere "veloce" se si rimuovesse esattamente un intero blocco / estensione, ma non ci sono chiamate di sistema per questo, quindi dovresti fare affidamento sulla semantica specifica del filesystem (se esiste). (O avendo una qualche forma di offset all'interno del primo blocco / estensione per contrassegnare il vero inizio del file, immagino. Nemmeno mai sentito parlare di questo.)


Se il file è molto grande, è probabile che l'overhead I / O sia (probabilmente molto) maggiore dell'overhead della CPU necessario per elaborare la fine delle linee.
Mat

Hai ragione. Tuttavia, potrebbero esserci differenze nel modo in cui gli strumenti accedono al contenuto del file. Il meglio non è elaborare riga per riga quando non è necessario o almeno non leggere riga per riga quando non è necessario.
arte

2
Sono sorpreso che la differenza sia così grande nei tuoi risultati e posso riprodurla con quella dimensione del file qui. I vantaggi sembrano diminuire con l'aumentare delle dimensioni del file (provato con seq 10M, 15s per sed, 5s per ed). Buoni consigli comunque (+1).
Mat

A partire dalla versione 3.15, Linux ora ha un'API per comprimere parti di un file su file system basati su una certa estensione, ma almeno per ext4 che può essere fatto solo su blocchi completi (di solito 4k).
Stéphane Chazelas,

Anche se la modifica richiede la riscrittura dell'intero file, a volte è molto utile disporre di strumenti da riga di comando per una modifica efficiente. Nel mio caso, questo mi ha aiutato quando ho dovuto rimuovere la prima riga di un file che era più grande della mia RAM di sistema totale.
Jason,

3

Il metodo più efficiente, non farlo! Se lo fai, in ogni caso, hai bisogno del doppio dello spazio "grande" sul disco e sprechi IO.

Se sei bloccato con un file di grandi dimensioni che desideri leggere senza la 1a riga, attendi di doverlo leggere per rimuovere la 1a riga. Se devi inviare il file da stdin a un programma, usa tail per farlo:

tail -n +2 | your_program

Quando è necessario leggere il file, è possibile cogliere l'occasione per rimuovere la prima riga, ma solo se si dispone dello spazio necessario sul disco:

tail -n +2 | tee large_file2 | your_program

Se non riesci a leggere dallo stdin, usa un fifo:

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

ancora meglio se stai usando bash, approfitta della sostituzione del processo:

your_program -i <(tail -n +2 large_file)

Se hai bisogno di cercare nel file, non vedo una soluzione migliore di non rimanere bloccato con il file in primo luogo. Se questo file è stato generato da stdout:

large_file_generator | tail -n +2 > large_file

Altrimenti, esiste sempre la soluzione sostitutiva del processo o del Fifo:

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)

1

Puoi usare Vim in modalità Ex:

ex -sc '1d|x' large_file
  1. 1 seleziona la prima riga

  2. d Elimina

  3. x salva e chiudi


0

Questo è solo teorizzare, ma ...

Un filesystem personalizzato (implementato usando FUSE o un meccanismo simile) potrebbe esporre una directory il cui contenuto è esattamente uguale a una directory già esistente altrove, ma con i file troncati come desideri. Il filesystem tradurrebbe tutti gli offset dei file. Quindi non dovresti fare una lunga riscrittura di un file.

Ma dato che questa idea è molto banale, a meno che tu non abbia decine di terabyte di tali file, implementare tale filesystem sarebbe troppo costoso / richiede tempo per essere pratico.

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.