Erklärung erforderlich, wie ich ein Zeichen in der POSIX-Shell wiederholen kann

8

Die folgende Antwort zum Stapelüberlauf:

Wie kann ich ein Zeichen in Bash wiederholen?

legt eine plausible Möglichkeit fest, ein einzelnes Zeichen wie folgt durch POSIX zu wiederholen: In diesem Beispiel verwenden wir das Gleichheitszeichen 100 Mal:

printf %100s | tr " " "="

Mein Problem ist, dass ich nicht verstehe, wie es funktioniert, und ich würde eine einfache Erklärung vorziehen. Bitte unterlassen Sie Kommentare wie das Lesen des Handbuchs . Ich habe dies getan, und da ich nicht schlau darin bin, stelle ich diese Frage, da ich sie noch nie benutzt troder gesehen habe printf.

LinuxSecurityFreak
quelle

Antworten:

13

Kurz gesagt, printf %100sdruckt 100 Leerzeichen und tr " " "="konvertiert diese Leerzeichen in Gleichheitszeichen, wodurch effektiv 100 Gleichheitszeichen gedruckt werden.

Brechen sie ab:


printfist eine eingebaute Shell. Normalerweise werden zwei oder mehr Argumente benötigt, von denen das erste eine "Formatzeichenfolge" ist und der Rest verwendet wird, um Platzhalter in dieser Formatzeichenfolge auszufüllen. Sobald diese Vorlage vollständig ausgefüllt ist, wird das Ergebnis ausgedruckt. Wenn noch mehr Argumente übrig sind, wird von vorne begonnen, mehr Argumente aufgefüllt und die resultierende Zeichenfolge gedruckt.

Die für verwendete Formatzeichenfolge printfnimmt Formatspezifikationen an, die %mit einem einzelnen Buchstaben beginnen und enden. Dies %dbedeutet eine Ganzzahl (unter Verwendung der Dezimalbasis, daher "d"), %feine Gleitkommazahl und %seine Zeichenfolge. Andere Zeichen als Buchstaben nach %sind Modifikatoren für die Formatspezifikation und insbesondere Zahlen, um die angeforderte Länge des Feldes bei der Ausgabe anzugeben. So %100swird die String - Format mindestens 100 Zeichen zu haben, wird es Pad es mit Leerzeichen und es wird halten sie rechts ausgerichtet (in anderen Worten, fügen Sie Leerzeichen am Anfang des Strings.)

Wenn ein zusätzliches Argument übergeben wird, wird es für dieses %sFeld verwendet, sodass beispielsweise printf %100s abc97 Leerzeichen gedruckt werden (um insgesamt 100 zu erhalten, unter Berücksichtigung der 3 in "abc"), gefolgt von der tatsächlichen Zeichenfolge "abc". Wenn jedoch kein Argument angegeben wird, wird die Formatspezifikation mit einem leeren oder Null-Argument gefüllt (was eine leere Zeichenfolge ist, für %sdie es 0 wäre %dusw.). Das ist also dasselbe, als ob eine leere Zeichenfolge übergeben wurde, wie z printf %100s ''. Das Endergebnis ist, dass nur die 100-Zeichen-Auffüllung gedruckt wird.

Wenn Sie also alles zusammenfügen, werden printf %100s100 Leerzeichen gedruckt.


Jetzt trist ein Werkzeug zum Übersetzen von Zeichen von Eingabe zu Ausgabe. Es werden zwei Argumente verwendet, SET1 und SET2, jeweils ein Satz von Zeichen, und dann wird das erste Zeichen von SET1 in das erste von SET2, das zweite Zeichen von SET1 in das zweite von SET2 usw. übersetzt. trLiest seine Eingabe von stdin und schreibt sie zurück in stdout (daher ist sie in Pipelines wie der obigen sehr nützlich.) trübersetzt immer alle Vorkommen dieses Zeichens in eine bestimmte Zeichenfolge.

Zum Beispiel tr aeiou 12345werden Kleinbuchstaben in dieser Reihenfolge in die Zahlen 1 bis 5 übersetzt, sodass beispielsweise "Warteschlangen" in "q52523ng" übersetzt werden. Sie können ihm auch Zeichenbereiche übergeben, z. B. tr a-z A-Zum einen Kleinbuchstaben in den entsprechenden Großbuchstaben umzuwandeln.

tr " " "="Übersetzen Sie also einfach Leerzeichen in Gleichheitszeichen in der gesamten Zeichenfolge. Das erste Leerzeichen muss in Anführungszeichen gesetzt werden, um als Argument erkannt zu werden. Die =nicht wirklich brauchen , um zitiert werden, aber dadurch nicht so nicht schaden. tr " " =hätte genauso funktioniert.


Wenn Sie alles zusammenfügen, drucken Sie 100 Leerzeichen und übersetzen Sie jedes in Gleichheitszeichen.

Hoffentlich erklärt dies dies ausführlich genug, aber wenn Sie noch etwas nicht verstehen, hinterlassen Sie bitte einen Kommentar, und ich werde versuchen, dies zu beheben.

filbranden
quelle
Nur überprüfen, wäre das Folgende syntaktisch korrekter?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
2
@Vlastimil Eigentlich printf '%100s' ''mit einer leeren Zeichenfolge ... Ich habe die Antwort aktualisiert, um das einzuschließen . In diesem speziellen Fall würde eine leere Zeichenfolge oder ein einzelnes Leerzeichen keinen Unterschied machen, aber Sie können einen Unterschied in sehen printf '%sx\n', der derselbe ist, sich printf '%sx\n' ''jedoch von dem unterscheidet printf '%sx\n' ' '. Ich hoffe das hilft!
Filbranden
1
+1 für Erwähnung, trdie auf Zeichensätzen arbeitet. Dies wird oft ausgelassen.
Sergiy Kolodyazhnyy
11

Der printfBefehl verwendet sein erstes Argument als Format zum Drucken seiner nachfolgenden Argumente. printf %100sdruckt die mit 100 Zeichen Breite aufgefüllten Argumente mit Leerzeichen (links) aus. Es gibt kein Argument zum Formatieren, daher wird die leere Zeichenfolge einmal formatiert und es werden 100 Leerzeichen ausgegeben. Sie können sehen, dass:

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

(20 ist das Hex für ein Leerzeichen; *bedeutet, dass die vorherige Zeile wiederholt wird)

Die Formatzeichenfolgen verwenden ungefähr die C- XprintfBezeichner : %, eine optionale Breite zum Anpassen des formatierten Werts und einen zu verwendenden Formattyp. sist die Zeichenfolgenformatierung, und Zeichenfolgen werden standardmäßig links mit Leerzeichen aufgefüllt. Es kann mehrere Formate oder andere wörtliche Teile geben: printf "a%10sb\n" helloDrucke

 a         xb.

trErsetzt ausgewählte Zeichen in der Standardeingabe durch ausgewählte Ersetzungen und druckt das Ergebnis in die Standardausgabe. tr " " "="hat ein einzelnes Zeichen, das ersetzt werden soll - ein Leerzeichen - und ein einzelnes Zeichen, durch das es ersetzt werden soll - ein Gleichheitszeichen. Es verwandelt somit jeden Raum in seiner Eingabe in einen =und lässt den Rest unverändert. Sie können das auch versuchen:

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

(Ich habe die "Hallo Welt" getippt)

Sie können mehrere Ersetzungen vornehmen: tr abc defVerwandelt a in d, b in e, c in f und lässt den Rest unverändert. Hier ist es nur ein einziger Charakter, da printfdies billig generiert werden konnte.

Die Pipe |bewirkt, dass die Ausgabe des Befehls auf der linken Seite printf %100sals Eingabe für den Befehl auf der rechten Seite verwendet wird tr " " "=". Das heißt, es werden hundert aufeinanderfolgende Leerzeichen vergeben tr, und jedes von ihnen wird durch ein =neues ersetzt , wobei die neue Zeichenfolge ausgedruckt wird.

printf %100s | tr " " "="
====================================================================================================
Michael Homer
quelle
Nur überprüfen, wäre das Folgende syntaktisch korrekter?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
1
Das Formatieren und Auffüllen eines Leerzeichens mit Leerzeichen ergibt die gleiche Ausgabe wie das Formatieren einer leeren Zeichenfolge und das Auffüllen mit Leerzeichen, ist jedoch strukturell nicht äquivalent. Vielleicht ist es "korrekter" in Bezug auf Klarheit darüber, was vor sich geht, aber nicht syntaktisch, und es handelt sich um verschiedene Befehle.
Michael Homer