For each en bash 9


Il arrive souvent de devoir appliquer une commande bash à tout une liste de fichiers. Pour trouver des fichiers en particulier, la commande find fait très bien son travail. Mais pour appliquer une commande à chaque fichier trouvé, il faut utiliser exec ou xargs avec leurs syntaxes bizarres et tout un tas de détails à ne pas oublier sous peine d’erreur. Une boucle for each est une très bonne alternative, surtout quand on a l’habitude d’itérer en Python.

for f in $(find /home/moi -iname "*.jpg"); do
	echo $(readlink -f $f);
done

En détails:

$() permet d’éxécuter en premier la commande entre parenthèses et de récupérer sa sortie.

readlink -f permet de transformer un chemin relatif en chemin absolu.

find /home/moi -iname "*.jpg" liste tous les fichiers qui finissent par .jpg dans le dossier courrant, sans tenir compte de la casse.

A noter que sur certains systèmes de fichiers (notamment issus du monde Microsoft), vous devrez utiliser l’option -noleaf sur find pour qu’il trouve tous les fichiers que vous cherchez.

for f in $(find /home/moi -iname "*.jpg"); do
	echo $(readlink -f $f);
done

Peut donc se lire:

Pour chaque fichier du dossier /home/moi dont le nom finit par .jpg (en majuscule ou minuscule), afficher son chemin absolu.

On peut d’ailleurs l’écrire en une ligne en jouant sur les points virgules:

for f in $(find /home/moi -iname "*.jpg"); do echo $(readlink -f $f); done 

EDIT suite à commentaires:

Si vous savez qu’il y a des espaces dans les chemins de fichiers, il vous faudra changer le séparateur de termes:

OLDIFS=$IFS # sauvegarde du séparateur par défaut (qui correspond à "tout espace")
IFS=$'\n' # le séparateur est maintenant le saut de ligne
for f in $(find /home/moi -iname "*.jpg"); do
	echo $(readlink -f $f);
done
IFS=$OLDIF # restauration du séparateur par défaut

9 thoughts on “For each en bash

  • zipe31

    Bonjour,

    Deux petites remarques concernant l’exemple pris :

    – À partir du moment où tu donne un chemin absolu à la commande find, le chemin en sortie (sauf demande explicite), sera toujours un chemin absolu.
    – La commande “echo” dans “echo $(readlink -f $f)” est inutile, la commande “readlink” en elle même renvoie son résultat sur la sortie standard (l’écran).

    Puis concernant la commande “find”, je ne vois pas ce qu’il y a de plus compliqué par rapport à la lourdeur de ta boucle “for… do… done” à mettre juste (espace dans le nom des fichiers compris):
    find /home/toi -iname “*.jpg” -exec readlink -f {} \;

    Bonne fin de week-end.

    JP.

  • Vash2593

    Salut,
    il faut faire attention dans le cas de scripts portable : les sous commandes shell avec `$(foo)’ ne sont pas toujours gere.

    Il faut utiliser les back quotes a la place.

    Sinon l’article est sympas : bravo. Je n’avais jamais ecrit IFS de cette maniere. J’ai plutot l’habitude de l’ecrire directement avec le saut de ligne en explicite :

    IFS=’

  • jeff

    Une autre solution qui permet d’éviter de changer la variable IFS est la suivante :

    find . -name "*.jpg" | while read line
    do
    echo $(readlink -f "$line")
    done

  • jeff

    find . -name “*.jpg” fournit la liste des fichiers dans un flux de sortie.

    Le pipe permet de rediriger ce flux sur l’entrée de ce qui se trouve à sa droite.

    Le while permet d’itérer tant que “le read renvoie quelque chose”.

    Le read permet de lire une ligne sur l’entrée qu’on lui fournit.

    Le paramètre line permet de spécifier le nom de la variable dans laquelle sera mise la ligne lue.

    C’est cette variable qui sera utilisée ensuite dans le corps de la boucle.

    Il faut quand même faire attention à bien protéger par des doubles quotes l’utilisation de “$line” sinon le readlink ne fonctionnera pas.

Leave a comment

Your email address will not be published. Required fields are marked *

Utilisez <pre lang='python'>VOTRE CODE</pre> pour insérer un block de code coloré

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.