Auffüllen von Leerzeichen in einer Zeichenfolge mit einem anderen Zeichen

12

Ich möchte hello worldüber 20 Zeichen ausgeben .

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

# Actual output
hello world!!        :

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

Ich möchte jedoch nicht mit Leerzeichen abschließen, sondern mit " = ". Wie mache ich das?

smarber
quelle

Antworten:

9

Aktualisierte Antwort, um allgemeinere Lösung zu sein. siehe auch meine andere antwort unten mit nur shell brace expansion und pritnf.

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

Wie es funktioniert?

dies (=*):$/fängt ein Leerzeichen ein, eines oder mehrere =, gefolgt von einem Doppelpunkt :am Ende seiner Eingabe; Wir machen das Set =als Gruppen-Match und \1werden seine Rückverweisung sein.

Mit :loopdefinierten wir ein Label mit dem Namen loopund mit t loopihm zu diesem Label springen , wenn eine s/ (=*):$/\1=:/erfolgreiche Substitution getan hat;

Im Ersatzteil mit \1=:wird immer die Anzahl von =s erhöht und der Doppelpunkt selbst bis zum Ende der Zeichenfolge zurückgesetzt.

αғsнιη
quelle
1
Sie müssen also das Flag anpassen, je nachdem, wie viele Wörter die Eingabe enthält?
user1686
@grawity Ich habe meine Antwort auf die allgemeine Lösung jetzt aktualisiert.
αғsнιη
19
filler='===================='
string='foo'

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

Gibt

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

${#string}ist die Länge des Werts $stringund ${filler:${#string}}die Teilzeichenfolge $fillervon Offset ${#string}an.

Die Gesamtbreite der Ausgabe entspricht der maximalen Breite von $filleroder $string.

Der Füllstring kann auf Systemen, jotdie dynamisch erstellt wurden, mithilfe von erstellt werden

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

(für 16 =in einer Zeile). GNU-Systeme können verwenden seq:

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

Andere Systeme verwenden möglicherweise Perl oder eine andere schnellere Methode, um die Zeichenfolge dynamisch zu erstellen.

Kusalananda
quelle
Warum nicht mit printfdem Filter, der in fast allen Systemen verfügbar ist, und der Klammererweiterung mit den Schalen wie erzeugen bash/szh?
α 13sнιη
@sddgob Wie würdest du es mit printf+ geschweiften Erweiterungen machen bash?
Kusalananda
Bitte überprüfen Sie unix.stackexchange.com/a/399059/72456
αғsнιη
17
printf "%.20s:\n\n" "$str========================="

Wo %.20sist das Format für das Abschneiden von Zeichenfolgen?

Joao
quelle
2
Dies ist meiner Meinung nach eine bessere Lösung als meine. Es bedarf lediglich einer kurzen Erläuterung des Formatstrings.
Kusalananda
12

Ein Weg, es zu tun:

printf "====================:\r%s\n\n" 'hello world!!'
Satō Katsura
quelle
6
Ha! Das ist ein kluger Trick! Es wird jedoch gedruckt ====================\rhello world, was ein Problem sein kann, wenn das OP dies speichern und nicht nur auf dem Bildschirm drucken muss.
Terdon
auch echo -e '=================\rHello World!!', aber hat das gleiche Problem wie @terdon darauf hingewiesen.
αғsнιη
1
@ αғsнιη Nur wenn echounterstützt -e. printfist echoaus vielen Gründen fast immer besser als .
Satō Katsura
5

Ein Perl-Ansatz:

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

Oder besser gesagt, @SatoKatsura hat in den Kommentaren darauf hingewiesen:

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

Wenn Sie UTF-Multibyte-Zeichen unterstützen müssen, verwenden Sie:

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

Gleiche Idee in der Shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
terdon
quelle
Sie brauchen nicht eine Schleife: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Dieses (und alle anderen bisher veröffentlichten Lösungen) funktioniert jedoch nicht, wenn Mehrbyte-Zeichen beteiligt sind.
Satō Katsura
@ SatōKatsura ooh, ja, das ist ordentlich! Sollte daran gedacht haben, danke. Und ja, ich habe darüber nachgedacht, einen Haftungsausschluss für mögliche Fehler bei UTF-Multibyte-Zeichen einzufügen, dachte aber, dass dies in diesem Zusammenhang eine unnötige Komplikation sein würde.
Terdon
Ich denke, es perl6könnte eine Möglichkeit geben, dies auch mit Multibyte-Zeichen korrekt zu tun. Aber andererseits perl6ist es in vielerlei Hinsicht ärgerlich.
Satō Katsura
@ SatōKatsura na ja, für so eine einfache Sache sollte es genügen, einfach einzustellen PERL_UNICODE='AS'. Zum Beispiel: printf '%s' nóóös | perl -nle 'print length($_)'druckt 8 ( "falsch") , während printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'druckt 5 ( "richtigen").
terdon
5

Eine andere Möglichkeit besteht darin, nur den printfBefehl zu verwenden und das Zeichenauffüllmuster zu generieren, indem Shell Brace ExpansionSie zuerst (Sie können mit einer Zahl ≥ den zu druckenden Formatierungsbereich beenden{1..end} ) und nur jedes erste Zeichen davon abrufen, %.1sdas =s ist, und dann nur die ersten 20 Zeichen drucken Bereich davon %.20s. Dies ist eine bessere Möglichkeit, Zeichen / Wörter zu wiederholen, anstatt sie zu duplizieren.

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

Erklärungen:

Normalerweise als Klammererweiterung , Shellerweiterung {1..20}wie folgt, wenn wir diese drucken.

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

Wenn Sie ein Gleichheitszeichen hinzufügen ={1..20}, wird die Shell folgendermaßen erweitert.

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

Und printf '%.1s'womit ist eigentlich gemeint printf '%WIDE.LENGTH', wir drucken nur eine LÄNGE der oben genannten mit der Standardeinstellung 1 WIDE . so wird es =nur s geben und sich 20 mal wiederholen.

Jetzt printf '%.20s:\n'drucken wir nur die Länge 20 von $strund wenn die Länge $str<20 ist, wird der Rest von den generierten =s zum Füllen anstelle von Leerzeichen benötigt.

αғsнιη
quelle