Explicación necesaria sobre cómo puedo repetir un personaje en POSIX shell

8

La siguiente respuesta en Stack Overflow,

¿Cómo puedo repetir un personaje en bash?

impone una forma plausible de POSIX : repetir un solo carácter, de la siguiente manera. En este ejemplo, usemos el signo igual 100 veces:

printf %100s | tr " " "="

Mi problema es que no entiendo cómo funciona, y preferiría una explicación directa. Por favor, abstenerse de comentarios como leer el manual , lo hice, y dado que no soy inteligente, estoy haciendo esta pregunta como nunca he usado tr, ni he visto tal printfdeclaración.

LinuxSecurityFreak
fuente

Respuestas:

13

En resumen, printf %100simprimirá 100 espacios y tr " " "="los convertirá en signos iguales, imprimiendo efectivamente 100 signos iguales.

Desglosándolo:


printfEs un shell incorporado. Por lo general, se necesitan dos o más argumentos, el primero de los cuales es una "cadena de formato" y el resto se usará para completar los marcadores de posición en esa cadena de formato. Una vez que la plantilla esté completamente llena, imprimirá el resultado. Si quedan más argumentos, comenzará de nuevo, llenando más argumentos e imprimiendo la cadena resultante.

La cadena de formato utilizada para printftoma especificaciones de formato, que comienzan %y terminan con una sola letra, por lo que %dsignifica un número entero (usando la base decimal, por lo tanto, "d"), %fsignifica un número de coma flotante y %ssignifica una cadena de caracteres. Los caracteres que no son letras después de ellos %son modificadores para la especificación de formato y, en particular, los números se utilizan para especificar la longitud solicitada del campo en la salida. Entonces %100sformateará la cadena para que tenga al menos 100 caracteres, la rellenará con espacios y la mantendrá alineada a la derecha (en otras palabras, agregará espacios al comienzo de la cadena).

Si se pasa un argumento adicional, lo usaría para ese %scampo, por lo que , por ejemplo printf %100s abc, imprimirá 97 espacios (para llegar a 100 en total, considerando el 3 en "abc") seguido de la cadena real "abc". Pero si no se proporciona ningún argumento, la especificación de formato se rellena con un argumento vacío o nulo (que es una cadena vacía para %s, sería 0 para %d, etc.) Entonces, eso es lo mismo que si se pasara una cadena vacía, como printf %100s ''. El resultado final es que solo se imprime el relleno de 100 caracteres.

Entonces, al unirlo todo, se printf %100simprimen 100 espacios.


Ahora tres una herramienta para traducir caracteres de entrada a salida. Toma dos argumentos, SET1 y SET2, cada uno un conjunto de caracteres, y luego traduce el primer carácter de SET1 en el primero de SET2, el segundo carácter de SET1 en el segundo de SET2 y así sucesivamente. trlee su entrada de stdin y la vuelve a escribir en stdout (por lo que es muy útil en canalizaciones como la anterior). trSiempre traducirá todas las apariciones de ese carácter en una cadena dada.

Por ejemplo, tr aeiou 12345traducirá las vocales en minúsculas en los números del 1 al 5 en ese orden, por lo que traducirá "hacer cola" en "q52523ng", por ejemplo. También puede pasarle rangos de caracteres, como tr a-z A-Zconvertir cualquier letra minúscula en su correspondiente mayúscula.

Entonces, tr " " "="simplemente está traduciendo espacios en signos iguales a lo largo de la cadena. El primer espacio necesita ser citado para ser reconocido como un argumento. En =realidad, no es necesario citarlo, pero hacerlo no hace daño. tr " " =hubiera funcionado igual.


Poniendo todo junto, imprime 100 espacios, luego traduce cada uno de ellos en signos iguales.

Esperemos que esto lo explique con suficiente detalle, pero si todavía hay algo que no comprende, deje un comentario e intentaré solucionarlo.

filbranden
fuente
Solo verificando, ¿sería lo siguiente más sintácticamente correcto ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
2
@Vlastimil En realidad printf '%100s' '', con una cadena vacía ... Actualicé la respuesta para incluir eso. En este caso particular, una cadena vacía o un solo espacio no marcarían la diferencia, pero puede ver una diferencia en printf '%sx\n', que es igual printf '%sx\n' ''pero diferente de printf '%sx\n' ' '. ¡Espero que eso ayude!
filbranden
1
+1 por mencionar que tropera en conjuntos de personajes. Esto a menudo se deja fuera.
Sergiy Kolodyazhnyy
11

El printfcomando utiliza su primer argumento como formato para imprimir sus argumentos posteriores. printf %100simprime sus argumentos rellenados a 100 caracteres de ancho, usando espacios (a la izquierda). No se proporciona ningún argumento para formatear, por lo que formatea la cadena vacía una vez y genera 100 espacios. Puedes ver eso:

$ printf %100s | hexdump -C
00000000  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
*
00000064

(20 es el hex para un espacio; *significa la línea anterior repetida)

Las cadenas de formato usan aproximadamente los Xprintfespecificadores de C : %un ancho opcional para ajustar el valor formateado y un tipo de formato para usar. ses el formato de cadena, y las cadenas se rellenan con espacios a la izquierda de forma predeterminada. Podría haber múltiples formatos u otras partes literales: printf "a%10sb\n" helloimpresiones

 a         xb.

trreemplaza los caracteres seleccionados en su entrada estándar con reemplazos seleccionados e imprime el resultado en su salida estándar. tr " " "="tiene un solo carácter para ser reemplazado - un espacio - y un solo carácter para reemplazarlo - un signo igual. Por lo tanto, convierte cada espacio en su entrada en an =, y deja el resto sin cambios. Puedes probar eso también:

$ tr " " "="
hello world
hello=world

(Escribí el "hola mundo")

Podría tener múltiples reemplazos: tr abc defconvierte a en d, b en e, c en f, y deja el resto sin cambios. Aquí es solo un personaje, ya que eso era lo que printfpodía generar a bajo precio.

La tubería |hace que la salida del comando a la izquierda printf %100s,, se utilice como entrada al comando a la derecha tr " " "=",. Es decir, se asignan cien espacios consecutivos tr, y cada uno de ellos se reemplaza por un =, con la nueva cadena impresa.

printf %100s | tr " " "="
====================================================================================================
Michael Homer
fuente
Solo verificando, ¿sería lo siguiente más sintácticamente correcto ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
1
Formatear un espacio y rellenarlo con espacios dará el mismo resultado que formatear una cadena vacía y rellenar con espacios, pero no es estructuralmente equivalente. Quizás sea más "correcto" en términos de claridad sobre lo que está sucediendo, pero no sintácticamente, y son comandos diferentes.
Michael Homer