Escribir un carácter N veces usando el comando printf

12

Encontré el siguiente comando para repetir un personaje en Linux:

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

Quiero, por ejemplo, Hrepetir 5000tiempos. ¿Qué %.0ssignifica aquí?

estrella
fuente
Con tcsho zsh, repeat 5000 printf Hes más fácil de entender. Con perl: print "H" x 5000(nota que esto {1..5000}es un operador zsh inspirado en perl's 1..5000uno y más tarde copiado por ksh93 y bash)
Stéphane Chazelas
sí, funciona, pero utiliza muchos recursos para repetir más, siga las sugerencias de Stéphane Chazelas
Skaperen
1
haría este comandoyes 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

Respuestas:

20

Ese comando depende de que el shell genere 5000 argumentos y se los pase y printfluego los ignore. Si bien puede parecer bastante rápido, y es relativo a algunas cosas, el shell aún debe generar todas esas cadenas como args (y delimitarlas), etc.

Además del hecho de que las Hs generadas no pueden imprimirse hasta que el shell se itera por primera vez a 5000, ese comando también cuesta en la memoria todo lo que se necesita para almacenar y delimitar los argumentos de cadena numérica printf más las Hs. Así de simple puedes hacer:

printf %05000s|tr \  H

... que genera una cadena de 5000 espacios, que, al menos, suelen ser de un solo byte y no cuesta nada delimitarlos porque no están delimitados. Algunas pruebas indican que incluso por tan solo 5000 bytes, el costo de la horquilla y la tubería requerida trvale la pena incluso en este caso, y casi siempre es cuando los números aumentan.

Corrí...

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

...y...

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

Cada una de aproximadamente 5 veces por pieza (nada científico aquí, solo anecdótico) y la versión de expansión del aparato ortopédico promedió un poco más de .02 segundos en el tiempo total de procesamiento, pero la trversión llegó a alrededor de .012 segundos en promedio en promedio, y la trversión la superó cada vez. No puedo decir que me sorprenda, {brace expansion}es una característica útil de la taquigrafía de la shell interactiva, pero generalmente es algo bastante inútil para cualquier tipo de scripting. La forma común:

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

... cuando lo piensas, en realidad son dos for bucles: el primero es interno e implica que el shell debe realizar un bucle de alguna manera para generar esos iteradores antes de guardarlos e iterarlos nuevamente para tu forbucle. Tales cosas generalmente se hacen mejor como:

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

... porque almacena solo unos pocos valores y los sobrescribe a medida que avanza, así como realiza la iteración mientras genera los iterables.

De todos modos, al igual que el relleno de espacio mencionado anteriormente, también puede usar printfpara poner a cero un número arbitrario de dígitos, por supuesto, como:

printf %05000d

Hago ambas cosas sin argumentos porque para cada argumento especificado en printfla cadena de formato cuando no se encuentra un argumento, se usa la cadena nula, que se interpreta como un cero para un argumento de dígitos o una cadena vacía para una cadena.

Este es el otro lado (y, en mi opinión, más eficiente) de la moneda en comparación con el comando en la pregunta, mientras que es posible no obtener nada de algo como lo hace cuando printf %.0alarga las cadenas para cada argumento, también lo es Es posible obtener algo de la nada.

Aún más rápido para grandes cantidades de bytes generados que puede usar ddcomo:

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

... y w / archivos normales dd's seek=[num]argumento pueden ser utilizados en forma más adecuada. Puede obtener 64k nuevas líneas en lugar de nulos si agrega ,unblock cbs=1a lo anterior y desde allí podría inyectar cadenas arbitrarias por línea con pastey /dev/null, pero en ese caso, si está disponible para usted, también podría usar:

yes 'output string forever'

Aquí hay algunos ddejemplos más de todos modos:

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

... que crea (o trunca) un \0NULarchivo lleno en el directorio actual llamado H.txt de tamaño 5000 bytes. ddbusca directamente al desplazamiento y NUL-llena todo detrás de él.

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

... que crea un archivo del mismo nombre y tamaño pero lleno de caracteres w / H. Aprovecha ddel comportamiento específico de escribir al menos un bloque nulo completo en caso de un error de lectura cuando se especifican noerrory las syncconversiones (y, sin count=, probablemente durarían más de lo que podría desear) , y redirige intencionalmente un descriptor de archivo de solo escritura en ddla entrada estándar.

mikeserv
fuente
8

Los %.0smedios para convertir el argumento como una cadena , con una precisión de cero. Según man 3 printf, el valor de precisión en tal caso da

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

por lo tanto, cuando la precisión es cero, el argumento de cadena no se imprime en absoluto. Sin embargo, el H(que es parte del especificador de formato) se imprime tantas veces como haya argumentos, ya que de acuerdo con la printfsección 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. 
conductor de acero
fuente
7

En este caso, %.0ssiempre imprime una instancia de los caracteres que la preceden, H en este caso. Cuando usa {1..5000}, el shell lo expande y se convierte en:

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

es decir, el comando printf ahora tiene 5000 argumentos, y para cada argumento, obtendrá un H. Estos no tienen que ser secuenciales o numéricos:

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

imprime HHHHH, es decir, el número de argumentos, 5 en este caso.

Tenga en cuenta que las elipses en el primer ejemplo anterior no se insertan literalmente, están allí para indicar una secuencia o rango.

KM.
fuente