GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi le bouclage sur la sortie de Find est-il une mauvaise pratique ?

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=$'

Linux
  1. Pourquoi Grep -o -w ne me donne-t-il pas la sortie attendue sur Mac Os X ?

  2. Itérer sur une liste de fichiers avec des espaces

  3. Pourquoi ce pipeline shell sort-il ?

  4. Linux pourquoi ne puis-je pas diriger le résultat vers rm?

  5. Est-ce que sudo su - est considéré comme une mauvaise pratique ?

Pourquoi "moins" n'affiche-t-il pas la sortie en gras ? ?

Sortie de la commande Linux en tant que paramètre d'une autre commande

Pourquoi est-il si difficile de trouver un fichier dans Ubuntu ?

sudoedit :pourquoi l'utiliser sur sudo vi ?

trouver :-exec rm {} \ ; vs. -delete - pourquoi le premier est-il largement recommandé ?

Sortie détaillée de la commande Bash find