1.
O primeiro:
for f in *; do echo "$f" done
falha em arquivos chamados -n , -e e variantes como -nene e com algumas implementações do bash, com nomes de arquivos contendo barras invertidas.
O segundo:
find * -prune | while read f; do echo "$f" done
falha em ainda mais casos (arquivos chamados ! , -H , -name , ( , nomes de arquivos que começam ou terminam com espaços em branco ou contêm caracteres de nova linha ...)
É o shell que expande * , find não faz nada além de imprimir os arquivos que recebe como argumentos. Você poderia também ter usado printf '%s\n' em vez disso, pois como printf está embutido, também evitaria o erro potencial muitos args .
2.
A expansão de * está classificada, você pode torná-la um pouco mais rápida se não precisar da classificação. Em zsh :
for f (*(oN)) printf '%s\n' $f
ou simplesmente:
printf '%s\n' *(oN)
bash não tem equivalente, pelo que eu posso dizer, então você precisa recorrer a find .
3.
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(acima usando uma extensão não padrão do GNU / BSD -print0 ).
Isso ainda envolve gerar um comando find e usar um loop while read lento, então provavelmente será mais lento do que usar o loop for , a menos que a lista de arquivos seja enorme.
4.
Além disso, ao contrário da expansão com curinga, find fará uma chamada lstat do sistema em cada arquivo, portanto, é improvável que a não classificação compense isso.
Com o GNU / BSD find , isso pode ser evitado usando sua extensão -maxdepth , que acionará uma otimização para salvar o lstat :
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Como find inicia a saída de nomes de arquivos assim que eles são encontrados (exceto para o buffer de saída stdio), onde pode ser mais rápido é se o que você faz no loop é demorado e a lista de nomes de arquivos é mais do que um buffer stdio (4/8 kB). Nesse caso, o processamento dentro do loop será iniciado antes que find tenha encontrado todos os arquivos. Nos sistemas GNU e FreeBSD, você pode usar stdbuf para fazer com que isso aconteça mais cedo (desabilitando o buffer stdio).
5.
A maneira POSIX / padrão / portátil de executar comandos para cada arquivo com find é usar o predicado -exec :
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
No caso de echo , é menos eficiente do que fazer o loop no shell, pois o shell terá uma versão incorporada de echo , enquanto find precisará gerar um novo processo e executar /bin/echo nele para cada arquivo.
Se você precisar executar vários comandos, faça o seguinte:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Mas tenha em atenção que cmd2 só é executado se cmd1 for bem sucedido.
6.
Uma maneira canônica de executar comandos complexos para cada arquivo é chamar um shell com -exec ... {} + :
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Dessa vez, voltamos a ser eficientes com echo , pois estamos usando sh e a versão -exec + gera o mínimo de sh possível.
7.
Em meus testes em um diretório com 200.000 arquivos com nomes curtos no ext4, o zsh one (parágrafo 2.) é de longe o mais rápido, seguido pelo primeiro loop for i in * simples (embora, como de costume, bash é muito mais lento que outros shells para isso).