web-dev-qa-db-fra.com

Comment utiliser une boucle for-each pour parcourir les chemins de fichiers dans bash?

La commande suivante tente d’énumérer tous les *.txt fichiers dans le répertoire courant et les traiter un par un:

for line in "find . -iname '*.txt'"; do 
     echo $line
     ls -l $line; 
done

Pourquoi est-ce que je reçois l'erreur suivante ?:

ls: invalid option -- 'e'
Try `ls --help' for more information.
49
0x90

Voici un meilleur moyen de boucler sur les fichiers car il gère les espaces et les nouvelles lignes dans les noms de fichiers:

find . -type f -iname "*.txt" -print0 | while IFS= read -r -d $'\0' line; do
    echo "$line"
    ls -l "$line"    
done
118
dogbane

La boucle for- itérera sur chaque entrée (séparée par un espace) de la chaîne fournie.

Vous n'exécutez pas réellement la commande find, mais vous fournissez qu'il s'agit d'une chaîne (qui est itérée par la boucle for-). Au lieu des guillemets doubles, utilisez backticks ou $():

for line in $(find . -iname '*.txt'); do 
     echo "$line"
     ls -l "$line"
done

De plus, si vos chemins/noms de fichiers contiennent des espaces, cette méthode échoue (puisque la boucle for- effectue une itération sur des entrées séparées par des espaces). Au lieu de cela, il est préférable d’utiliser la méthode décrite dans réponse dogbanes .


Pour clarifier votre erreur:

Comme dit, for line in "find . -iname '*.txt'"; itère toutes les entrées séparées par des espaces, qui sont:

  • trouver
  • .
  • -mon nom
  • '* .txt' (je pense ...)

Les deux premiers ne génèrent pas d'erreur (à part le comportement indésirable), mais le troisième est problématique car il s'exécute:

ls -l -iname

Beaucoup de commandes (bash) peuvent combiner des options à un seul caractère, donc -iname est le même que -i -n -a -m -e. Et voila: votre invalid option -- 'e' Erreur!

19
Veger

Version plus compacte utilisant les espaces et les nouvelles lignes dans le nom du fichier:

find . -iname '*.txt' -exec sh -c 'echo "{}" ; ls -l "{}"' \;
18
jserras

Utilisez substitution de commande au lieu de guillemets pour exécuter find au lieu de transmettre la commande sous forme de chaîne:

for line in $(find . -iname '*.txt'); do 
     echo $line
     ls -l $line; 
done
3
Jens Erat