Écriture d'un caractère N fois à l'aide de la commande printf

12

J'ai trouvé la commande suivante pour répéter un caractère sous Linux:

printf 'H%.0s' {1..5000} > H.txt

Je veux, par exemple, Hrépéter les 5000temps. Que veut %.0sdire ici?

étoile
la source
Avec tcshou zsh, repeat 5000 printf Hest plus facile à comprendre. Avec perl: print "H" x 5000(notez que {1..5000}c'est un opérateur zsh inspiré par perll » 1..5000un, puis copié par ksh93 et bash)
Stéphane Chazelas
oui cela fonctionne, mais utilise beaucoup de ressources pour une plus grande répétition, suivez les suggestions de Stéphane Chazelas
Skaperen
1
je ferais cette commandeyes H|head -5000|tr -d '\012'
Skaperen
dd if=/dev/zero bs=5000 count=1 | tr '\0' H
kojiro
@Skaepren:yes H| head -n 2500| tr \\n H
mikeserv

Réponses:

20

Cette commande dépend du shell qui génère 5000 arguments et les transmet à printfqui les ignore ensuite. Bien que cela puisse sembler assez rapide - et est relatif à certaines choses - le shell doit toujours générer toutes ces chaînes en tant qu'args (et les délimiter) et ainsi de suite.

Outre le fait que les H générés ne peuvent pas être imprimés avant que le shell n'itère pour la première fois à 5000, cette commande coûte également en mémoire tout ce qu'il faut pour stocker et délimiter les arguments de chaîne numérique en printf plus des H. Vous pouvez tout aussi simplement faire:

printf %05000s|tr \  H

... qui génère une chaîne de 5000 espaces - qui, au moins, ne sont généralement qu'un seul octet par et ne coûtent rien à délimiter car ils ne sont pas délimités. Quelques tests indiquent que même pour aussi peu que 5000 octets, le coût de la fourche et du tuyau requis en trvaut la peine, même dans ce cas, et c'est presque toujours lorsque les chiffres augmentent.

J'ai couru...

time bash -c 'printf H%.0s {1..5000}' >/dev/null

...et...

time bash -c 'printf %05000s|tr \  H' >/dev/null

Chaque fois environ 5 fois une pièce (rien de scientifique ici - seulement anecdotique) et la version d'extension de l'accolade en moyenne un peu plus de 0,02 seconde de temps de traitement total, mais la trversion est arrivée à environ 0,012 seconde au total en moyenne - et la trversion l' a battu à chaque fois. Je ne peux pas dire que je suis surpris - {brace expansion}est une fonctionnalité raccourcie interactive du shell, mais c'est généralement une chose plutôt inutile à faire pour tout type de script. La forme commune:

for i in {[num]..[num]}; do ...

... quand vous y pensez, il y a vraiment deux for boucles - la première est interne et implique que le shell doit boucler d'une manière ou d'une autre pour générer ces itérateurs avant de les enregistrer tous et de les réitérer pour votre forboucle. De telles choses sont généralement mieux faites comme:

iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...

... car vous ne stockez que très peu de valeurs et les écrasez au fur et à mesure ainsi que l'itération pendant que vous générez les itérables.

Quoi qu'il en soit, comme le remplissage d'espace mentionné précédemment, vous pouvez également utiliser printfpour zéros un nombre arbitraire de chiffres, bien sûr, comme:

printf %05000d

Je fais les deux sans arguments parce que pour chaque argument spécifié dans printfla chaîne de format de quand un argument n'est pas trouvé, la chaîne nulle est utilisée - ce qui est interprété comme un zéro pour un argument numérique ou une chaîne vide pour une chaîne.

C'est l'autre côté (et - à mon avis - plus efficace) de la pièce par rapport à la commande dans la question - alors qu'il est possible de ne rien obtenir de quelque chose comme vous le faites lorsque vous printf %.0longueurz des chaînes pour chaque argument, il en va de même pour il est possible de tirer quelque chose de rien.

Plus rapide encore pour de grandes quantités d'octets générés que vous pouvez utiliser ddcomme:

printf \\0| dd bs=64k conv=sync 

... et w / fichiers réguliers de ddl » seek=[num]argument peut être utilisé pour plus grand avantage. Vous pouvez obtenir 64 000 sauts de ligne plutôt que des valeurs nulles si vous ajoutez ,unblock cbs=1à ce qui précède et à partir de là, vous pouvez injecter des chaînes arbitraires par ligne avec pasteet /dev/null- mais dans ce cas, s'il est disponible, vous pouvez aussi utiliser:

yes 'output string forever'

Voici ddquand même quelques exemples:

dd bs=5000 seek=1 if=/dev/null of=./H.txt

... qui crée (ou tronque) un \0NULfichier rempli dans le répertoire courant nommé H.txt de taille 5000 octets. ddrecherche directement le décalage et remplit NUL tout derrière.

<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt

... qui crée un fichier du même nom et de la même taille mais rempli de caractères H / H. Il tire parti du ddcomportement spécifié par l'écriture d'au moins un bloc nul complet en cas d'erreur de lecture lorsque noerroret les syncconversions sont spécifiées (et - sans count=- continuerait probablement plus longtemps que vous ne le souhaiteriez) , et redirige intentionnellement un descripteur de fichier en écriture seule sur ddstdin.

mikeserv
la source
8

Le %.0smoyen de convertir l'argument sous forme de chaîne , avec une précision de zéro. Selon man 3 printf, la valeur de précision dans un tel cas donne

   [ ... ] the  maximum  number  of characters to be printed from a
   string for s and S conversions.

par conséquent, lorsque la précision est nulle, l' argument chaîne n'est pas imprimé du tout. Cependant, le H(qui fait partie du spécificateur de format) est imprimé autant de fois qu'il y a d'arguments, car selon la printfsection deman bash

The format is reused as necessary to consume all  of  the  argu
ments.  If the format requires more arguments than are supplied,
the extra format specifications behave as if  a  zero  value  or
null  string,  as  appropriate,  had  been supplied. 
tournevis
la source
7

Dans ce cas, %.0simprime toujours une instance de caractère (s) qui le précède, H dans ce cas. Lorsque vous utilisez {1..5000}, le shell le développe et il devient:

printf 'H%.0s' 1 2 3 4 ... 5000 > H.txt

c'est-à-dire que la commande printf a maintenant 5000 arguments, et pour chaque argument, vous obtiendrez un H. Ceux-ci ne doivent pas être séquentiels ou numériques:

printf 'H%.0s' a bc fg 12 34

imprime HHHHH- c'est-à-dire le nombre d'arguments, 5 dans ce cas.

Notez que les ellipses dans le 1er exemple ci-dessus ne sont pas insérées littéralement, elles sont là pour indiquer une séquence ou une plage.

KM.
la source