Scrivere un carattere N volte usando il comando printf


12

Ho trovato il seguente comando per ripetere un personaggio in Linux:

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

Voglio, ad esempio, Hripetere i 5000tempi. Cosa %.0ssignifica qui?


Con tcsho zsh, repeat 5000 printf Hè più facile da capire. Con perl: print "H" x 5000(notare che che {1..5000}è un operatore di zsh ispirato a perl's 1..5000uno e poi copiato da ksh93 e bash)
Stéphane Chazelas

sì, funziona, ma utilizza molte risorse per una ripetizione più ampia, segui i suggerimenti di Stéphane Chazelas
Skaperen

1
farei questo comandoyes 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

Risposte:


20

Tale comando dipende dalla shell che genera 5000 argomenti e li passa a printfcui li ignora. Mentre può sembrare piuttosto veloce - ed è relativo ad alcune cose - la shell deve comunque generare tutte quelle stringhe come args (e delimitarle) e così via.

Oltre al fatto che gli H generati non possono essere stampati fino a quando la shell non passa per la prima volta a 5000, quel comando costa anche in memoria tutto ciò che serve per archiviare e delimitare gli argomenti di stringa numerici a printf più gli H. Proprio come puoi semplicemente fare:

printf %05000s|tr \  H

... che genera una stringa di 5000 spazi - che, almeno, di solito sono solo un singolo byte per e non costano nulla da delimitare perché non sono delimitati. Alcuni test indicano che anche per un minimo di 5000 byte il costo della forcella e del tubo richiesti trvale la pena anche in questo caso, e quasi sempre accade quando i numeri aumentano.

Mi sono imbattuto...

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

...e...

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

Ciascuno circa 5 volte un pezzo (niente di scientifico qui - solo aneddotico) e la versione di espansione del controvento è stata in media di poco più di 0,02 secondi nel tempo totale di elaborazione, ma la trversione è arrivata a circa 0,02 secondi in totale - e la trversione ha battuto ogni volta. Non posso dire di essere sorpreso - {brace expansion}è un'utile funzione di stenografia interattiva della shell, ma di solito è una cosa piuttosto dispendiosa da fare per qualsiasi tipo di scripting. La forma comune:

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

... quando ci pensate, in realtà sono due for loop: il primo è interno e implicito nel fatto che la shell deve eseguire un ciclo in qualche modo per generare quegli iteratori prima di salvarli tutti e iterarli nuovamente per il forloop. Queste cose di solito sono fatte meglio come:

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

... perché memorizzi solo pochissimi valori e li sovrascrivi mentre vai oltre a fare l'iterazione mentre generi gli iterabili.

Ad ogni modo, come il padding spaziale menzionato in precedenza, puoi anche usare printfper zeropad un numero arbitrario di cifre, ovviamente, come:

printf %05000d

Faccio entrambe le cose senza argomenti perché per ogni argomento specificato nella printfstringa di formato quando non viene trovato un argomento viene utilizzata la stringa null, che viene interpretata come zero per un argomento digit o una stringa vuota per una stringa.

Questo è l'altro (e - a mio avviso - più efficiente) della medaglia rispetto al comando nella domanda - mentre è possibile non ottenere nulla da qualcosa come si fa quando si printf %.0allungano le stringhe per ogni argomento, quindi è anche è possibile ottenere qualcosa dal nulla.

Ancora più veloce per grandi quantità di byte generati che puoi usare ddcome:

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

... e l' argomento dddei file regolari seek=[num]può essere usato per un maggiore vantaggio. Puoi ottenere 64k newline anziché null se aggiungi ,unblock cbs=1a quanto sopra e da lì puoi iniettare stringhe arbitrarie per riga con pastee /dev/null- ma in tal caso, se è disponibile per te, puoi anche usare:

yes 'output string forever'

Ecco ddcomunque altri esempi:

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

... che crea (o tronca) un \0NULfile riempito nella directory corrente denominata H.txt di dimensione 5000 byte. ddcerca dritto all'offset e riempie NUL tutto dietro di esso.

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

... che crea un file con lo stesso nome e dimensione ma riempito con caratteri maiuscoli / minuscoli. Sfrutta ddil comportamento specifico di scrivere almeno un blocco null completo in caso di un errore di lettura quando vengono specificate noerrore le syncconversioni (e - senza count=- probabilmente andrebbero avanti più a lungo di quanto si possa desiderare) e reindirizzamenti intenzionali un descrittore di file scrivibile solo allo ddstdin.


8

I %.0smezzi per convertire l'argomento come stringa , con una precisione pari a zero. Secondo man 3 printf, il valore di precisione in questo caso dà

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

quindi quando la precisione è zero, l' argomento stringa non viene stampato affatto. Tuttavia H(che fa parte dell'identificatore di formato) viene stampato tante volte quanti sono gli argomenti, poiché secondo la printfsezione diman 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. 

7

In questo caso, %.0sstampa sempre un'istanza di carattere (i) che lo precede, H in questo caso. Quando si utilizza {1..5000}, la shell lo espande e diventa:

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

cioè, il comando printf ora ha 5000 argomenti e per ogni argomento ne otterrai uno H. Questi non devono essere sequenziali o numerici:

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

stampa HHHHH- cioè il numero di argomenti, 5 in questo caso.

Nota, le ellissi nel primo esempio sopra non sono inserite letteralmente, sono lì per indicare una sequenza o un intervallo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.