Expansion

Le
Ph. Ivaldi
Bonjour,

On a : EXCLUDE_STR="! -name 'fic1' ! -name 'fic2'"

Le code

for SVN_FILE in $(eval "find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}"); do
[etc]
done

.. fonctionne correctement, en particulier les fichiers fic1 et fic2 ne
sont pas retournés.

Alors que le code « naturel »

for SVN_FILE in $(find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}); do
[etc]
done

renvoie fic1 et fic2.

Quelqu'un aurait-il la gentillesse de m'expliquer pourquoi ?
Ça ne va pas m'empêcher de dormir mais j'aimerais comprendre.

Merci,
--
Philippe Ivaldi.
http://www.piprime.fr/
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Benoit Izac
Le #20704321
Bonjour,

le 05/12/2009 à 23:11, Ph. Ivaldi a écrit dans le message

On a : EXCLUDE_STR="! -name 'fic1' ! -name 'fic2'"

Le code...

for SVN_FILE in $(eval "find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}"); do
[etc]
done

.. fonctionne correctement, en particulier les fichiers fic1 et fic2 ne
sont pas retournés.

Alors que le code « naturel »...

for SVN_FILE in $(find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}); do
[etc]
done

renvoie fic1 et fic2.

Quelqu'un aurait-il la gentillesse de m'expliquer pourquoi ?
Ça ne va pas m'empêcher de dormir mais j'aimerais comprendre.



Le deuxième code après expansion est équivalent à :
find "$SVN_DIR" -type f
! -regex ".*.svn.*"
! -name 'fic1' ! -name 'fic2'

Un petit exemple pour être plus clair :
% cat test.c
#include int
main(int argc, char *argv[])
{
int i;

for (i = 0; i < argc; i++) {
printf(" %d: %sn", i, argv[i]);
}
return 0;
}
% cc test.c
% ./a.out a b c
0: ./a.out
1: a
2: b
3: c
% z="a b c"
% ./a.out $z
0: ./a.out
1: a b c
% eval ./a.out $z
0: ./a.out
1: a
2: b
3: c

Bonne nuit !
--
Benoit Izac
Ph. Ivaldi
Le #20705891
Le 06 décembre 2009, Benoit Izac écrivit :

% z="a b c"
% ./a.out $z
0: ./a.out
1: a b c



qui est donc équivalent à
./a.out "a b c"
0: ./a.out
1: a b c

% eval ./a.out $z
0: ./a.out
1: a
2: b
3: c



Pour me fixer les idées :
* dans « ./a.out $z », l'expansion est littéral (tel que) ; il y a donc
un seul paramètre.
* dans « eval ./a.out $z » il y a d'abord expansion littéral
PUIS évaluation ; il y a donc 3 paramètres.

J'ai bon ?

Merci,
--
Philippe Ivaldi.
http://www.piprime.fr/
Benoit Izac
Le #20706831
Bonjour,

le 06/12/2009 à 12:38, Ph. Ivaldi a écrit dans le message

Pour me fixer les idées :
* dans « ./a.out $z », l'expansion est littéral (tel que) ; il y a donc
un seul paramètre.



Je ne comprend pas trop le sens de « littéral ». Pour moi, ./a.out n'a
qu'un seul argument qui est le contenu de la variable $z peu importe
qu'il y ait ou non des espaces. D'ailleurs :
% strace ./a.out $z
execve("./a.out", ["./a.out", "a b c"], [/* 39 vars */]) = 0

* dans « eval ./a.out $z » il y a d'abord expansion littéral
PUIS évaluation ; il y a donc 3 paramètres.



Selon ma page de manuel :

| The eval utility shall construct a command by concatenating arguments
| together, separating each with a <space>. The constructed command shall
| be read and executed by the shell.

Sinon, tu peux aussi faire :
./a.out $(printf "$z")

--
Benoit Izac
Manuel Pégourié-Gonnard
Le #20709831
Benoit Izac scripsit :

le 06/12/2009 à 12:38, Ph. Ivaldi a écrit dans le message

Pour me fixer les idées :
* dans « ./a.out $z », l'expansion est littéral (tel que) ; il y a donc
un seul paramètre.



Je ne comprend pas trop le sens de « littéral ». Pour moi, ./a.out n'a
qu'un seul argument qui est le contenu de la variable $z peu importe
qu'il y ait ou non des espaces. D'ailleurs :
% strace ./a.out $z
execve("./a.out", ["./a.out", "a b c"], [/* 39 vars */]) = 0



Tu utilises quoi comme shell ? Sous zsh avec ses options par défaut,
oui, il n'y a qu'un seul argument comme tu dis. Sous la plupart des
autres shells, il faut écrire « "$z" » pour avoir le même comportement.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Ph. Ivaldi
Le #20710491
Le 06 décembre 2009, Benoit Izac écrivit :

Pour me fixer les idées :
* dans « ./a.out $z », l'expansion est littéral (tel que) ; il y a donc
un seul paramètre.


Je ne comprend pas trop le sens de « littéral ».



Je pense à quelque chose comme ça
z="a b c"; foo $z

est équivalent à

foo "a b c"

le $z est remplacer par "a b c"... littéralement quoi :-)

mais en fait c'est faux ; ça m'apprendra à ne pas tester.
Chez moi :
$ z="a b c"; ./a.out $z
0: ./a.out
1: a
2: b
3: c

alors que
$ z="a b c"; ./a.out "$z"
0: ./a.out
1: a b c

Pour moi, ./a.out n'a
qu'un seul argument qui est le contenu de la variable $z peu importe
qu'il y ait ou non des espaces.



Comme le dit Manuel, sous Bash en tout cas, il faut des guillemets.

Du coup mon code
8<------8<------8<------8<------8<------8<------8<------8<------8<------
for SVN_FILE in $(find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}); do
[etc]
done
8<------8<------8<------8<------8<------8<------8<------8<------8<------
devrait fonctionner... mais non, il ne fonctionne pas et je comprends de
moins en moins pourquoi.

D'ailleurs :
% strace ./a.out $z
execve("./a.out", ["./a.out", "a b c"], [/* 39 vars */]) = 0



Chez moi:
$ z="a b c"; strace ./a.out $z
execve("./a.out", ["./a.out", "a", "b", "c"], [/* 41 vars */]) = 0

* dans « eval ./a.out $z » il y a d'abord expansion littéral
PUIS évaluation ; il y a donc 3 paramètres.



Selon ma page de manuel :

| The eval utility shall construct a command by concatenating arguments
| together, separating each with a <space>. The constructed command shall
| be read and executed by the shell.



Moi aussi mais il ne dit rien de l'expansion...
--
Philippe Ivaldi.
http://www.piprime.fr/
Benoit Izac
Le #20710641
Bonjour,

le 06/12/2009 à 20:32, Manuel Pégourié-Gonnard a écrit dans le message

Pour me fixer les idées :
* dans « ./a.out $z », l'expansion est littéral (tel que) ; il y a donc
un seul paramètre.



Je ne comprend pas trop le sens de « littéral ». Pour moi, ./a.out n'a
qu'un seul argument qui est le contenu de la variable $z peu importe
qu'il y ait ou non des espaces. D'ailleurs :
% strace ./a.out $z
execve("./a.out", ["./a.out", "a b c"], [/* 39 vars */]) = 0



Tu utilises quoi comme shell ? Sous zsh avec ses options par défaut,
oui, il n'y a qu'un seul argument comme tu dis. Sous la plupart des
autres shells, il faut écrire « "$z" » pour avoir le même comportement.



Effectivement j'utilise zsh. Avec dash par contre, le code de Philippe
(simplifié) fonctionne sans problème :

$ find . -type f
./efgh
./ijkl
./abcd
./mnop
$ find . -type f ! -name efgh
./ijkl
./abcd
./mnop
$ str="! -name efgh"
$ find . -type f $str
./ijkl
./abcd
./mnop
$ echo $0
dash

Même chose avec bash ou pdksh donc je ne comprends pas le problème
initial. Un strace serait utile pour avoir l'appel à execve sur le code
qui ne fonctionne pas :
strace -eexecve find ...

--
Benoit Izac
Ph. Ivaldi
Le #20713891
Le 06 décembre 2009, Benoit Izac écrivit :

Avec dash par contre, le code de Philippe
(simplifié) fonctionne sans problème :



Le simplifié oui, le mien non... j'insiste :-)
J'ai trouvé le coupable... mais je comprends toujours pas la raison
profonde de ce non fonctionnement.

8<------8<------8<------8<------8<------8<------8<------8<------8<------
$ cat /home/pi/bin/essai
#!/bin/bash

EXCLUDE="! -name 'a' ! -name 'b'"

echo "CODE 1"
for FILE in $(find ./ -type f ! -regex "toto" ${EXCLUDE} | sort); do
echo $FILE
done
echo "CODE 2"
for FILE in $(eval "find ./ -type f ! -regex "toto" ${EXCLUDE} | sort"); do
echo $FILE
done
8<------8<------8<------8<------8<------8<------8<------8<------8<------

8<------8<------8<------8<------8<------8<------8<------8<------8<------
$ ll
total 0
-rw-r--r-- 1 pi pi 0 Dec 7 13:44 a
-rw-r--r-- 1 pi pi 0 Dec 7 13:44 b
-rw-r--r-- 1 pi pi 0 Dec 7 13:44 c
8<------8<------8<------8<------8<------8<------8<------8<------8<------

8<------8<------8<------8<------8<------8<------8<------8<------8<------
$ essai
CODE 1
./a
./b
./c
CODE 2
./c
8<------8<------8<------8<------8<------8<------8<------8<------8<------

En revanche ça marche dans les deux cas en prenant (sans les quotes
autour de a et b) :
EXCLUDE="! -name a ! -name b"

8<------8<------8<------8<------8<------8<------8<------8<------8<------
$ essai
CODE 1
./c
CODE 2
./c
8<------8<------8<------8<------8<------8<------8<------8<------8<------


--
Philippe Ivaldi.
http://www.piprime.fr/
Manuel Pégourié-Gonnard
Le #20718751
Ph. Ivaldi scripsit :

EXCLUDE="! -name 'a' ! -name 'b'"

echo "CODE 1"
for FILE in $(find ./ -type f ! -regex "toto" ${EXCLUDE} | sort); do
echo $FILE
done



Là, ce que find verra comme argument, c'est
- [...]
- -regex
- toto (sans les quotes)
- !
- -name
- 'a' (avec les quotes)

En fait, les quotes sont là pour une raison simple : il n'y a aucune
raison qu'elles n'y soient pas ! Après avec développé ${EXCLUDE}, le
shell va découper suivant les espaces et c'est tout. On peut s'en
convaincre en créant un fichier nommé « 'a' » dans le répertoire et
constater qui sera exclu des résultats du find.

echo "CODE 2"
for FILE in $(eval "find ./ -type f ! -regex "toto" ${EXCLUDE} | sort"); do
echo $FILE
done


Là, eval va regarder son argument, littéralement

[...] -regex "toto" ! -name 'a' ! -name 'b' | [...]

et va l'interpréter, ce qui va enlever les quotes (celles autour de
toto, de a et de b).

En revanche ça marche dans les deux cas en prenant (sans les quotes
autour de a et b) :
EXCLUDE="! -name a ! -name b"



Oui, c'est les quotes qui gênaient.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Cyrille Lefevre
Le #20718861
Ph. Ivaldi a écrit :
Bonjour,

On a : EXCLUDE_STR="! -name 'fic1' ! -name 'fic2'"

Le code...

for SVN_FILE in $(eval "find "$SVN_DIR" -type f !
-regex ".*.svn.*" ${EXCLUDE_STR}"); do
[etc]
done




Bonjour,

perso, je préfère la syntaxe suivante :

EXCLUDE_STR="! -name 'fic1' ! -name 'fic2'"
FIND_ARGS=
FIND_ARGS="$FIND_ARGS '$SVN_DIR'"
FIND_ARGS="$FIND_ARGS ( -type d -name '.svn' ) -prune -o"
FIND_ARGS="$FIND_ARGS ( $EXCLUDE_STR )"

alternative

EXCLUDE_STR="-name 'fic1' -name 'fic2'"
FIND_ARGS=
FIND_ARGS="$FIND_ARGS '$SVN_DIR'"
FIND_ARGS="$FIND_ARGS ("
FIND_ARGS="$FIND_ARGS ( -type d -name '.svn' ) -o"
FIND_ARGS="$FIND_ARGS ( -type f $EXCLUDE_STR )"
FIND_ARGS="$FIND_ARGS ) -prune -o -print"

while read -r SVN_FILE; do
...
done << EOF
$(eval find $FIND_ARGS)
EOF

1/ moins de hormis les (, mais la, ya pas le choix
2/ les arguments de find ont tous la même syntaxe
3/ pas de gnuism (-regex), donc marche partout
4/ tous les arguments sont protégés par des ' dans les "
5/ pas de pb de fichiers avec des blancs (boucle while read)

PS : comme cité dans le thread,, prends l'habitude de toujours
protégé tes variables avec des ", je ne comprenais pas les 1ères
réponses n'ayant pas fait le rapprochement avec zsh... le
"comportement" normal du shell type bourne (bash, ksh, etc.) est
d'étendre les variables non protégés.
PS : bash n'est pas une référence... le ksh est plus posix compliant ;-)

Cordialement,

Cyrille Lefevre.
--
mailto:Cyrille.Lefevre-news%
supprimer "%nospam% et ".invalid" pour me repondre.
Ph. Ivaldi
Le #20741701
Le 08 décembre 2009, Cyrille Lefevre écrivit :

perso, je préfère la syntaxe suivante :
[...]



Joli, je retiens !
Je ne connaissais pas -prune.

PS : bash n'est pas une référence... le ksh est plus posix
compliant ;-)



Ça fait un moment que je dois essayé ksh... il va falloir que je me
force un peu.

Merci,
--
Philippe Ivaldi.
http://www.piprime.fr/
Publicité
Poster une réponse
Anonyme