Cette question est inspirée de
Pourquoi l'utilisation d'une boucle shell pour traiter du texte est-elle considérée comme une mauvaise pratique ?
Je vois ces constructions
for file in `find . -type f -name ...`; do smth with ${file}; done
et
for dir in $(find . -type d -name ...); do smth with ${dir}; done
utilisé ici presque quotidiennement même si certaines personnes prennent le temps de commenter ces messages en expliquant pourquoi ce genre de choses devrait être évité…
Vu le nombre de tels messages (et le fait que parfois ces commentaires sont simplement ignoré) J'ai pensé que je pourrais aussi bien poser une question :
Pourquoi boucle-t-il sur find
? mauvaise pratique de sortie et quelle est la bonne façon d'exécuter une ou plusieurs commandes pour chaque nom de fichier/chemin renvoyé par find
?
Réponse acceptée :
Le problème
for f in $(find .)
combine deux choses incompatibles.
find
imprime une liste de chemins de fichiers délimités par des caractères de retour à la ligne. Tandis que l'opérateur split+glob qui est invoqué lorsque vous quittez ce $(find .)
non cité dans ce contexte de liste le divise sur les caractères de $IFS
(par défaut inclut une nouvelle ligne, mais aussi un espace et une tabulation (et NUL dans zsh
)) et effectue un globbing sur chaque mot résultant (sauf en zsh
) (et même l'expansion des accolades dans les dérivés ksh93 ou pdksh !).
Même si vous y parvenez :
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
C'est toujours faux car le caractère de nouvelle ligne est aussi valide que n'importe quel autre dans un chemin de fichier. La sortie de find -print
n'est tout simplement pas post-traitable de manière fiable (sauf en utilisant une astuce alambiquée, comme indiqué ici ).
Cela signifie également que le shell doit stocker la sortie de find
complètement, puis divisez-le + glob (ce qui implique de stocker cette sortie une seconde fois en mémoire) avant de commencer à boucler sur les fichiers.
Notez que find . | xargs cmd
a des problèmes similaires (ici, blancs, retour à la ligne, guillemets simples, guillemets doubles et antislash (et avec certains xarg
les implémentations d'octets ne faisant pas partie de caractères valides) posent problème)
Plus d'alternatives correctes
La seule façon d'utiliser un for
boucle sur la sortie de find
serait d'utiliser zsh
qui prend en charge IFS=$'