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 |
Si les fichiers comportent des espaces dans leur nom, cette commande ne fonctionnera pas.
Plus d’info ici :
http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
a+
C’est exacte, je vais modifier l’article pour y pallier.
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.
@zipe31 pour répondre à la dernière remarque: rajoutez awk dans la commande…
@sam Désolé mais je ne vois pas ce que vient faire awk là-dedans ;-\
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=’
‘
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
Cool. Tu peut nous expliquer ce que ça fait pas à pas ?
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.