Schreiben Sie ein Zeichen N-mal mit dem Befehl printf

12

Ich habe den folgenden Befehl gefunden, um ein Zeichen unter Linux zu wiederholen:

printf 'H%.0s' {1..5000} > H.txt

Ich möchte zum Beispiel mal Hwiederholen 5000. Was %.0sbedeutet hier?

Star
quelle
Mit tcshoder zsh, repeat 5000 printf Hist leichter zu verstehen. Mit perl: print "H" x 5000(beachten Sie, dass das {1..5000}ein zsh Operator inspiriert perl‚s 1..5000ein und später von ksh93 und bash kopiert)
Stéphane Chazelas
Ja, es funktioniert, aber es werden viele Ressourcen für größere Wiederholungen verwendet.
Folgen Sie
1
Ich würde diesen Befehl tunyes H|head -5000|tr -d '\012'
Skaperen
dd if=/dev/zero bs=5000 count=1 | tr '\0' H
Kojiro
@Skaepren:yes H| head -n 2500| tr \\n H
Mikeserv

Antworten:

20

Dieser Befehl hängt davon ab, dass die Shell 5000 Argumente generiert und diese übergibt, printfdie dann ignoriert werden. Während es ziemlich schnell zu sein scheint - und relativ zu einigen Dingen ist - muss die Shell dennoch alle diese Zeichenfolgen als Argumente generieren (und sie abgrenzen) und so weiter.

Neben der Tatsache, dass die generierten Hs erst gedruckt werden können, wenn die Shell zum ersten Mal auf 5000 iteriert, kostet dieser Befehl im Speicher auch alles, was zum Speichern und Abgrenzen der numerischen Zeichenfolgenargumente printf plus Hs erforderlich ist . Genauso einfach können Sie Folgendes tun:

printf %05000s|tr \  H

... was eine Zeichenfolge von 5000 Leerzeichen erzeugt - die normalerweise nur ein einzelnes Byte pro sind und deren Abgrenzung nichts kostet, weil sie nicht abgegrenzt sind. Einige Tests zeigen, dass sich die Kosten für die Gabel und das Rohr, die für trnur 5000 Bytes erforderlich sind, auch in diesem Fall lohnen, und dies fast immer dann, wenn die Zahlen höher werden.

Ich rannte...

time bash -c 'printf H%.0s {1..5000}' >/dev/null

...und...

time bash -c 'printf %05000s|tr \  H' >/dev/null

Jeweils etwa fünfmal pro Stück (hier nichts Wissenschaftliches - nur anekdotisch) und die Klammererweiterungsversion hatten eine durchschnittliche Gesamtverarbeitungszeit von etwas mehr als 0,02 Sekunden, aber die trVersion erreichte durchschnittlich etwa 0,012 Sekunden - und die trVersion übertraf sie jedes Mal. Ich kann nicht sagen, dass ich überrascht bin - es {brace expansion}ist eine nützliche interaktive Shell-Kurzform, aber normalerweise eine ziemlich verschwenderische Sache, wenn es um Skripte geht. Die übliche Form:

for i in {[num]..[num]}; do ...

... wenn Sie darüber nachdenken, sind es wirklich zwei for Schleifen - die erste ist intern und impliziert, dass die Shell eine Schleife ausführen muss, um diese Iteratoren zu generieren, bevor sie alle gespeichert und für Ihre forSchleife erneut iteriert werden . Solche Dinge werden normalerweise besser gemacht als:

iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...

... weil Sie nur sehr wenige Werte speichern und diese im Laufe der Zeit überschreiben und die Iteration ausführen, während Sie die Iterables generieren.

Wie bei der zuvor erwähnten Leerzeichenauffüllung können Sie printfnatürlich auch eine beliebige Anzahl von Ziffern auf Null setzen , z. B.:

printf %05000d

Ich mache beides ohne Argumente, weil für jedes Argument, das in der Formatzeichenfolge angegeben ist printf, wenn ein Argument nicht gefunden wird, die Nullzeichenfolge verwendet wird - die als Null für ein Ziffernargument oder als leere Zeichenfolge für eine Zeichenfolge interpretiert wird.

Dies ist die andere (und meiner Meinung nach effizientere) Seite der Medaille im Vergleich zu dem Befehl in der Frage - während es möglich ist, nichts von etwas zu bekommen, wie Sie es tun, wenn Sie printf %.0Zeichenfolgen für jedes Argument verlängern, so ist es auch es ist möglich, etwas aus dem Nichts zu bekommen.

Noch schneller für große Mengen generierter Bytes, die Sie ddwie folgt verwenden können :

printf \\0| dd bs=64k conv=sync 

... und w / reguläre Dateien dd‚s seek=[num]Argument kann zu größeren Vorteil verwendet werden. Sie können 64.000 Zeilenumbrüche anstelle von Nullen erhalten, wenn Sie ,unblock cbs=1die obigen Zeilen hinzufügen und von dort aus beliebige Zeichenfolgen pro Zeile mit pasteund einfügen. /dev/nullIn diesem Fall können Sie jedoch auch Folgendes verwenden , wenn es für Sie verfügbar ist:

yes 'output string forever'

Hier noch einige ddBeispiele:

dd bs=5000 seek=1 if=/dev/null of=./H.txt

... die eine gefüllte Datei im aktuellen Verzeichnis mit dem Namen H.txt mit einer Größe von 5000 Byte erstellt (oder abschneidet)\0NUL . ddsucht direkt zum Offset und NUL-Fills alle dahinter.

<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt

... wodurch eine Datei mit demselben Namen und derselben Größe erstellt wird, die jedoch mit H-Zeichen gefüllt ist. Es nutzt ddgeskilled Verhalten s‘des Schreibens aus mindestens einen vollständigen Nullblock im Fall eines Lesefehlers , wenn noerrorund syncConversions angegeben werden (und - ohne count=- wahrscheinlich weitergehen würde mehr als man sich wünschen könnte) , und absichtlich Umleitungen ein schreibgeschützter Dateideskriptor bei dd's stdin.

mikeserv
quelle
8

Das %.0sMittel, um das Argument als Zeichenfolge mit einer Genauigkeit von Null zu konvertieren . Demnach man 3 printfergibt sich in einem solchen Fall der Genauigkeitswert

   [ ... ] the  maximum  number  of characters to be printed from a
   string for s and S conversions.

Wenn die Genauigkeit Null ist, wird das Zeichenfolgenargument daher überhaupt nicht gedruckt. Das H(das Teil des Formatbezeichners ist) wird jedoch so oft gedruckt, wie es Argumente gibt, da gemäß dem printfAbschnitt vonman bash

The format is reused as necessary to consume all  of  the  argu
ments.  If the format requires more arguments than are supplied,
the extra format specifications behave as if  a  zero  value  or
null  string,  as  appropriate,  had  been supplied. 
Steeldriver
quelle
7

In diesem Fall wird %.0simmer eine Instanz von Zeichen davor gedruckt, in diesem Fall H. Wenn Sie {1..5000} verwenden, erweitert die Shell es und es wird:

printf 'H%.0s' 1 2 3 4 ... 5000 > H.txt

Das heißt, der Befehl printf hat jetzt 5000 Argumente, und für jedes Argument erhalten Sie ein H. Diese müssen nicht sequentiell oder numerisch sein:

printf 'H%.0s' a bc fg 12 34

druckt HHHHH- dh die Anzahl der Argumente, in diesem Fall 5.

Beachten Sie, dass die Ellipsen im ersten Beispiel oben nicht wörtlich eingefügt werden, sondern eine Sequenz oder einen Bereich angeben.

KM.
quelle