OVH Cloud OVH Cloud

Bash : mauvais shell

9 réponses
Avatar
Fred
Bonjour,
Je sais que ce sujet a déjà été abordé dans divers forums mais là je
n'en peux plus et j'ai franchi le pas : j'ai installé le vrai ksh sur
mes serveurs Linux.

Le problème que vous connaissez surement est la non compatibilité des
commandes built-in "while read" du Bash avec le Bourne shell et le Korn
shell.

Le cas le plus parlant est le suivant :
On fait met à jour une variable à l'intérieur d'une boucle while.

Exemple (cas réel) :

!/bin/sh
EXCLUDE_BIN_CLAUSE=
if [ -d $REF ]
then
cd $REF > /dev/null
find * -type d -a ! -name $TARGET -print | while read EXCL_BIN_DIR
do
EXCLUDE_BIN_CLAUSE="$EXCLUDE_BIN_CLAUSE -o -name
$EXCL_BIN_DIR -prune"
done
fi
cd $TARGET
find . $EXCLUDE_BIN_CLAUSE -o -print | cpio -pdl $TARGET/Data


Ceci marche très bien avec tous les vrais Bourne shell mais sous Linux,
où sh -> /bin/bash, ça ne fonctionne pas.

La FAQ du Bash dit que c'est normal (ce n'est pas un bug, c'est une
foncionnalité, air connu) car le pipe crée des processus fils etc.

Je maintiens que ce comportement est un gros bug. En efet, pour une
commande built-in le shell ne crée pas de processus fils. Dans l'exemple
ci-dessus, le shell doit donc créer un pipe entre la commande find et
lui-même et non pas créer un sous process pour faire le while read.

J'utilise beaucoup de scripts shell qui peuvent inclure une telle
structure et je n'ai donc aucune confiance dans le bash pour les
interpréter.

A quand une modification du Bash pour être enfin compatible avec le
Bourne Shell.

(PS : j'ai déjà posté ce message sur fr.comp.os.linux.moderated mais ce
forum est dans une léthargie proche de la mort)

A+
Fred

9 réponses

Avatar
Pascal Bourguignon
Fred writes:
Le cas le plus parlant est le suivant :
On fait met à jour une variable à l'intérieur d'une boucle while.


Avec ksh ou zsh, ça marche quand while est à la fin du pipe, mais
quand il est au début?

i ; s=0
while [ $i -gt 0 ] ; do
s=$(( $s + $i * $i )) ; i=$(( $i - 1 )) ;
echo $i $s ;
done | grep 3
echo $s

Avec zsh 4.0.6 (i386-suse-linux), ça ne marche pas mieux qu'avec bash,
tu me diras avec ksh...

C'est pour ça que dès que j'ai un script de plus de deux lignes, c'est
en lisp, un vrai langage de programmation, que je l'écris !

--
__Pascal_Bourguignon__ _ Software patents are endangering
() ASCII ribbon against html email (o_ the computer industry all around
/ 1962:DO20I=1.100 // the world http://lpf.ai.mit.edu/
2001:my($f)=`fortune`; V_/ http://petition.eurolinux.org/

Avatar
R12y
On Thu, 15 Dec 2005 09:28:16 +0100, Fred wrote:

Bonjour,
Je sais que ce sujet a déjà été abordé dans divers forums mais là je
n'en peux plus et j'ai franchi le pas : j'ai installé le vrai ksh sur
mes serveurs Linux.


<troll + provoc + 2nd degré>
Si t'es vraiment tellement fort, pourquoi t'as pas non plus viré bash
de ton système? au aurai vu comment se seraient comporté les script d'init.
</>

Follow up dans l'arène, gladiateur....
--
Telephone portable "intelligent" (SmartPhone) GSM, GPRS,...
Il est sous Linux, ne coute pas trop cher,...
http://www.it2l.com/product_info.php?cPath‘&products_idE6

Avatar
Fred
Fred writes:

Le cas le plus parlant est le suivant :
On fait met à jour une variable à l'intérieur d'une boucle while.



Avec ksh ou zsh, ça marche quand while est à la fin du pipe, mais
quand il est au début?

i ; s=0
while [ $i -gt 0 ] ; do
s=$(( $s + $i * $i )) ; i=$(( $i - 1 )) ;
echo $i $s ;
done | grep 3
echo $s

Avec zsh 4.0.6 (i386-suse-linux), ça ne marche pas mieux qu'avec bash,
tu me diras avec ksh...

C'est pour ça que dès que j'ai un script de plus de deux lignes, c'est
en lisp, un vrai langage de programmation, que je l'écris !



Ca ne marche pas mieux avec le ksh.
J'ai essayé aussi avec un vieux bourne shell sur Solaris (en changeant
les $(( )) par `expr ...`) et c'est pareil (ce qui est rassurant)
Je ne m'explique pas pourquoi il y a une différence quand le while est
avant le pipe. Si quelqu'un a une idée...

A+
Fred


Avatar
[SauronDeMordor]
On Thu, 15 Dec 2005 09:28:16 +0100, Fred wrote:

Bonjour,
Je sais que ce sujet a déjà été abordé dans divers forums ma is là je
n'en peux plus et j'ai franchi le pas : j'ai installé le vrai ksh su r
mes serveurs Linux.


<troll + provoc + 2nd degré>
Si t'es vraiment tellement fort, pourquoi t'as pas non plus viré bash
de ton système? au aurai vu comment se seraient comporté les script d'init.
</>

Follow up dans l'arène, gladiateur....


oui dabns le cas ou ton os est linux,
mais j ai plein de machine qui n ont pas bash d installees et qui se port ent
tres bien.

je te rappele qu ici c est f.c.os.UNIX et pas linux


Avatar
Stephane Chazelas
On Thu, 15 Dec 2005 09:28:16 +0100, Fred wrote:
[...]
Le problème que vous connaissez surement est la non compatibilité des
commandes built-in "while read" du Bash avec le Bourne shell et le Korn
shell.
[...]

!/bin/sh
EXCLUDE_BIN_CLAUSE > if [ -d $REF ]
then
cd $REF > /dev/null
find * -type d -a ! -name $TARGET -print | while read EXCL_BIN_DIR
do
EXCLUDE_BIN_CLAUSE="$EXCLUDE_BIN_CLAUSE -o -name
$EXCL_BIN_DIR -prune"
done
fi
cd $TARGET
find . $EXCLUDE_BIN_CLAUSE -o -print | cpio -pdl $TARGET/Data


Ceci marche très bien avec tous les vrais Bourne shell mais sous Linux,
où sh -> /bin/bash, ça ne fonctionne pas.


Non, ca ne marche avec aucun Bourne shell. Ca marche avec
certaines versions de "sh" basees sur certaines version de ksh
ou zsh.

Et, d'apres POSIX ton script n'est pas POSIX car il presuppose
qu'une partie ou l'autre d'un pipe n'est pas executé dans un
sous-shell.

Je maintiens que ce comportement est un gros bug. En efet, pour une
commande built-in le shell ne crée pas de processus fils.


Non, ce n'est pas un bug pour faire communiquer a travers un
pipe, il faut deux processus, c'est un choix d'implementation de
dire quelle partie du pipe est dans quel processus.

Tu verras qu'avec ksh, dans certaines situations, tu auras un
sous-shell aussi. Et ce genre d'optimisations de ksh pose
souvent plus de probleme qu'elles ne sont utiles (en particulier
avec la gestion des signaux).

J'utilise beaucoup de scripts shell qui peuvent inclure une telle
structure et je n'ai donc aucune confiance dans le bash pour les
interpréter.


Tu ne devrais pas, c'est incorrect. pdksh, le Bourne shell et
probablement les shells basés sur ash se comportent comme bash.
Le Bourne shell va meme encore plus loin (toute redirection sur
une commande composee lance un sous-shell).

A quand une modification du Bash pour être enfin compatible avec le
Bourne Shell.


A quand une modification du ksh de David Korn, tu veux dire?

Je crois que tu te meprends sur le sens de "Bourne shell". La
plupart des systemes n'ont plus de Bourne shell de nos jour. Ce
shell n'est pas POSIX/UNIX compatible, c'est une relique qu'on
ne trouve guere plus que sous Solaris de nos jours.

--
Stephane

Avatar
Fred
On Thu, 15 Dec 2005 09:28:16 +0100, Fred wrote:
[...]

Le problème que vous connaissez surement est la non compatibilité des
commandes built-in "while read" du Bash avec le Bourne shell et le Korn
shell.


[...]

!/bin/sh
EXCLUDE_BIN_CLAUSE >>if [ -d $REF ]
then
cd $REF > /dev/null
find * -type d -a ! -name $TARGET -print | while read EXCL_BIN_DIR
do
EXCLUDE_BIN_CLAUSE="$EXCLUDE_BIN_CLAUSE -o -name
$EXCL_BIN_DIR -prune"
done
fi
cd $TARGET
find . $EXCLUDE_BIN_CLAUSE -o -print | cpio -pdl $TARGET/Data




Non, ce n'est pas un bug pour faire communiquer a travers un
pipe, il faut deux processus, c'est un choix d'implementation de
dire quelle partie du pipe est dans quel processus.


Pour moi, une commande dite "build-in" ne doit pas créer de processus.
Quel est sinon l'intérêt des commandes build-in ?



Tu verras qu'avec ksh, dans certaines situations, tu auras un
sous-shell aussi. Et ce genre d'optimisations de ksh pose
souvent plus de probleme qu'elles ne sont utiles (en particulier
avec la gestion des signaux).



Disons plutôt que j'aurais dû voir (je le pratique depuis... beaucoup
d'années). Le ksh étant le shell standard sur Solaris, AIX, HP-UX je
n'ai rencontré ces problèmes que depuis les portages sur Linux avec le
bash par défaut et pas de ksh.
Les scripts commençant par #!/bin/ksh, il est donc nécessaire
d'installer le ksh et non pas de faire un lien ksh -> /bin/bash


J'utilise beaucoup de scripts shell qui peuvent inclure une telle
structure et je n'ai donc aucune confiance dans le bash pour les
interpréter.



Tu ne devrais pas, c'est incorrect. pdksh, le Bourne shell et
probablement les shells basés sur ash se comportent comme bash.
Le Bourne shell va meme encore plus loin (toute redirection sur
une commande composee lance un sous-shell).


Je n'ai pas le choix, je ne vais pas tout réécrire.

Dans mon exemple, il suffit de remplacer :

find * -type d -a ! -name $TARGET -print | while read EXCL_BIN_DIR

par

for EXCL_BIN_DIR in `find * -type d -a ! -name $TARGET -print`

mais le problème est que rien n'indique que la première version n'a pas
marché. La variable sensée être remplie reste vide mais ça n'empêche pas
le script de fonctionner. Il faut donc tout reprendre à la main !
Surtout qu'il y a peut-être d'autre cas litigieux.


Avatar
Pascal Bourguignon
Fred writes:

Fred writes:

Le cas le plus parlant est le suivant :
On fait met à jour une variable à l'intérieur d'une boucle while.
Avec ksh ou zsh, ça marche quand while est à la fin du pipe, mais

quand il est au début?
i ; s=0
while [ $i -gt 0 ] ; do s=$(( $s + $i * $i )) ; i=$(( $i - 1 )) ;
echo $i $s ; done | grep 3
echo $s Avec zsh 4.0.6 (i386-suse-linux), ça ne marche pas mieux
qu'avec bash,
tu me diras avec ksh...
C'est pour ça que dès que j'ai un script de plus de deux lignes,
c'est
en lisp, un vrai langage de programmation, que je l'écris !



Ca ne marche pas mieux avec le ksh.
J'ai essayé aussi avec un vieux bourne shell sur Solaris (en changeant
les $(( )) par `expr ...`) et c'est pareil (ce qui est rassurant)
Je ne m'explique pas pourquoi il y a une différence quand le while est
avant le pipe. Si quelqu'un a une idée...



C'est normal, car tous les programmes avant les pipes sont lancés dans
des processus distincts. Seul la dernière commande d'une chaine de
pipes peut être exécutée par le shell courrant, si c'est une commande
interne. bash ne le fait pas: il fait comme pour les autres
commandes du pipe, il exécute la dernière commande dans un processus
distinct; d'autre shells le font, ce qui permet de modifier des
variables du shell courrant dans cette dernière commande d'une chaine
de pipes.

Mais dans tous les cas, c'est foireux: si on veut manipuler des
variables et faire des calculs, il vaut mieux utiliser un vrai langage
de programmation.

--
__Pascal Bourguignon__ http://www.informatimago.com/

"You question the worthiness of my code? I should kill you where you
stand!"



Avatar
Stephane Chazelas
On Thu, 15 Dec 2005 15:26:50 +0100, Fred wrote:
[...]
Non, ce n'est pas un bug pour faire communiquer a travers un
pipe, il faut deux processus, c'est un choix d'implementation de
dire quelle partie du pipe est dans quel processus.


Pour moi, une commande dite "build-in" ne doit pas créer de processus.
Quel est sinon l'intérêt des commandes build-in ?


Elle ne demarre pas de process, c'est le pipe qui demarre des
processus. Dans:

whatever | {
external-command
builtin-command
whatever
}

Il y a un fork de plus pour external-command que pour
builtin-command.

Disons plutôt que j'aurais dû voir (je le pratique depuis... beaucoup
d'années). Le ksh étant le shell standard sur Solaris, AIX, HP-UX je
n'ai rencontré ces problèmes que depuis les portages sur Linux avec le
bash par défaut et pas de ksh.


bash et ksh sont tous les deux POSIX conformant (aux bugs pres),
c'est ton script qui n'est pas POSIX conformant (standard) s'il
fait ce genre de supposition. Dans une future version de ksh, le
comportement pourrait changer, dans la mesure ou POSIX l'autorise.

Les scripts commençant par #!/bin/ksh, il est donc nécessaire
d'installer le ksh et non pas de faire un lien ksh -> /bin/bash


Encore une fois, pdksh se comporte comme bash. Donc, ce n'est
pas correct non plus de supposer que ksh se comporte d'une facon
ou d'une autre.

Tu ne peux pas faire cette supposition dans un script en Bourne
shell, parce que tout simplement ca ne marchera pas avec le
Bourne shell lui-meme.

Tu ne peux pas le faire non plus en sh (POSIX), car c'est contre
la loi (la specification Unix).

Tu ne peux pas le faire non plus en ksh, puisque le comportement
depend de l'implementation de ksh (qui n'est pas specifiée).

[...]
Tu ne devrais pas, c'est incorrect. pdksh, le Bourne shell et
probablement les shells basés sur ash se comportent comme bash.
Le Bourne shell va meme encore plus loin (toute redirection sur
une commande composee lance un sous-shell).


Je n'ai pas le choix, je ne vais pas tout réécrire.

Dans mon exemple, il suffit de remplacer :

find * -type d -a ! -name $TARGET -print | while read EXCL_BIN_DIR

par

for EXCL_BIN_DIR in `find * -type d -a ! -name $TARGET -print`


Aucun n'est correct.

find . -type d ! -name "$TARGET" -print | IFS= while read -r ...

ou

set -f
IFS='
'
for ... in `find . ...`

Tu as le droit de faire:

find ... | {
while ...; do
...
done
... avec "$variable-de-ce-sous-shell"
}

mais le problème est que rien n'indique que la première version n'a pas
marché. La variable sensée être remplie reste vide mais ça n'empêche pas
le script de fonctionner. Il faut donc tout reprendre à la main !


C'est la variable du sous-shell qui est remplie.

Dans:

var=1
(
var=2
)
echo "$var"

tu auras 1, d'apres POSIX.

dans

var=1
whatever | var=2
echo "$var"

le resultat est "unspecified" d'apres POSIX car var=2 peut etre
lance dans un sous-shell ou non, donc faut pas faire de
supposition dessus.

Vaut meme mieux faire:

var=1
whatever | (var=2)

ou

(whatever | var=2)

pour eviter que $var ne soit touché.

--
Stephane


Avatar
Xavier Gachon
Le 15-12-2005, Fred a écrit :
Le problème que vous connaissez surement est la non compatibilité des
commandes built-in "while read" du Bash avec le Bourne shell et le Korn
shell.



Pour moi, une commande dite "build-in" ne doit pas créer de processus.
Quel est sinon l'intérêt des commandes build-in ?


Je m'etonnerais toujours d'entendre regulierement parler de "vrais"
Bourne Shell: http://www.in-ulm.de/~mascheck/bourne/

voir en particulier le deuxieme point de
http://www.in-ulm.de/~mascheck/bourne/common.html

pour repondre a ta question il serait "legerement" casse gueule de faire
du control de flux ou de modifier l'environnement avec des simples commands
sans meme parler de syntaxe :)

puisqu'il a ete question de POSIX une autre solution en plus de celles
ci-dessus ou precedemment proposées (for et jonglage d'IFS):

while read ...; do ... ; done << /KLA/PWET
$(find ...)
/KLA/PWET

Mais il est vrai qu'il est plus simple de s'en prendre a Bash
que d'admettre qu'on se traine des kilometres de scripts pourris
quel que soit le bourne shell ciblé, c'est evidemment pire si
l'on est soi-meme l'auteur de nombre des scripts en question :-)

xavier.