J'ai un fichier de 1 To. Je voudrais lire de l'octet 12345678901 à l'octet 19876543212 et mettre cela sur la sortie standard sur une machine avec 100 Mo de RAM.
Je peux facilement écrire un script perl qui fait cela. sysread délivre 700 Mo / s (ce qui est bien), mais syswrite ne délivre que 30 Mo / s. Je voudrais quelque chose de plus efficace, de préférence quelque chose qui est installé sur chaque système Unix et qui peut fournir de l'ordre de 1 Go / s.
Ma première idée est:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Mais ce n'est pas efficace.
Éditer:
Je n'ai aucune idée de la façon dont j'ai mal mesuré l'écriture système. Cela fournit 3,5 Go / s:
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-12345678901)) < bigfile
et évite le yes | dd bs=1024k count=10 | wc
cauchemar.
bs=1M iflag=skip_bytes,count_bytes
Réponses:
Ceci est lent en raison de la petite taille du bloc. En utilisant un GNU récent
dd
( coreutils v8.16 + ), la manière la plus simple est d'utiliser les optionsskip_bytes
etcount_bytes
:Mise à jour
fullblock
option ajoutée ci-dessus selon la réponse @Gilles . Au début, je pensais que cela pouvait être impliqué parcount_bytes
, mais ce n'est pas le cas.Les problèmes mentionnés sont un problème potentiel ci-dessous, si
dd
les appels en lecture / écriture sont interrompus pour une raison quelconque, les données seront perdues. Ce n'est pas probable dans la plupart des cas (les chances sont quelque peu réduites puisque nous lisons à partir d'un fichier et non d'un tuyau).L'utilisation d'un
dd
sans les optionsskip_bytes
etcount_bytes
est plus difficile:Vous pouvez également expérimenter différentes tailles de blocs, mais les gains ne seront pas très spectaculaires. Voir - Existe - t-il un moyen de déterminer la valeur optimale pour le paramètre bs à dd?
la source
bs
n'est pas un facteur deskip
?skip
c'est un certain nombre de blocs, pas d'octets. Peut-être que vous êtes confus carskip_bytes
est utilisé dans le premier exemple, ce qui signifie qu'ilskip
est en octets?bs
est4,096
, ce qui signifie que vous ne pouvez pas sauter plus précisément que les4,096
octetsdd
la première et de la dernière utilisationbs=1
afin de copier les données qui ne démarrent ni ne se terminent sur un alignement de bloc.bs=1
indiquedd
de lire et d'écrire un octet à la fois. Il y a des frais généraux pour chacunread
et pour chaquewrite
appel, ce qui rend cela lent. Utilisez une taille de bloc plus grande pour des performances décentes.Lorsque vous copiez un fichier entier, au moins sous Linux, je l'ai trouvé
cp
etcat
sont plus rapides quedd
, même si vous spécifiez une grande taille de bloc.Pour copier uniquement une partie d'un fichier, vous pouvez diriger
tail
vershead
. Cela nécessite GNU coreutils ou une autre implémentation qui doithead -c
copier un nombre spécifié d'octets (tail -c
est dans POSIX maishead -c
ne l'est pas). Un benchmark rapide sur Linux montre que cela est plus lent quedd
, probablement à cause du tuyau.Le problème
dd
est qu'il n'est pas fiable: il peut copier des données partielles . Pour autant que je sache,dd
est-il sûr lors de la lecture et de l'écriture dans un fichier normal - voir Quand dd convient-il pour copier des données? (ou, quand sont read () et write () partial) - mais seulement tant qu'il n'est pas interrompu par un signal . Avec GNU coreutils, vous pouvez utiliser l'fullblock
indicateur, mais ce n'est pas portable.Un autre problème
dd
est qu'il peut être difficile de trouver un nombre de blocs qui fonctionne, car le nombre d'octets ignorés et le nombre d'octets transférés doivent être un multiple de la taille du bloc. Vous pouvez utiliser plusieurs appels pourdd
: un pour copier le premier bloc partiel, un pour copier la masse des blocs alignés et un pour copier le dernier bloc partiel - voir la réponse de Graeme pour un extrait de shell. Mais n'oubliez pas que lorsque vous exécutez le script, à moins que vous n'utilisiez lefullblock
drapeau, vous devez prier pourdd
copier toutes les données.dd
renvoie un état différent de zéro si une copie est partielle, il est donc facile de détecter l'erreur, mais il n'y a aucun moyen pratique de la réparer.POSIX n'a rien de mieux à offrir au niveau du shell. Mon conseil serait d'écrire un petit programme C à usage spécial (selon exactement ce que vous implémentez, vous pouvez l'appeler
dd_done_right
outail_head
oumini-busybox
).la source
yes | dd bs=1024k count=10 | wc
problème auparavant. Méchant.Avec
dd
:Alternativement avec
losetup
:Et puis
dd
,cat
... le périphérique de boucle.la source
Voici comment vous pouvez le faire:
C'est tout ce qui est vraiment nécessaire - cela ne demande pas beaucoup plus. En premier lieu
dd count=0 skip=1 bs=$block_size1
,lseek()
la saisie de fichiers régulière sur pratiquement instantanément. Il n'y a aucune chance de données manquées ou de tout autre mensonge qui soit dit à ce sujet, vous pouvez simplement rechercher directement la position de départ souhaitée. Étant donné que le descripteur de fichier appartient au shell etdd
que celui-ci ne fait que l'hériter, il affectera la position de son curseur et vous pourrez donc le suivre par étapes. C'est vraiment très simple - et il n'y a pas d'outil standard mieux adapté à la tâche quedd
.Cela utilise une taille de bloc de 64k, ce qui est souvent idéal. Contrairement à la croyance populaire, les blocs plus grands ne rendent pas le
dd
travail plus rapide. D'un autre côté, les petits tampons ne sont pas bons non plus.dd
doit synchroniser son temps dans les appels système afin qu'il n'ait pas besoin d'attendre la copie des données en mémoire et à nouveau, mais aussi pour qu'il n'ait pas besoin d'attendre les appels système. Vous voulez donc que cela prenne suffisamment de temps pour que le prochainread()
n'ait pas à attendre le dernier, mais pas tellement que vous tamponniez dans des tailles plus grandes que nécessaire.Le premier
dd
saute donc à la position de départ. Cela ne prend aucun temps. Vous pouvez appeler n'importe quel autre programme que vous aimiez à ce moment-là pour lire son stdin et il commencerait à lire directement à votre décalage d'octet souhaité. J'appelle un autredd
pour lire les((interval / blocksize) -1)
blocs de comptage sur stdout.La dernière chose qui est nécessaire est de copier le module (le cas échéant) de l'opération de division précédente. Et c'est ça.
Soit dit en passant, ne le croyez pas quand les gens disent des faits sur leur visage sans preuve. Oui, il est possible
dd
de faire une courte lecture (bien que de telles choses ne soient pas possibles lors de la lecture à partir d'un périphérique de bloc sain - d'où le nom) . De telles choses ne sont possibles que si vous ne mettez pas correctement en mémoire tampon undd
flux lu à partir d'un périphérique autre qu'un bloc. Par exemple:Dans les deux cas,
dd
copie toutes les données. Dans le premier cas, il est possible (bien que peu probable aveccat
) que certains des blocs de sortie qui copient soient desdd
octets "$ num" cardd
ils ne sont spécifiés que pour tamponner quoi que ce soit lorsque le tampon est spécifiquement demandé sur sa commande - ligne.bs=
représente une taille de bloc maximale car le but de ce moduledd
est les E / S en temps réel.Dans le deuxième exemple, je spécifie explicitement la taille de bloc de sortie et les
dd
lectures de tampons jusqu'à ce que des écritures complètes puissent être effectuées. Cela n'affecte pascount=
ce qui est basé sur les blocs d'entrée, mais pour cela, vous avez juste besoin d'un autredd
. Toute information erronée qui vous est donnée autrement doit être ignorée.la source