Melhor maneira de remover bytes do início de um arquivo?

56

Hoje eu tive que remover os primeiros 1131 bytes de um arquivo misto de texto / binário de 800MB, um dump de subversão filtrado que estou hackeando para um novo repositório. Qual é a melhor maneira de fazer isso?

Para começar, tentei

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

mas depois do salto, copia o restante do arquivo um byte de cada vez, ou seja, muito lentamente. No final, eu trabalhei que precisava de 405 bytes para arredondar isso para três blocos de 512 que eu poderia pular

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

que foi concluído rapidamente, mas deve ter havido uma maneira mais simples / melhor? Existe outra ferramenta que eu esqueci? Obrigado!

    
por Rup 03.02.2011 / 18:34

7 respostas

58

Você pode alternar entre opções e ignorar opções:

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

Desta forma, a operação pode se beneficiar de um bloco maior.

Caso contrário, você poderia tentar com o tail (embora não seja seguro usá-lo com arquivos binários):

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

Finalmente, você pode usar 3 instâncias dd para escrever algo assim:

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

onde o primeiro dd imprime sua saída padrão filtrada.dump; o segundo apenas lê 1131 bytes e os joga fora; então, o último lê de sua entrada padrão os bytes restantes de filtrado e grava-os em trimmed.dump.

    
por 03.02.2011 / 23:08
16

Não tenho certeza quando skip_bytes foi adicionado, mas pule os primeiros 11 bytes que você tem:

# 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

Em que iflag=skip_bytes informa ao dd para interpretar o valor da opção skip como bytes em vez de blocos, tornando-o simples.

    
por 17.05.2014 / 22:08
15

Você pode usar um sub-shell e duas chamadas dd desta forma:

$ ( 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
    
por 03.02.2011 / 23:09
6

Se o sistema de arquivos e o kernel do Linux suportarem, então você pode tentar fallocate se você quiser fazer as alterações no local: no melhor caso, não há nenhum IO de dados:

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

onde <magic> depende do sistema de arquivos, da versão do Linux e do tipo de arquivo ( FALLOC_FL_COLLAPSE_RANGE ou FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE pode ser usado internamente ).

    
por 27.02.2015 / 14:15
3

Você deve usar count=0 - é um lseek() simples sempre que possível.

Assim:

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

dd irá lseek() do descritor do arquivo de entrada para um deslocamento de 1131 bytes, e então cat simplesmente copiará o que restar para a saída.

    
por 29.12.2015 / 13:43
2

No entanto, outra maneira de remover os bytes principais de um arquivo (sem usar dd ) é usar xxd e sed ou tail respectivamente.

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
    
por 15.04.2013 / 16:40
2

@maxschlepzig pede um folheto on-line. Aqui está um em perl. Leva 2 argumento: De byte e comprimento. O arquivo de entrada deve ser dado por '<' e a saída será no 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 o tamanho for maior que o arquivo, o restante do arquivo será copiado.

No meu sistema, isso oferece 3,5 GB / s.

    
por 29.03.2014 / 07:52

Tags