Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

échappement dans un sous-shell

0 views
Skip to first unread message

Cornillet Pascal

unread,
Mar 2, 2008, 2:57:44 PM3/2/08
to
Bonsoir,
il apparaît que je m'étais trompé dans mon précédent post et que j'avais mal
identifié la source du problème, qui n'est pas spécifique à Mac OS X.

Le problème (plus vicieux qu'il n'y parait de prime abord) est qu'un des
arguments d'une commande de compilation dans un Makefile doit contenir des
espaces pour être interprété comme un seul argument et non tronqué en deux.

Ainsi si on réalise un petit script de test appelé 'count.sh' qui compte
juste le nombre d'arguments avec :
echo $#

si on l'exécute avec :
./count.sh "a a"
1
on obtient bien ce qu'on recherche qui est un seul argument.

Mais dans mon Makefile les arguements sont générés à partir d'une commande
de type `lib-config --cflags` que beaucoup de bibliothèques fournissent
pour aider la portabilité des commandes de compilation.
Mais dans mon cas certains arguments doivent être modifiés avec sed, d'où le
problème ci-dessous :

./count.sh `echo foo | sed -e 's/foo/"a a"/g'`
2

Ici on vois bien que les guillemets double ont disparues,
et que l'argument est interprété comme deux arguments :
./count.sh a a
2


Malheureusement aucun caractère d'échappement ne semble résoudre ce
problème.

J'ai alors pensé au HTML qui a des espaces insécables  
et ceci fonctionne :
./count.sh a\ a
1

mais encore une fois, ce caractère placé dans le sous-shell disparaît
cordialement comme les guillemets :
./count.sh `echo foo | sed -e 's/foo/a\ a/g'`
2

Je ne veux pas croire qu'il n'y ai aucun moyen pour résoudre ce problème.
Est-ce que quelqu'un aurait une idée ?


Eric Levenez

unread,
Mar 2, 2008, 3:35:52 PM3/2/08
to
Le 02/03/08 20:57, dans <fqf0ne$d18$1...@news.tiscali.fr>, « Cornillet Pascal »
<p.cor...@laposte.net> a écrit :

> ./count.sh `echo foo | sed -e 's/foo/"a a"/g'`

> Je ne veux pas croire qu'il n'y ai aucun moyen pour résoudre ce problème.

Je ne sais pas si c'est ce que tu veux :

./count.sh "`echo foo | sed -e 's/foo/a a/g'`"


--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.

Vincent Lefevre

unread,
Mar 2, 2008, 10:22:36 PM3/2/08
to
Dans l'article <fqf0ne$d18$1...@news.tiscali.fr>,
Cornillet Pascal <p.cor...@laposte.net> écrit:

> Mais dans mon cas certains arguments doivent être modifiés avec sed,
> d'où le problème ci-dessous :

> ./count.sh `echo foo | sed -e 's/foo/"a a"/g'`
> 2

> Ici on vois bien que les guillemets double ont disparues,

Ils n'ont pas disparu:

$ for i in `echo foo | sed -e 's/foo/"a a"/g'`; do echo ".$i."; done
."a.
.a".

C'est simplement le word splitting de sh. Pour supprimer le
word splitting, il suffit de mettre des guillemets:

$ for i in "`echo foo | sed -e 's/foo/"a a"/g'`"; do echo ".$i."; done
."a a".

ou de redéfinir la variable IFS:

$ IFS=
$ for i in `echo foo | sed -e 's/foo/"a a"/g'`; do echo ".$i."; done
."a a".

Ou si tu veux splitter sur un autre caractère:

$ IFS=a
$ for i in `echo foo | sed -e 's/foo/"a a"/g'`; do echo ".$i."; done
.".
. .
.".

En revanche, si tu veux splitter sur certains espaces mais pas sur
d'autres dans la même chaîne, c'est impossible: il faut contourner
le problème d'une manière ou d'une autre. De toute façon, le word
splitting, c'était une mauvaise idée. En cela, zsh est bien meilleur.

--
Vincent Lefèvre <vin...@vinc17.org> - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Cornillet Pascal

unread,
Mar 3, 2008, 11:16:18 AM3/3/08
to
> C'est simplement le word splitting de sh. Pour supprimer le
> word splitting, il suffit de mettre des guillemets:
>
> $ for i in "`echo foo | sed -e 's/foo/"a a"/g'`"; do echo ".$i."; done
> ."a a".

J'y aurais bien pensé seulement voilà comme je l'expliquais, il s'agit de
transmettre une liste d'argument issue d'une commande fournie par une
bibliothèque du type `lib-config --cflags` ; il y a donc plusieurs
arguments et je dois en modifier certains. Si je met des guillemets sur
l'ensemble toutes les commandes seront concaténées. Je veux juste que
certaines commandes composées de deux mots (donc avec un espace au milieu)
ne sous pas coupées.


> ou de redéfinir la variable IFS:
>
> $ IFS=
> $ for i in `echo foo | sed -e 's/foo/"a a"/g'`; do echo ".$i."; done
> ."a a".

> En revanche, si tu veux splitter sur certains espaces mais pas sur


> d'autres dans la même chaîne, c'est impossible: il faut contourner
> le problème d'une manière ou d'une autre. De toute façon, le word
> splitting, c'était une mauvaise idée. En cela, zsh est bien meilleur.

Super, ça fonctionne !
mais la façon dont cela fonctionne est des plus curieuse,
voici le hack que j'ai mis dans mon Makefile :

rule:
IFS=^ ; \
cmd arg1 arg2 \
`lib-config --cflags | sed \
-e 's/ /^/g' \
-e 's/-optx/-arg3^-optx/g' \
-e 's/-framework^Cocoa/-arg4^"-framework Cocoa"/g'`

ce qui m'étonne c'est que le "word-spliting" reste sur l'espace hors du
sous-shell, et que l'action de IFS n'interagisse que dans le sous-shell.
Je n'arrive pas à comprendre la logique de ce comportement,
mais bon lol ça fonctionne...
Merci beaucoup !

Vincent Lefevre

unread,
Mar 3, 2008, 12:17:27 PM3/3/08
to
Dans l'article <fqh832$sev$1...@news.tiscali.fr>,
Cornillet Pascal <p.cor...@laposte.net> �crit:

> voici le hack que j'ai mis dans mon Makefile :

> rule:
> IFS=^ ; \
> cmd arg1 arg2 \
> `lib-config --cflags | sed \
> -e 's/ /^/g' \
> -e 's/-optx/-arg3^-optx/g' \
> -e 's/-framework^Cocoa/-arg4^"-framework Cocoa"/g'`

> ce qui m'�tonne c'est que le "word-spliting" reste sur l'espace hors


> du sous-shell, et que l'action de IFS n'interagisse que dans le
> sous-shell.

Je ne comprends pas bien ce que tu veux dire. La variable IFS n'est
prise en compte que dans certains cas particuliers, par exemple pour
la substitution de commande[*]. Mais sinon, elle reste active dans
les sous-shells (i.e. elle n'est pas remise � sa valeur par d�faut):

sh-3.1$ IFS=:
sh-3.1$ echo `echo ab:cd; for i in \`echo ab:cd\`; do echo $i; done`
ab cd
ab
cd
sh-3.1$

C'est peut-�tre un peu plus clair avec la forme suivante:

sh-3.1$ echo $(echo ab:cd; for i in $(echo ab:cd); do echo $i; done)
ab cd
ab
cd
sh-3.1$

Le \`echo ab:cd\` ou $(echo ab:cd) est remplac� par les deux mots:
ab cd et non par ab:cd
i.e.

sh-3.1$ echo `echo ab:cd; for i in ab cd; do echo $i; done`
ab cd
ab
cd
sh-3.1$

et non:

sh-3.1$ echo `echo ab:cd; for i in ab:cd; do echo $i; done`
ab cd
ab cd
sh-3.1$

[*] http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_03

> Je n'arrive pas � comprendre la logique de ce comportement,
> mais bon lol �a fonctionne...
> Merci beaucoup !

Maintenant, �a ne fonctionnera que si tu n'as pas de '^' dans tes noms
de fichiers. Pour un truc compl�tement robuste, il faut probablement
passer par un autre langage (e.g. Perl), ou peut-�tre zsh.

--
Vincent Lef�vre <vin...@vinc17.org> - Web: <http://www.vinc17.org/>

0 new messages