Il modo migliore per rimuovere i byte dall'inizio di un file?


62

Oggi ho dovuto rimuovere i primi 1131 byte da un file misto di testo / binario da 800 MB, un dump di sovversione filtrato che sto hackerando per un nuovo repository. Qual'è il miglior modo per farlo?

Per cominciare, ho provato

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

ma dopo il salto questo copia il resto del file un byte alla volta, cioè molto lentamente. Alla fine ho capito che avevo bisogno di 405 byte per arrotondare questo fino a tre blocchi di 512 che potevo saltare

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

che è stato completato abbastanza rapidamente ma deve esserci stato un modo più semplice / migliore? C'è un altro strumento di cui mi sono dimenticato? Grazie!


ddè lo strumento giusto per il lavoro: sembra che tu abbia trovato una soluzione piacevole ed elegante al tuo problema.
Justin Ethier,

Risposte:


64

Puoi cambiare bs e saltare le opzioni:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

In questo modo l'operazione può beneficiare di un blocco maggiore.

Altrimenti, potresti provare con tail (anche se non è sicuro usarlo con file binari):

tail -c +1132 filtered.dump >trimmed.dump

Infine, puoi usare 3 istanze dd per scrivere qualcosa del genere:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

dove il primo dd stampa il suo output standard filtered.dump; il secondo legge solo 1131 byte e li butta via; quindi l'ultimo legge dal suo input standard i byte rimanenti di filtered.dump e li scrive su trimmed.dump.


6
Grazie! Non sapevo che l'input convogliato fosse trasferito a un secondo processo del genere - è molto pulito. Non riesco a credere di non aver pensato bs=1131 skip=1però: - /
Rup

2
La maggior parte delle implementazioni moderne delle utilità di shell funzionano correttamente con i file binari (cioè non hanno problemi con caratteri null e non inseriranno una nuova riga alla fine del file). Certamente le implementazioni GNU e * BSD sono sicure.
Gilles 'SO- smetti di essere malvagio' il

Che cosa significa "non è sicuro utilizzarlo con i file binari"?
Scott,

18

Non sono sicuro quando è skip_bytesstato aggiunto, ma per saltare i primi 11 byte hai:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

Where iflag=skip_bytesdice a dd di interpretare il valore skipdell'opzione come byte anziché come blocchi, rendendolo semplice.


Certamente un vantaggio di velocità per file di grandi dimensioni e una piccola quantità di dati da rimuovere.
sstn

Questa è la risposta migliore, poiché funziona per ogni dimensione di blocco, ad esempioiflag=skip_bytes skip=1234 bs=1M
phiresky,

15

Puoi usare una sotto-shell e due ddchiamate come questa:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig

1
Grazie - Non sapevo che l'input di piping continuasse a un secondo processo del genere, immagino che sia la sotto shell? Lo ricorderò sicuramente! Ho dato il segno di spunta a Marco perché è arrivato prima ma +1 e grazie per la risposta!
Rup,

1
@Rup, sì, la sotto-shell - creata tramite le parentesi - fornisce un descrittore di file stdin ed entrambe le chiamate dd ne consumano successivamente l'input. Sì - Marco mi ha battuto di 29 secondi :)
maxschlepzig

6

Se il filesystem e il kernel Linux lo supportano, puoi provare fallocatese vuoi apportare le modifiche sul posto: nel migliore dei casi non ci sono dati IO:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

dove <magic>dipende dal filesystem, dalla versione di Linux e dal tipo di file ( FALLOC_FL_COLLAPSE_RANGEo FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEpotrebbe essere usato internamente ).


1
Questo è il mio metodo preferito, ma eseguirlo in un contenitore ha i suoi problemi. stackoverflow.com/questions/31155591/…
michaelcurry

3

Dovresti usare count=0- è semplice lseek()quando possibile.

Come questo:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddsarà lseek()il file di input a un descrittore di 1131 byte di offset, e poi catsemplicemente copiare ciò che rimane di uscita.


2

Ancora un altro modo per rimuovere i byte iniziali da un file (senza usare ddaffatto) è usare xxde sedo tailrispettivamente.

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump

È pulito, ma penso che preferisco lavorare con il file in binario piuttosto che convertirlo in e da hex.
Rup,

2

@maxschlepzig chiede un liner online. Eccone uno in perl. Prende 2 argomenti: da byte e lunghezza. Il file di input deve essere dato da '<' e l'output sarà su stdout:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift;
     while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){
        $left -= $read; syswrite(STDOUT,$buf);
     }' 12345678901 19876543212 < bigfile > outfile

Se la lunghezza è maggiore del file, il resto del file verrà copiato.

Sul mio sistema questo fornisce 3,5 GB / s.


Penso che la sua sfida a una riga sia stata quella di farti provare che la soluzione del linguaggio di scripting era migliore della sua soluzione a shell a una riga. E preferisco il suo: è più corto e più chiaro per me. Se il tuo funziona meglio è perché stai usando una dimensione del blocco più grande di lui, che è facilmente rialzabile anche nella sua versione.
Rup,

@Rup Ahimè, ma no. Sembra che tu dimentichi che ddnon garantisce una lettura completa. Prova: si | dd bs = 1024k count = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

Inoltre, la mia soluzione non leggerà i byte non necessari (che potrebbero essere lunghi diversi terabyte).
Ole Tange,
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.