1)
El primero:
for f in *; do
echo "$f"
done
falla por archivos llamados -n, -ey variantes como -neney con algunas implementaciones de bash, con los nombres de archivo que contiene las barras invertidas.
El segundo:
find * -prune | while read f; do
echo "$f"
done
falla para aún más casos (llamados archivos !, -H, -name, (, nombres de archivo que comienzan o terminan con espacios en blanco o caracteres de nueva línea ...)
Es el shell que se expande *, findno hace más que imprimir los archivos que recibe como argumentos. También podría haber utilizado en su printf '%s\n'lugar lo que printfestá incorporado también evitaría el error potencial de demasiados argumentos .
2)
La expansión de *está ordenada, puede hacerlo un poco más rápido si no necesita la clasificación. En zsh:
for f (*(oN)) printf '%s\n' $f
o simplemente:
printf '%s\n' *(oN)
bashno tiene equivalente por lo que puedo decir, por lo que tendrías que recurrir find.
3)
find . ! -name . -prune ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
(arriba usando una -print0extensión no estándar GNU / BSD ).
Eso todavía implica generar un comando de búsqueda y usar un while readbucle lento , por lo que probablemente será más lento que usar el forbucle a menos que la lista de archivos sea enorme.
4)
Además, a diferencia de la expansión de comodín de shell, findhará una lstatllamada al sistema en cada archivo, por lo que es poco probable que la no clasificación compense eso.
Con GNU / BSD find, eso puede evitarse utilizando su -maxdepthextensión que activará una optimización guardando lstat:
find . -maxdepth 1 ! -name '.*' -print0 |
while IFS= read -rd '' f; do
printf '%s\n' "$f"
done
Debido a que findcomienza a generar nombres de archivos tan pronto como los encuentra (excepto para el búfer de salida stdio), donde puede ser más rápido es si lo que hace en el bucle lleva mucho tiempo y la lista de nombres de archivo es más que un búfer stdio (4 / 8 kB). En ese caso, el procesamiento dentro del ciclo comenzará antes de que findhaya terminado de encontrar todos los archivos. En los sistemas GNU y FreeBSD, puede usar stdbufpara hacer que eso suceda antes (deshabilitando el almacenamiento en búfer stdio).
5)
La forma POSIX / estándar / portátil de ejecutar comandos para cada archivo findes usar el -execpredicado:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Sin echoembargo, en el caso de que, sea menos eficiente que hacer el bucle en el shell, ya que el shell tendrá una versión incorporada de echowhile find, tendrá que generar un nuevo proceso y ejecutarlo /bin/echopara cada archivo.
Si necesita ejecutar varios comandos, puede hacer:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Pero ten cuidado porque cmd2solo se ejecuta si cmd1tiene éxito.
6)
Una forma canónica de ejecutar comandos complejos para cada archivo es llamar a un shell con -exec ... {} +:
find . ! -name . -prune ! -name '.*' -exec sh -c '
for f do
cmd1 "$f"
cmd2 "$f"
done' sh {} +
Esa vez, volvemos a ser eficientes, echoya que estamos usando shuno incorporado y la -exec +versión genera la menor cantidad shposible.
7)
En mis pruebas en un directorio con 200,000 archivos con nombres cortos en ext4, el zsh(párrafo 2) es, con mucho, el más rápido, seguido del primer for i in *bucle simple (aunque, como de costumbre, bashes mucho más lento que otros shells para eso).
findno abre los archivos que encuentra. Lo único que puedo ver mordiéndote aquí con respecto a una gran cantidad de archivos es ARG_MAX .