2010-11-23 9 views
9

Voici un script shell:Bash: variable globale incrément

globvar=0 

function myfunc { 
    let globvar=globvar+1 
    echo "myfunc: $globvar" 
} 

myfunc 
echo "something" | myfunc 

echo "Global: $globvar" 

Quand appelé, il imprime les suivantes:

$ sh zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 1 
$ bash zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 1 
$ zsh zzz.sh 
myfunc: 1 
myfunc: 2 
Global: 2 

La question est: pourquoi cela se produit et ce comportement est correct?

P.S. J'ai un sentiment étrange que la fonction derrière le tuyau est appelée dans une coquille fourchue ... Alors, peut-il y avoir une solution de contournement simple?

P.P.S. Cette fonction est un wrapper de test simple. Il exécute une application de test et analyse sa sortie. Ensuite, il incrémente les variables $ PASSED ou $ FAILED. Enfin, vous obtenez un certain nombre de tests réussis/échoués dans les variables globales. L'utilisation est comme:

test-util << EOF | myfunc 
input for test #1 
EOF 
test-util << EOF | myfunc 
input for test #2 
EOF 
echo "Passed: $PASSED, failed: $FAILED" 

Merci, Serge

+0

Avez-vous lu: http://tldp.org/LDP/abs/html/localvar.html? –

+0

Eh bien, à mon avis $ globvar n'est pas une variable locale, car l'appel de myfunc() sans un tube incrémente assez bien la variable globale $ globvar. Le problème est que la fonction d'appel sur le canal ne fonctionne pas dans bash/sh. – zserge

Répondre

7

shell Korn donne les mêmes résultats comme zsh, au fait.

Veuillez consulter BashFAQ/024. Les tuyaux créent des sous-couches dans Bash et les variables sont perdues quand les sous-couches sortent.

Sur la base de votre exemple, je restructurer quelque chose comme ceci:

globvar=0 

function myfunc { 
    echo $(($1 + 1)) 
} 

myfunc "$globvar" 
globalvar=$(echo "something" | myfunc "$globalvar") 
+0

Merci pour le lien! La question est de savoir comment repasser deux variables: $ PASSED et $ FAILED. Je comprends, que je peux rechercher $ ?, si elle est égale à zéro - incrémenter $ PASSED par le mien, sinon - incrément $ FAILED. Je voudrais juste aussi peu de code que possible en dehors de myfunc(). – zserge

+0

@zserge: Une façon serait la suivante: 'myfunc() {echo" deux mots "; } read word1 word2 <<< $ (myfunc) 'ou' array = ($ (myfunc)) '. –

3

de quelque chose de tuyauterie en myfunc dans sh ou bash provoque un nouveau shell pour se reproduire. Vous pouvez le confirmer en ajoutant un long sommeil dans myfunc. Pendant qu'il dort, appelez ps et vous verrez un sous-processus. Lorsque la fonction renvoie, ce sous-shell se ferme sans modifier la valeur dans le processus parent.

Si vous avez vraiment besoin de cette valeur à modifier, vous aurez besoin de retourner une valeur de la fonction et vérifier $ PIPESTATUS après, je suppose, comme ceci:

globvar=0 

function myfunc { 
    let globvar=globvar+1 
    echo "myfunc: $globvar" 
    return $globvar 
} 

myfunc 
echo "something" | myfunc 
globvar=${PIPESTATUS[1]} 

echo "Global: $globvar" 
+0

Cool. Je ne connaissais pas 'PIPESTATUS' avant. – dennycrane

+0

Le seul problème avec cette technique est que votre valeur de retour doit être comprise entre 0 et 255. –

0

Utilisation export au lieu de let, sinon la variable est locale (utilisez $ (()) et faire opération arithmétique)

export globvar=0 

function myfunc { 
    export globvar=$((globvar+1)) 
    echo "myfunc: $globvar" 
} 
+0

mais les variables exportées ne retournent pas dans le shell de l'appelant, donc si vous appelez votre fonction sur le tuyau, votre variable globale restera inchangée (testé sur bash) – zserge

+0

mon erreur, je n'ai pas vu le | problème. Pourquoi avez-vous besoin d'appeler un nouveau shell plutôt que d'appeler simplement le script? – mb14

+0

En fait, je ne le fais pas. J'ai juste besoin de rediriger une sortie du programme de test vers la fonction shell pour savoir si le test a réussi. Un moyen clair de rediriger la sortie est un tube, mais le tube forge une sous-coque. – zserge

0

Le problème est «quelle extrémité d'un pipeline utilisant des composants intégrés est exécutée par le processus d'origine?

Dans zsh, il semble que la dernière commande du pipeline soit exécutée par le script shell principal lorsque la commande est une fonction ou intégrée. Dans Bash (et sh est susceptible d'être un lien vers Bash si vous êtes sur Linux), alors les deux commandes sont exécutées dans un sous-shell ou la première commande est exécutée par le processus principal et les autres sont géré par des sous-coquilles.

Clairement, lorsque la fonction est exécutée dans un sous-shell, elle n'affecte pas la variable dans le shell parent (uniquement le global dans la sous-shell).

un test Pensez à ajouter supplémentaire:

echo Something | { myfunc; echo $globvar; } 
echo $globvar