J'ai eu des problèmes étranges avec Bash dernièrement. En essayant de simplifier mon script, j'ai proposé ce petit morceau de code:
$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
return
aurait dû sortir de la fonction sans impression $?
, ne devrait-il pas? Eh bien, alors j'ai vérifié si je peux revenir d'un tuyau seul:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
La même chose se produit sans une boucle while
:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
Y a-t-il quelque chose qui me manque ici? Une recherche Google n'a rien apporté à ce sujet! Ma version Bash est 4.2.37 (1) -Release sur Debian Wheezy.
Associé: https://stackoverflow.com/a/7804208/49379
Ce n'est pas un bogue que vous ne pouvez pas quitter un script ou votre retour d'une fonction par exit
ou return
dans les sous-espaces. Ils sont exécutés dans un autre processus et n'affectant pas le processus principal.
En outre, je suppose que vous voyez des comportements sans papiers de bash sur (probablement) des spécifications non définies. Dans une fonction, aucune erreur n'est affirmée pour return
au niveau supérieur des commandes SUBSHELL et cela se comporte comme exit
.
IMHO C'est un bug Bash pour le comportement incohérent de return
selon que l'instruction principale est en fonction ou non.
#!/bin/bash
o() {
# Runtime error, but no errors are asserted,
# each $? is set to the return code.
echo | return 10
echo $?
(return 11)
echo $?
# Valid, each $? is set to the exit code.
echo | exit 12
echo $?
(exit 13)
echo $?
}
o
# Runtime errors are asserted, each $? is set to 1.
echo | return 20
echo $?
(return 21)
echo $?
# Valid, each $? is set to the exit code.
echo | exit 22
echo $?
(exit 23)
echo $?
Sortir:
$ bash script.sh
10
11
12
13
script.sh: line 20: return: can only `return' from a function or sourced script
1
script.sh: line 22: return: can only `return' from a function or sourced script
1
22
23
La documentation de POSIX, tilisation return
en dehors de la fonction ou du script source est indéterminée . Donc, cela dépend de votre coquille à gérer.
Systemv Shell fera signaler une erreur, _ ksh
, return
en dehors de la fonction ou du script source se comporter comme exit
. La plupart des autres coquilles de Posix et Schily's Osh se comportent également comme ça:
$ for s in /bin/*sh /opt/schily/bin/osh; do
printf '<%s>\n' $s
$s -c '
o(){ echo | while read l; do return 0; done; echo $?;}; o
'
done
</bin/bash>
0
</bin/dash>
0
</bin/ksh>
</bin/lksh>
0
</bin/mksh>
0
</bin/pdksh>
0
</bin/posh>
0
</bin/sh>
0
</bin/yash>
0
</bin/zsh>
</opt/schily/bin/osh>
0
ksh
et zsh
_ N'a pas sortie car la dernière partie du tuyau dans ces coquilles a été exécutée dans une coque de courant au lieu de sous-espaces. La déclaration de retour a affecté l'environnement de shell actuel qui a appelé la fonction, car la fonction retour immédiatement sans rien imprimer.
En session interactive, bash
_ ne signalez que l'erreur mais n'a pas terminé la coquille, schily's osh
a rapporté l'erreur et terminé la coquille:
$ for s in /bin/*sh; do printf '<%s>\n' $s; $s -ci 'return 1; echo 1'; done
</bin/bash>
bash: return: can only `return' from a function or sourced script
1
</bin/dash>
</bin/ksh>
</bin/lksh>
</bin/mksh>
</bin/pdksh>
</bin/posh>
</bin/sh>
</bin/yash>
</bin/zsh>
</opt/schily/bin/osh>
$ cannot return when not in function
(zsh
_ en session interactive et sortie est terminal Ne pas terminer, bash
, yash
et schily's osh
signalé l'erreur mais n'a pas terminé la coquille)
Je pense que vous avez obtenu le comportement attendu, à bash, chaque commande dans un pipeline est exécutée dans un sous-groupe. Vous pouvez vous convaincre en essayant de modifier une variable globale de votre fonction:
foo(){ x=42; : | x=3; echo "x==$x";}
Au fait, le retour fonctionne mais cela revient du sous-groupe. Encore une fois, vous pouvez vérifier cela:
foo(){ : | return 1; echo$?; echo "This should not be printed.";}
Va émettre ce qui suit:
1
This should not be printed.
Donc déclaration de retour correctement quitté le sous-groupe
.
La réponse plus générale est que Bash et d'autres coquilles mettent normalement tous les éléments d'un pipeline dans des processus distincts. Ceci est raisonnable lorsque la ligne de commande est
programme1 | programme2 | programme3
étant donné que les programmes sont normalement exécutés dans des processus distincts de toute façon (sauf si vous dites exec program
). Mais cela peut être une surprise pour
commander1 | commander2 | commander3
où certaines ou toutes les commandes sont des commandes intégrées. Les exemples triviaux incluent:
$ a=0
$ echo | a=1
$ echo "$a"
0
$ cd /
$ echo | cd /tmp
$ pwd
/
Un exemple légèrement plus réaliste est
$ t=0
$ ps | while read pid rest_of_line
> do
> : $((t+=pid))
> done
$ echo "$t"
0
où l'ensemble while
... _ ... do
... done
boucle est placée dans une sous-processus, de sorte que ses modifications apportées à t
ne sont pas visibles à la coque principale après la boucle se termine. Et c'est exactement ce que vous faites - la tuyauterie dans une boucle while
, ce qui oblige la boucle à exécuter en tant que sous-(-n-shell, puis essayant de revenir du sous-groupe.