Le meilleur moyen de supprimer des octets au début d’un fichier?


62

Aujourd'hui, je devais supprimer les 1131 premiers octets d'un fichier texte / binaire mixte de 800 Mo, un cliché de sous-version filtré que je cherchais dans un nouveau référentiel. Quelle est la meilleure façon de faire cela?

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, j’ai trouvé que j’avais besoin de 405 octets pour arrondir à trois blocs de 512 que je pouvais sauter.

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

qui a terminé assez rapidement mais il devait y avoir une manière plus simple / meilleure? Y a-t-il un autre outil que j'ai oublié? Merci!


ddest le bon outil pour le travail - on dirait que vous avez trouvé une solution élégante et élégante à votre problème.
Justin Ethier

Réponses:


64

Vous pouvez changer les options bs et skip:

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 que ce ne soit pas sûr de l'utiliser avec des fichiers binaires):

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

Enfin, vous pouvez utiliser 3 instances de 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 imprime sa sortie standard filter.dump; le second lit simplement 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.


6
Merci! Je ne savais pas que l'entrée par la chaîne était reportée dans un deuxième processus comme celui-là - c'est très chouette. Je n'arrive pas à croire que je n'y aie pas pensé bs=1131 skip=1: - /
Rup

2
La plupart des implémentations modernes d’utilitaires shell fonctionnent correctement avec les fichiers binaires (c’est-à-dire qu’elles n’ont aucun problème avec les caractères nuls et qu’elles ne vont pas insérer de nouvelle ligne à la fin du fichier). Les implémentations GNU et * BSD sont sûres.
Gilles 'SO- arrête d'être méchant'

Que signifie "non sûr de l'utiliser avec des fichiers binaires"?
Scott

18

Vous ne savez pas quand a skip_bytesété ajouté, mais pour ignorer les 11 premiers octets, vous avez:

# 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_bytesindique à dd d'interpréter la valeur de l' skipoption sous forme d'octets au lieu de blocs, ce qui simplifie les choses.


Certainement un avantage de vitesse pour les gros fichiers et une petite quantité de données à supprimer.
samedi

C'est la meilleure réponse, car cela fonctionne pour chaque taille de bloc, par exempleiflag=skip_bytes skip=1234 bs=1M
phiresky

15

Vous pouvez utiliser un sous-shell et deux ddappels 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

1
Merci - je ne savais pas que l'entrée par pipeline continuait pour un deuxième processus comme celui-là, je suppose que c'est le sous-shell? Je m'en souviendrai certainement! J'ai donné la tique à Marco parce qu'il est arrivé le premier mais +1 et merci pour la réponse!
Rup

1
@Rup, oui, le sous-shell - créé via les parenthèses - fournit un descripteur de fichier stdin et les deux appels dd en consomment successivement les entrées. Ouais - Marco m'a battu par 29 secondes :)
maxschlepzig

6

Si le système de fichiers et le noyau Linux le supportent, vous pouvez essayer fallocatesi vous souhaitez apporter les modifications à la place: dans le meilleur des cas, il n'y a pas du tout d'entrées / sorties de données:

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

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


1
Ceci est ma méthode préférée, mais son exécution dans un conteneur a ses problèmes. stackoverflow.com/questions/31155591/…
michaelcurry

3

Vous devriez utiliser count=0- c'est aussi simple lseek()que possible.

Comme ça:

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

ddLe lseek()descripteur de fichier d’entrée contiendra un décalage de 1131 octets, puis catcopiera simplement le reste à produire.


2

Un autre moyen de supprimer les octets de début d'un fichier (sans utiliser dddu tout) consiste à utiliser xxdet sedou tailrespectivement.

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

C'est bien, mais je pense que je préfère simplement travailler avec le fichier en binaire plutôt que de le convertir vers et depuis l'hex.
Rup

2

@maxschlepzig demande un liner en ligne. En voici un en perl. Il faut 2 arguments: De octet et de longueur. Le fichier d'entrée doit être donné par '<' et le résultat 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 plus grande que le fichier, le reste du fichier sera copié.

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


Je pense que son défi sur une ligne consistait à vous amener à prouver que la solution de langage de script était meilleure que sa solution de shell à une ligne. Et je préfère le sien: c'est plus court et plus clair pour moi. Si le vôtre fonctionne mieux, c'est que vous utilisez une taille de bloc plus grande que lui, ce qui est également possible dans sa version.
Rup

@Rup Hélas, mais non. Vous semblez oublier que ddcela ne garantit pas une lecture complète. Essayez: oui | dd bs = 1024k count = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

De plus, ma solution ne lira pas les octets dont vous n’avez pas besoin (qui peuvent durer plusieurs téraoctets).
Ole Tange
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.