Esse comando depende do shell gerar 5000 argumentos e passá-los aos printf
quais os ignora. Embora possa parecer bastante rápido - e seja relativo a algumas coisas - o shell ainda deve gerar todas essas strings como args (e delimitá-las) e assim por diante.
Além do fato de que os Hs gerados não podem ser impressos até que o shell itere primeiro para 5000, esse comando também custa na memória tudo o que é necessário para armazenar e delimitar os argumentos de sequência numérica para printf
somar os Hs. Da mesma maneira que você pode:
printf %05000s|tr \ H
... que gera uma sequência de 5.000 espaços - que, pelo menos, geralmente são apenas um byte e não custam nada para delimitar porque não são delimitados. Alguns testes indicam que, mesmo com apenas 5000 bytes, o custo do garfo e do tubo necessário tr
vale a pena mesmo nesse caso, e quase sempre ocorre quando os números aumentam.
Eu corri...
time bash -c 'printf H%.0s {1..5000}' >/dev/null
...e...
time bash -c 'printf %05000s|tr \ H' >/dev/null
Cada uma cerca de 5 vezes por peça (nada de científico aqui - apenas anedótico) e a versão de expansão da cinta tr
tiveram em média um pouco mais de 0,02 segundos no tempo total de processamento, mas a versão chegou em média 0,012 segundos no total - e a tr
versão foi melhorada toda vez. Não posso dizer que estou surpreso - {brace expansion}
é um recurso de taquigrafia interativo útil do shell, mas geralmente é uma coisa muito esbanjadora para se fazer em qualquer tipo de script. A forma comum:
for i in {[num]..[num]}; do ...
... quando você pensa sobre isso, são realmente dois for
loops - o primeiro é interno e está implícito, pois o shell deve fazer um loop de alguma maneira para gerar esses iteradores antes de salvá-los e iterá-los novamente para o seu for
loop. Geralmente, essas coisas são feitas melhor como:
iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...
... porque você armazena apenas muito poucos valores e os substitui à medida que avança, além de fazer a iteração enquanto gera os iteráveis.
De qualquer forma, como o preenchimento de espaço mencionado anteriormente, você também pode usar printf
para zeropar um número arbitrário de dígitos, é claro, como:
printf %05000d
Faço as duas coisas sem argumentos, porque para cada argumento especificado na printf
string de formato de um argumento, quando não é encontrado, a string nula é usada - que é interpretada como zero para um argumento de dígito ou vazia para uma string.
Esse é o outro lado (e - na minha opinião - mais eficiente) da moeda quando comparado com o comando da pergunta - embora seja possível não obter nada de algo como você faz ao printf %.0
estender as seqüências de caracteres para cada argumento, também é é possível obter algo do nada.
Ainda mais rápido para grandes quantidades de bytes gerados, você pode usar dd
como:
printf \\0| dd bs=64k conv=sync
... e dd
o seek=[num]
argumento de arquivos regulares pode ser usado para maior vantagem. Você pode obter novas linhas de 64k em vez de nulas se adicionar as opções ,unblock cbs=1
acima e a partir daí poderá injetar seqüências arbitrárias por linha com paste
e /dev/null
- mas nesse caso, se estiver disponível, você também poderá usar:
yes 'output string forever'
Aqui estão mais alguns dd
exemplos de qualquer maneira:
dd bs=5000 seek=1 if=/dev/null of=./H.txt
... que cria (ou trunca) um \0NUL
arquivo preenchido no diretório atual chamado H.txt com tamanho de 5000 bytes. dd
procura direto para o offset e preenche NUL por trás dele.
<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt
... que cria um arquivo com o mesmo nome e tamanho, mas preenchido com caracteres H / H. Ele aproveita dd
o comportamento spec'd de escrever pelo menos um nulo-quarteirão inteiro em caso de um erro de leitura quando noerror
e sync
conversões são especificados (e - sem count=
- provavelmente continuar mais tempo do que você poderia querer) , e intencionalmente redirecionamentos um descritor de arquivo writeonly no dd
stdin.
tcsh
ouzsh
,repeat 5000 printf H
é mais fácil de entender. Comperl
:print "H" x 5000
(note que esse{1..5000}
é um operador zsh inspirado porperl
's1..5000
um e depois copiado por ksh93 e bash)