1
O primeiro:
for f in *; do
echo "$f"
done
falha para arquivos chamados -n, -ee variantes como -nenee com algumas implementações festança, com nomes de arquivos que contém barras invertidas.
O segundo:
find * -prune | while read f; do
echo "$f"
done
falha para 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 se expande *, findnão faz nada além de imprimir os arquivos que recebe como argumentos. Em printf '%s\n'vez disso, você também poderia ter usado o que, como printfestá embutido, evitaria o erro potencial de muitos argumentos .
2)
A expansão de *é 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)
bashnão tem equivalente, tanto quanto eu posso dizer, então você precisaria recorrer find.
3)
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(acima, usando uma -print0extensão não padrão do GNU / BSD ).
Isso ainda envolve gerar um comando find e usar um while readloop lento ; portanto, provavelmente será mais lento do que usar o forloop, a menos que a lista de arquivos seja enorme.
4)
Além disso, ao contrário da expansão de curinga do shell, findfará uma lstatchamada 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 -maxdepthextensão que acionará uma otimização, salvando o lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Como findinicia a saída de nomes de arquivos assim que os encontra (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 que um buffer stdio (4 / 8 kB). Nesse caso, o processamento no loop será iniciado antes de findconcluir a localização de todos os arquivos. Nos sistemas GNU e FreeBSD, você pode stdbuffazer isso acontecer mais cedo (desativando o buffer do stdio).
5)
A maneira POSIX / standard / portátil de executar comandos para cada arquivo findé usar o -execpredicado:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
No echoentanto, isso é menos eficiente do que fazer o loop no shell, pois o shell terá uma versão interna do echowhile finde precisará gerar um novo processo e executá /bin/echo-lo para cada arquivo.
Se você precisar executar vários comandos, poderá:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Mas cuidado, isso cmd2só é executado se cmd1for 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 {} +
Nesse momento, voltamos a ser eficientes, echopois estamos usando sho incorporado e a -exec +versão gera o shmínimo possível.
7)
Nos meus testes em um diretório com 200.000 arquivos com nomes abreviados no ext4, o zsh(parágrafo 2.) é de longe o mais rápido, seguido pelo primeiro for i in *loop simples (embora, como de costume, bashé muito mais lento que outros shells para isso).
findnão abre os arquivos que encontra. A única coisa que posso ver mordendo você aqui em relação a um grande número de arquivos é ARG_MAX .