Relleno de espacios en blanco al final de una cadena con otro carácter

12

Me gustaría generar hello worldmás de 20 caracteres.

printf "%-20s :\n\n" 'hello world!!'

# Actual output
hello world!!        :

# Wanted output
hello world!!========:

Sin embargo, no quiero completar con espacios sino con " = " en su lugar. ¿Cómo puedo hacer eso?

smarber
fuente

Respuestas:

9

Respuesta actualizada para ser una solución más general. vea también mi otra respuesta a continuación usando solo expansión de llave de concha y pritnf.

$ str='Hello World!'
$ sed -r ':loop; s/ (=*):$/\1=:/; t loop' <<< "$(printf '%-20s:\n' "$str" )"
Hello World!========:

¿Cómo funciona?

esto (=*):$/captura un espacio, uno o más =que seguido de dos puntos :al final de su entrada; hacemos que el conjunto =sea ​​una coincidencia grupal y \1será su referencia de referencia.

Con :loopdefinimos una etiqueta llamada loopy con t loopella saltaremos a esa etiqueta cuando a s/ (=*):$/\1=:/haya realizado una sustitución exitosa;

En la parte de reemplazo con \1=:, siempre incrementará el número de =sy retrocederá los dos puntos hasta el final de la cadena.

αғsнιη
fuente
1
¿Entonces tiene que ajustar la bandera según cuántas palabras tenga la entrada?
usuario1686
@grawity He actualizado mi respuesta a la solución general ahora.
αғsнιη
20
filler='===================='
string='foo'

printf '%s\n' "$string${filler:${#string}}"

Da

foo=================

${#string}es la longitud del valor $stringy ${filler:${#string}}es la subcadena de $fillerdesde el desplazamiento ${#string}hacia adelante.

El ancho total de la salida será el ancho máximo de $fillero $string.

La cadena de relleno se jotpuede crear dinámicamente en sistemas que sí

filler=$( jot -s '' -c 16 '=' '=' )

(para 16 =en una línea). Los sistemas GNU pueden usar seq:

filler=$( seq -s '=' 1 16 | tr -dc '=' )

Otros sistemas pueden usar Perl o alguna otra forma más rápida de crear la cadena dinámicamente.

Kusalananda
fuente
¿Por qué no usar printfpara generar el filtro que está casi disponible en todos los sistemas y la expansión de la llave con los cascos bash/szh?
αғsнιη
@sddgob ¿Cómo lo harías con printf+ expansión de llaves bash?
Kusalananda
17
printf "%.20s:\n\n" "$str========================="

donde %.20sestá el formato de truncamiento de cadena

JJoao
fuente
2
Esta es mi humilde opinión, una mejor solución que la mía. Solo necesita una breve explicación de la cadena de formato.
Kusalananda
12

Una forma de hacerlo:

printf "====================:\r%s\n\n" 'hello world!!'
Satō Katsura
fuente
66
¡Decir ah! Ese es un truco inteligente! Sin embargo, en realidad se imprimirá ====================\rhello world, lo que podría ser un problema si el OP necesita almacenar esto y no solo imprimirlo en la pantalla.
terdon
también echo -e '=================\rHello World!!', pero tiene el mismo problema que @terdon señaló que.
αғsнιη
1
@ αғsнιη Solo si es echocompatible -e. printfCasi siempre es mejor que echo, por muchas razones.
Satō Katsura
5

Un enfoque de Perl:

$ perl -le '$k="hello world!!"; while(length($k)<20){$k.="=";} print "$k\n"'
hello world!!=======

O, mejor, @SatoKatsura señaló en los comentarios:

perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Si necesita admitir caracteres UTF de varios bytes, use:

PERL_UNICODE='AS' perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

La misma idea en el shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
terdon
fuente
No es necesario un bucle: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Sin embargo, esta (y todas las otras soluciones publicadas hasta el momento) se rompe si hay caracteres de varios bytes involucrados.
Satō Katsura
@ SatōKatsura ooh, sí, eso es genial! Debería haber pensado en eso, gracias. Y sí, estaba pensando en agregar un descargo de responsabilidad por posible falla en los caracteres de múltiples bytes UTF, pero pensé que sería una complicación innecesaria en este contexto.
terdon
Creo que perl6podría tener una manera de hacerlo correctamente incluso con caracteres de varios bytes. Pero por otro lado perl6es molesto de muchas maneras.
Satō Katsura
@ SatōKatsura bien, para este tipo de cosa simple, debería ser suficiente simplemente configurar PERL_UNICODE='AS'. Por ejemplo: printf '%s' nóóös | perl -nle 'print length($_)'imprime 8 ("incorrecto") mientras printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'imprime 5 ("correcto").
terdon
5

Otra forma es usar solo el printfcomando y generar el patrón de relleno de caracteres primero Shell Brace Expansion(puede poner el final con un área de formato de número ≥ en la que desea imprimir {1..end}) y obtener solo cada primer carácter %.1sque sea =sy luego imprimir solo los primeros 20 caracteres de longitud área de eso %.20s. Esta es una forma mejor de repetir caracteres / palabras en lugar de duplicarlos.

printf '%.20s:\n' "$str$(printf '%.1s' ={1..20})"
Hello World!!=======:

Explicaciones:

Normalmente, como Brace Expansion , Shell se expande de la {1..20}siguiente manera si imprimimos esos.

printf '%s ' {1..20}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

Entonces, al agregarle un signo igual ={1..20}, Shell se expandirá de la siguiente manera.

printf '%s ' ={1..20}
=1 =2 =3 =4 =5 =6 =7 =8 =9 =10 =11 =12 =13 =14 =15 =16 =17 =18 =19 =20 

Y con lo printf '%.1s'que realmente significa printf '%WIDE.LENGTH', estamos imprimiendo solo una LONGITUD de las anteriores con 1 ANCHO predeterminado . así resultará =s solamente y se repetirá 20 veces.

Ahora con printf '%.20s:\n'estamos imprimiendo solo la longitud de 20 $stry si la longitud de $str<20, el resto tomará de =s generados para rellenar en lugar de espacios.

αғsнιη
fuente