Remplissage des espaces blancs de fin dans une chaîne avec un autre caractère

12

J'aimerais afficher hello worldplus de 20 caractères.

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

# Actual output
hello world!!        :

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

Cependant, je ne veux pas compléter avec des espaces mais avec " = " à la place. Comment je fais ça?

smarber
la source

Réponses:

9

Réponse mise à jour pour être une solution plus générale. voir aussi ma autre réponse ci-dessous en utilisant uniquement l'expansion de l'accolade shell et pritnf.

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

Comment ça fonctionne?

cela (=*):$/capture un espace, un ou plusieurs =qui a suivi par deux points :à la fin de son entrée; nous faisons l'ensemble de =comme match de groupe et \1serons sa référence arrière.

Avec, :loopnous avons défini une étiquette nommée loopet avec t loopelle, nous passerons à cette étiquette lorsque a s/ (=*):$/\1=:/a réussi la substitution;

En pièce de rechange avec \1=:, il incrémentera toujours le nombre de =s et remettra les deux points à la fin de la chaîne.

αғsнιη
la source
1
vous devez donc ajuster le drapeau en fonction du nombre de mots de l'entrée?
user1686
@grawity J'ai mis à jour ma réponse à la solution générale maintenant.
αғsнιη
20
filler='===================='
string='foo'

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

Donne

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

${#string}est la longueur de la valeur $stringet ${filler:${#string}}est la sous-chaîne de à $fillerpartir du décalage ${#string}.

La largeur totale de la sortie sera celle de la largeur maximale de $fillerou $string.

La chaîne de remplissage peut, sur les systèmes existants jot, être créée dynamiquement à l'aide de

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

(pour 16 =en ligne). Les systèmes GNU peuvent utiliser seq:

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

D'autres systèmes peuvent utiliser Perl ou un autre moyen plus rapide de créer dynamiquement la chaîne.

Kusalananda
la source
pourquoi ne pas utiliser printfpour générer le filtre qui est presque disponible dans tous les systèmes et l'expansion de l'entretoise avec les coques comme bash/szh?
αғsнιη
@sddgob Comment feriez-vous avec l' printfextension + brace en bash?
Kusalananda
veuillez consulter unix.stackexchange.com/a/399059/72456
αғsнιη
17
printf "%.20s:\n\n" "$str========================="

%.20sest le format de chaîne tronquée

JJoao
la source
2
Ceci est à mon humble avis, une meilleure solution que la mienne. Il n'a besoin que d'une courte explication de la chaîne de format.
Kusalananda
12

Une façon de le faire:

printf "====================:\r%s\n\n" 'hello world!!'
Satō Katsura
la source
6
Ha! Voilà une astuce astucieuse! Cependant, il s'imprimera réellement ====================\rhello world, ce qui pourrait être un problème si l'OP doit le stocker et pas seulement l'imprimer à l'écran.
terdon
aussi echo -e '=================\rHello World!!', mais a le même problème que @terdon l'a souligné.
αғsнιη
1
@ αғsнιη Uniquement si les echosupports -e. printfest presque toujours mieux que echo, pour de nombreuses raisons.
Satō Katsura
5

Une approche Perl:

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

Ou mieux, @SatoKatsura a souligné dans les commentaires:

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

Si vous devez prendre en charge les caractères multi-octets UTF, utilisez:

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

Même idée dans le shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
terdon
la source
Vous n'avez pas besoin d' une boucle: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Cependant, cela (et toutes les autres solutions publiées jusqu'à présent) s'arrête si des caractères multi-octets sont impliqués.
Satō Katsura
@ SatōKatsura ooh, oui, c'est bien! J'aurais dû y penser, merci. Et oui, je pensais ajouter un avertissement pour une éventuelle panne sur les caractères multi-octets UTF, mais je pensais que ce serait une complication inutile dans ce contexte.
terdon
Je pense que perl6pourrait avoir un moyen de le faire correctement, même avec des caractères multi-octets. Mais d'un autre côté, perl6c'est ennuyeux à bien des égards.
Satō Katsura
@ SatōKatsura bien, pour ce genre de chose simple, cela devrait suffire à régler PERL_UNICODE='AS'. Par exemple: printf '%s' nóóös | perl -nle 'print length($_)'imprime 8 ("faux") tandis que printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'imprime 5 ("correct").
terdon
5

Une autre façon consiste à utiliser uniquement la printfcommande et à générer d'abord le motif de remplissage des caractères par Shell Brace Expansion(vous pouvez mettre fin avec un nombre ≥ zone de formatage dans laquelle vous souhaitez imprimer {1..end}) et à n'en obtenir que chaque premier caractère, %.1squi est =s, puis à n'imprimer que les 20 premiers caractères domaine de cela %.20s. C'est en quelque sorte une meilleure façon d'avoir des caractères / mots répétés au lieu de les dupliquer.

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

Explications:

Normalement, en tant qu'expansion Brace , le shell se développe {1..20}comme suit si nous les imprimons.

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

Ainsi, en y ajoutant un signe égal ={1..20}, le shell se développera comme suit.

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

Et printf '%.1s'ce qui signifie en fait printf '%WIDE.LENGTH', nous n'imprimons qu'une seule LONGUEUR de ceux ci-dessus avec 1 WIDE par défaut . il en résultera =seulement s et 20 fois répété.

Maintenant, avec printf '%.20s:\n'nous n'imprimons que la longueur de 20 $stret si la longueur est $str<20, le reste prendra des =s générés à remplir au lieu d'espaces.

αғsнιη
la source