web-dev-qa-db-fra.com

La meilleure façon de supprimer les octets du début d'un fichier?

Aujourd'hui, j'ai dû supprimer les 1131 premiers octets d'un fichier mixte texte/binaire de 800 Mo, un vidage Subversion filtré que je pirate pour un nouveau référentiel. Quelle est la meilleure façon de procéder?

Pour commencer j'ai essayé

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

mais après le saut, cela copie le reste du fichier un octet à la fois, c'est-à-dire très lentement. À la fin, je me suis rendu compte que j'avais besoin de 405 octets pour arrondir cela à trois blocs de 512 que je pouvais ignorer

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

qui s'est terminé assez rapidement mais il devait y avoir un moyen plus simple/meilleur? Y a-t-il un autre outil que j'ai oublié? Merci!

64
Rup

Vous pouvez changer bs et ignorer les options:

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

De cette façon, l'opération peut bénéficier d'un bloc plus important.

Sinon, vous pouvez essayer avec tail (bien qu'il ne soit pas sûr de l'utiliser avec des fichiers binaires):

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

Enfin, vous pouvez utiliser des instances 3 dd pour écrire quelque chose comme ceci:

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

où le premier dd affiche sa sortie standard filter.dump; le second lit juste 1131 octets et les jette; ensuite, le dernier lit à partir de son entrée standard les octets restants de filter.dump et les écrit dans trimmed.dump.

67
marco

Je ne sais pas quand skip_bytes a été ajouté, mais pour ignorer les 11 premiers octets, vous disposez:

# 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

iflag=skip_bytes indique à dd d'interpréter la valeur de l'option skip sous forme d'octets au lieu de blocs, ce qui la rend simple.

Vous pouvez utiliser un sous-shell et deux appels dd comme ceci:

$ ( 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
15
maxschlepzig

Si le système de fichiers et le noyau Linux le prennent en charge, vous pouvez essayer fallocate si vous souhaitez effectuer les modifications sur place: dans le meilleur des cas, il n'y a pas de données IO du tout:

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

<magic> dépend du système de fichiers, de la version Linux et du type de fichier ( FALLOC_FL_COLLAPSE_RANGE ou FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE pourrait être utilisé en interne ).

7
jfs

Vous devez utiliser count=0 - c'est une simple lseek() chaque fois que possible.

Comme ça:

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

dd va lseek() le descripteur du fichier d'entrée à un décalage de 1131 octets, puis cat copiera simplement tout ce qui reste à afficher.

3
mikeserv

Encore une autre façon de supprimer les octets de tête d'un fichier (sans utiliser dd du tout) consiste à utiliser xxd et sed ou tail respectivement.

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
2
wop

@maxschlepzig demande un liner en ligne. En voici un en Perl. Il faut 2 arguments: de l'octet et de la longueur. Le fichier d'entrée doit être donné par '<' et la sortie sera sur 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

Si la longueur est supérieure au fichier, le reste du fichier sera copié.

Sur mon système, cela fournit 3,5 Go/s.

2
Ole Tange