Esse comando depende do shell que gera 5000 argumentos e passa-os para printf
, que os ignora. Embora possa parecer bem rápido - e é 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 seja iterado em 5.000, esse comando também custa na memória tudo o que é necessário para armazenar e delimitar os argumentos da string numérica para printf
plus o Hs. Assim como simplesmente você pode fazer:
printf %05000s|tr \ H
... que gera uma cadeia de 5000 espaços - que, pelo menos, são geralmente apenas um único byte e não custam nada para serem delimitados porque não são delimitados. Alguns testes indicam que, mesmo para apenas 5000 bytes, o custo do fork e do pipe necessários para tr
vale a pena, mesmo neste caso, e quase sempre é 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 cerca de 5 vezes uma parte (nada de científico aqui - apenas anedótico) e a versão de expansão da chave demorou um pouco mais de .02 segundos no tempo total de processamento, mas a versão tr
chegou cerca de 0,012 segundo total em média - e a versão tr
venceu todas as vezes. Eu não posso dizer que estou surpreso - {brace expansion}
é um recurso de abreviação de shell interativo útil, mas geralmente é uma coisa bastante esbanjadora para fazer onde qualquer tipo de script está em causa. A forma comum:
for i in {[num]..[num]}; do ...
... quando você pensa sobre isso, é realmente dois for
loops - o primeiro é interno e implícito em que o shell deve fazer um loop de alguma forma para gerar esses iteradores antes de salvá-los todos e iterando-os novamente para o loop for
. Geralmente, essas coisas são mais bem feitas como:
iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...
... porque você armazena apenas alguns poucos valores e sobrescreve-os conforme 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 zerar um número arbitrário de dígitos, é claro, como:
printf %05000d
Eu faço ambos sem argumentos porque para cada argumento especificado na string de formato printf
quando um argumento não é encontrado, a string nula é usada - que é interpretada como zero para um argumento numérico ou uma string vazia para uma string .
Este é o outro lado (e - na minha opinião - mais eficiente) da moeda quando comparado com o comando na questão - enquanto é possível não obter nada de algo como você faz quando você printf %.0
length strings para cada argumento, então também é possível obter algo do nada.
Mais rápido ainda para grandes quantidades de bytes gerados, você pode usar dd
como:
printf \0| dd bs=64k conv=sync
... e w / arquivos regulares O argumento dd
seek=[num]
pode ser usado para maior vantagem. Você pode obter 64k newlines em vez de nulls se adicionar ,unblock cbs=1
ao acima e de lá puder injetar strings arbitrárias por linha com paste
e /dev/null
- mas, nesse caso, se estiver disponível para você, você pode bem usar:
yes 'output string forever'
Aqui estão mais alguns exemplos de dd
:
dd bs=5000 seek=1 if=/dev/null of=./H.txt
... que cria (ou trunca) um arquivo
preenchido no diretório atual chamado H.txt de tamanho 5000 bytes. dd
NULdd
procura diretamente no deslocamento e NUL-preenche todos atrás dele.
<&1 dd bs=5000 conv=sync,noerror count=1 | tr \0 H >./H.txt
... que cria um arquivo de mesmo nome e tamanho, mas preenchido com caracteres H / H. Ele tira proveito do comportamento spec'd de noerror
de gravar pelo menos um bloco nulo completo no caso de um erro de leitura quando sync
e count=
conversões são especificados (e - sem dd
- provavelmente duraria mais do que você poderia querer) , e redireciona intencionalmente um descritor de arquivo writeonly na stdin de %code% .