Esta pregunta está inspirada en
¿Por qué se considera una mala práctica usar un bucle de shell para procesar texto?
Veo estas construcciones
for file in `find . -type f -name ...`; do smth with ${file}; done
y
for dir in $(find . -type d -name ...); do smth with ${dir}; done
siendo utilizado aquí casi a diario, incluso si algunas personas se toman el tiempo para comentar esas publicaciones explicando por qué este tipo de cosas deben evitarse...
Al ver la cantidad de tales publicaciones (y el hecho de que a veces esos comentarios son simplemente ignorado) Pensé que también podría hacer una pregunta:
¿Por qué se repite find
? La mala práctica de salida y cuál es la forma correcta de ejecutar uno o más comandos para cada nombre de archivo/ruta devuelto por find
?
Respuesta aceptada:
El problema
for f in $(find .)
combina dos cosas incompatibles.
find
imprime una lista de rutas de archivo delimitadas por caracteres de nueva línea. Mientras que el operador split+glob que se invoca cuando deja ese $(find .)
sin comillas en ese contexto de lista lo divide en los caracteres de $IFS
(por defecto incluye nueva línea, pero también espacio y tabulación (y NUL en zsh
)) y realiza globbing en cada palabra resultante (excepto en zsh
) (¡e incluso expansión de llaves en derivados de ksh93 o pdksh!).
Incluso si lo logras:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
Eso sigue siendo incorrecto ya que el carácter de nueva línea es tan válido como cualquier otro en una ruta de archivo. La salida de find -print
simplemente no es posprocesable de manera confiable (excepto mediante el uso de algún truco intrincado, como se muestra aquí ).
Eso también significa que el shell necesita almacenar la salida de find
completamente, y luego dividirlo+globarlo (lo que implica almacenar esa salida por segunda vez en la memoria) antes de comenzar a recorrer los archivos.
Tenga en cuenta que find . | xargs cmd
tiene problemas similares (allí, espacios en blanco, nueva línea, comillas simples, comillas dobles y barra invertida (y con algo de xarg
bytes de implementaciones que no forman parte de caracteres válidos) son un problema)
Más alternativas correctas
La única forma de usar un for
bucle en la salida de find
sería usar zsh
que soporta IFS=$'