Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[bash] bonne façon de faire une boucle sur un ensemble de fichiers contenu dans un dossier

35 réponses
Avatar
Francois Lafont
Bonjour à tous,

Assez régulièrement, je suis amené à me faire des petits scripts bash
qui font une boucle sur une liste de fichiers contenus dans un dossier
(et dans ses sous-dossiers etc). La liste de fichiers, qui peut être
assez grande, est obtenue à l'aide de la commande find. Le but est de
faire un même petit traitement sur chacun des fichiers de cette liste
via une boucle (le traitement importe peu ici, ça peut être un
"renommage", un grep etc). La structure de mes petits scripts
ressemblent à ça du coup (c'est un exemple) :

#-------------------------------------
for fichier in $(find /le/dossier/ -type f -iname '*.tex')
do
echo Traitement de $fichier
# etc.
done
#-------------------------------------

Est-ce la bonne façon de procéder ? J'ai des doutes :


1) En effet, finalement le script revient à ça :

#-------------------------------------
for fichier in /le/dossier/f1.tex /le/dossier/f2.tex /le/dossier/f3.tex
do
echo Traitement de $fichier
# etc.
done
#-------------------------------------

Du coup, si la liste est très grande, ça me semble un peu maladroit
comme script et peut-être pas très performant. (Aucune certitude bien
sûr, c'est juste une intuition.)

2) Une autre chose qui me fait penser que ce n'est peut-être pas la
bonne façon de procéder, c'est le problème avec les noms de fichiers qui
contiennent un espace. Si find détecte un fichier qui s'appelle par
exemple "/le/dossier/sous dossier/toto.tex" alors, durant la boucle,
$fichier sera égal à la chaîne "/le/dossier/sous" puis à
"dossier/toto.tex" mais jamais à "/le/dossier/sous dossier/toto.tex".

Bref, quelle est la bonne façon de faire ce genre de boucle ?
Merci d'avance pour votre aide.


--
François Lafont

5 réponses

1 2 3 4
Avatar
Lucas Levrel
Aéris :
Dans « IFS=$'n' ; toto », c'est bien une instruction.
Dans « IFS=$'n' toto », ça n'en est pas une.
["ça" étant la partie « IFS=$'n' »]









François :
Mais si ce n'est pas une instruction, qu'est-ce donc alors ?







Benoit :
Une modification de l'environnement de la commande qui suit. Un petit
exemple :





Le 26 mai 2011, Francois Lafont a écrit :
Merci beaucoup Benoît pour cet exemple super clair.




Pour en mettre une dernière couche, j'ajouterais que IFS n'est pas
normalement une variable d'environnement, c'est juste une variable du
shell (comme ma_variable dans l'exemple de Benoît avant qu'on fasse
« export ma_variable ») ; voir Shell Variables dans man bash.

Donc pour un toto quelconque il n'est pas correct de faire
IFS=truc toto $arguments


car ça n'aura pas d'effet sur le découpage des arguments. Exemple :
var=atata
IFS=t echo $var


atata
IFS=t
echo $var


a a a
Bien sûr, ici toto c'est read, un bash-builtin qui précisément utilise
la valeur que possède IFS dans son environnement.

--
LL
Avatar
Francois Lafont
Le 31/05/2011 14:51, Lucas Levrel a écrit :

Pour en mettre une dernière couche, j'ajouterais que IFS n'est pas
normalement une variable d'environnement, c'est juste une variable du
shell (comme ma_variable dans l'exemple de Benoît avant qu'on fasse
« export ma_variable ») ; voir Shell Variables dans man bash.



Ok, c'est une variable d'environnement pour le shell bash uniquement.

Donc pour un toto quelconque il n'est pas correct de faire
$ IFS=truc toto $arguments
car ça n'aura pas d'effet sur le découpage des arguments. Exemple :
$ var=atata
$ IFS=t echo $var
atata



Je comprends très bien ce que tu veux dire, mais il me semble que
l'exemple n'est pas bien choisi ici car echo est justement une primitive
du bash. Donc on aurait pu s'attendre à ce qu'elle soit sensible à IFS.

$ IFS=t
$ echo $var
a a a



Mais echo a l'air particulièrement bizarre. Comment expliquer ceci ?

(Sous Bash donc)
$ IFS="B"
$ echo aBaBa
aBaBa

Bizarre, non ? J'ai l'impression que echo, toute bash-builtin qu'elle
puisse être, est en fin de compte totalement insensible à IFS et que
c'est au moment l'expansion de paramètres que IFS joue un rôle car par
contre j'ai bien :

$ IFS="B"
$ var="aBaBa"
$ echo $var
a a a

Mais sur ce point, je n'ai pas réussi à trouver une explication dans le
manual.

Bien sûr, ici toto c'est read, un bash-builtin qui précisément utilise
la valeur que possède IFS dans son environnement.



Oui, il est clairement indiqué dans « man bash » que read fait usage de
IFS pour le « word splitting ». Pour echo, c'est beaucoup moins clair je
trouve...


--
François Lafont
Avatar
Benoit Izac
Bonjour,

le 03/06/2011 à 16:50, Francois Lafont a écrit dans le message
<4de8f48c$0$1188$ :

Donc pour un toto quelconque il n'est pas correct de faire
$ IFS=truc toto $arguments
car ça n'aura pas d'effet sur le découpage des arguments. Exemple :
$ var=atata
$ IFS=t echo $var
atata



Je comprends très bien ce que tu veux dire, mais il me semble que
l'exemple n'est pas bien choisi ici car echo est justement une primitive
du bash. Donc on aurait pu s'attendre à ce qu'elle soit sensible à IFS.

$ IFS=t
$ echo $var
a a a



Mais echo a l'air particulièrement bizarre. Comment expliquer ceci ?

(Sous Bash donc)
$ IFS="B"
$ echo aBaBa
aBaBa

Bizarre, non ? J'ai l'impression que echo, toute bash-builtin qu'elle
puisse être, est en fin de compte totalement insensible à IFS et que
c'est au moment l'expansion de paramètres que IFS joue un rôle car par
contre j'ai bien :

$ IFS="B"
$ var="aBaBa"
$ echo $var
a a a

Mais sur ce point, je n'ai pas réussi à trouver une explication dans le
manual.



echo se moque d'IFS par contre il renvoie les arguments en les séparant
par un espace :
$ echo a b c d
a b c d

Dans ton exemple ci-dessus, $var est transformé en « a a » avant d'être
passer en argument à echo. Un « set -x » te le montre clairement :
$ set -x
$ IFS=B
+ IFS=B
$ var«aB
+ var«aB
$ echo $var
+ echo a a
a a


J'ai l'impression que ce que tu n'as pas compris, c'est que le « word
splitting » ne s'applique pas pour echo ou read (bien que ce soit
similaire car read utilise aussi $IFS). Reprenons la page de manuel de
bash :

Word Splitting
The shell scans the results of parameter expansion, command substitu‐
tion, and arithmetic expansion that did not occur within double quotes
for word splitting.

Soit seulement trois cas sont concernés pour le « word splitting » :

parameter expansion, c'est ce que l'on appelle communément une variable
$var qui peux éventuellement être transformé avant : ${var#a}, etc.

command substitution, c'est pour récupérer la sortie d'une commande :
$(ifconfig eth0)

arithmetic expansion, c'est pour les calculs : $((30+12))

--
Benoit Izac
Avatar
Benoit Izac
Bonjour,

le 03/06/2011 à 23:55, Francois Lafont a écrit dans le message
<4de9580d$0$3962$ :

J'ai l'impression que ce que tu n'as pas compris, c'est que le « word
splitting » ne s'applique pas pour echo ou read (bien que ce soit
similaire car read utilise aussi $IFS). Reprenons la page de manuel de
bash :



Tu as raison, je n'avais pas bien compris cela. Mais quand tu dis que
read ne fait pas de « word spitting », je ne suis pas tout à fait
d'accord car on lit dans le manuel « The characters in IFS are used
to split the line into words ». Du coup, j'ai un peu envie d'appeler ça
du « word splitting ». Ceci dit, tu nuances tes propos avec ce que tu
mets entre parenthèses du coup, là c'est peut-être juste une histoire de
terminologie.



Découper un mot et découper en mots, ce n'est pas exactement pareil. Le
rôle de read, qui dans les exemples précédent servaient à lire une
ligne, est plus généralement :
$ echo 'un deux trois quatre' | {
read a b c
printf '$a=%s $b=%s $c=%sn' "$a" "$b" "$c"
}
$a=un $bÞux $c=trois quatre

J'anticipe avant que tu ne poses la question, tu ne peux pas faire :
$ echo 'un deux trois quatre' | read a b c
$ # ensuite utiliser $a $b $c
car le read va être exécuté dans un sous-shell (sauf avec zsh) et du
coup le père n'a pas accès à ces variables (on en a déjà parlé il
y a quelque temps si je me souviens bien).

[...]
$ set -x
$ echo "a""bb"
+ echo abb
abb

Je pensais que "a""bb" formait 2 arguments et en fin de compte il n'en
forme qu'un. Donc au final, quelle règle s'applique pour la délimitation
d'arguments d'une commande ?



Quand tu écris "a""bb", le shell retire les « " », il ne reste plus que
abb comme le montre le set -x ci-dessus. C'est les blancs (espaces et
tabulations) qui délimitent les arguments (Simple Commands dans la page
de manuel de bash) :

$ cat arg.c
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
printf("il y a %d argumentsn", argc - 1);
while (i++ < argc - 1)
printf("arg %2d: _%s_n", i, argv[i]);
return 0;
}
$ cc -o arg arg.c
$ ./arg a b c d "ee"f 'g h'i k # tabulation entre i et k
il y a 6 arguments
arg 1: _a_
arg 2: _b_
arg 3: _c d_
arg 4: _eef_
arg 5: _g hi_
arg 6: _k_

--
Benoit Izac
Avatar
Benoit Izac
Bonjour,

le 04/06/2011 à 02:05, Francois Lafont a écrit dans le message
<4de97694$0$4173$ :

Oui il est bien dit que ce sont les blancs qui délimitent les arguments.
Mais cela me semble très réducteur, car les doubles quotes ou les
simples quotes sont aussi des délimiteurs (il me semble) et je n'arrive
pas à y trouver mention dans le manuel. Par exemple :

echo "a" "b b" # 2 arguments
echo a b b # 3 arguments.



echo a b b # 2 arguments

Pour moi, ce ne sont pas seulement les blancs qui délimitent les
arguments, les quotes jouent un rôle important.



Ils (Elles ?) jouent un rôle mais pas celui de délimiteur :

QUOTING
Quoting is used to remove the special meaning of certain characters or
words to the shell. Quoting can be used to disable special treatment
for special characters, to prevent reserved words from being recognized
as such, and to prevent parameter expansion.

--
Benoit Izac
1 2 3 4