1)
El primero:
for f in *; do
echo "$f"
done
falla por archivos llamados -n
, -e
y variantes como -nene
y 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 *
, find
no 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 printf
está 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)
bash
no 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 -print0
extensión no estándar GNU / BSD ).
Eso todavía implica generar un comando de búsqueda y usar un while read
bucle lento , por lo que probablemente será más lento que usar el for
bucle a menos que la lista de archivos sea enorme.
4)
Además, a diferencia de la expansión de comodín de shell, find
hará una lstat
llamada 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 -maxdepth
extensió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 find
comienza 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 find
haya terminado de encontrar todos los archivos. En los sistemas GNU y FreeBSD, puede usar stdbuf
para 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 find
es usar el -exec
predicado:
find . ! -name . -prune ! -name '.*' -exec some-cmd {} ';'
Sin echo
embargo, 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 echo
while find
, tendrá que generar un nuevo proceso y ejecutarlo /bin/echo
para cada archivo.
Si necesita ejecutar varios comandos, puede hacer:
find . ! -name . -prune ! -name '.*' -exec cmd1 {} ';' -exec cmd2 {} ';'
Pero ten cuidado porque cmd2
solo se ejecuta si cmd1
tiene é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, echo
ya que estamos usando sh
uno incorporado y la -exec +
versión genera la menor cantidad sh
posible.
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, bash
es mucho más lento que otros shells para eso).
find
no abre los archivos que encuentra. Lo único que puedo ver mordiéndote aquí con respecto a una gran cantidad de archivos es ARG_MAX .