web-dev-qa-db-fra.com

Définir une variable Shell parent à partir d'un sous-shell

Comment définir une variable dans le shell parent, à partir d'un sous-shell?

a=3
(a=4)
echo $a
33
Matt Joiner

L'intérêt d'un sous-shell est qu'il n'a pas n'affecte pas la session appelante. Dans bash, un sous-shell est un processus enfant, les autres shells diffèrent, mais même dans ce cas, un paramètre variable dans un sous-shell n'affecte pas l'appelant. Par définition.

Avez-vous besoin d'un sous-shell? Si vous avez juste besoin d'un groupe, utilisez des accolades:

a=3
{ a=4;}
echo $a

donne 4 (faites attention aux espaces dans celui-là). Sinon, écrivez la valeur de la variable sur stdout et capturez-la dans l'appelant:

a=3
a=$(a=4;echo $a)
echo $a

évitez d’utiliser les guillemets arrière ``, ils sont obsolètes et peuvent être difficiles à lire. 

41
cdarke

Il y a le hack gdb-bash-variable:

gdb --batch-silent -ex "attach $$" -ex 'set bind_variable("a", "4", 0)'; 

bien que cela définisse toujours une variable dans la portée globale, pas seulement la portée parente

28
BeniBela

Vous pas. Le sous-shell n'a pas accès à l'environnement de son parent. (Au moins dans l'abstraction fournie par Bash. Vous pourriez potentiellement essayer d'utiliser gdb, ou écraser la pile, ou quoi, pour obtenir un tel accès clandestinement. Je ne recommanderais cependant pas cela.)

Une solution consiste pour le sous-shell à écrire des instructions d'affectation dans un fichier temporaire afin que son parent puisse lire:

a=3
(echo 'a=4' > tmp)
. tmp
rm tmp
echo "$a"
20
ruakh

Si le problème est lié à une boucle while, vous pouvez résoudre ce problème en utilisant Substitution de processus:

    var=0
    while read i;
    do
      # perform computations on $i
      ((var++))
    done < <(find . -type f -name "*.bin" -maxdepth 1)

comme indiqué ici: https://stackoverflow.com/a/13727116/2547445

9
marioquark

Pour modifier les variables dans un script appelé à partir d'un script parent, vous pouvez appeler le script précédé d'un "."

a=3
echo $a    
. ./calledScript.sh
echo $a

en appeléScript.sh

a=4

Production attendue

3
4
5
Carl Bosch

Vous pouvez sortir la valeur dans le sous-shell et affecter la sortie du sous-shell à une variable du script de l'appelant:

# subshell.sh
echo Value

# caller
myvar=$(subshell.sh)

Si le sous-shell a plus à produire, vous pouvez séparer la valeur de la variable et les autres messages en les redirigeant vers différents flux de sortie:

# subshell.sh
echo "Writing value" 1>&2
echo Value

# caller
myvar=$(subshell.sh 2>/dev/null) # or to somewhere else
echo $myvar

Vous pouvez également générer des affectations de variables dans le sous-shell, les évaluer dans le script de l'appelant et éviter d'utiliser des fichiers pour échanger des informations:

# subshell.sh
echo "a=4"

# caller
# export $(subshell.sh) would be more secure, since export accepts name=value only.
eval $(subshell.sh)
echo $a

La dernière façon dont je peux penser est d’utiliser des codes de sortie, mais elle ne concerne que l’échange de valeurs entières (dans une plage limitée) et rompt la convention d’interprétation des codes de sortie (0 pour succès non-0 pour tout le reste).

4
khachik

J'aime le concept de liaison tardive. Le lancement du sous-shell persistant de Bash via le $ (substitution de commande) le plus utile est presque une raison suffisante pour changer de langue. 

Les fichiers, cependant, sont les variables globales de bash ...

Créez une fonction set/get/default comme ceci:

globalVariable() { # NEW-VALUE
    # set/get/default globalVariable
    if [ 0 = "$#" ]; then
        # new value not given -- echo the value
        [ -e "$aRam/globalVariable" ] \
            && cat "$aRam/globalVariable" \
            || printf "default-value-here"
    else
        # new value given -- set the value
        printf "%s" "$1" > "$aRam/globalVariable"
    fi
}

"$ aRam" est le répertoire dans lequel les valeurs sont stockées. J'aime que ce soit un disque virtuel pour la vitesse et la volatilité:

aRam="$(mktemp -td $(basename "$0").XXX)" # temporary directory
mount -t tmpfs ramdisk "$aRam" # mount the ram disk there
trap "umount "$aRam" && rm -rf "$aRam"" EXIT # auto-eject

Pour lire la valeur:

v="$(globalVariable)" # or part of any command

Pour définir la valeur:

globalVariable newValue # newValue will be written to file

Pour annuler la valeur:

rm -f "$aRam/globalVariable"

La seule raison réelle pour la fonction d’accès est d’appliquer une valeur par défaut car cat provoquera une erreur si un fichier est inexistant. Il est également utile d'appliquer une autre logique get/set. Sinon, cela ne serait pas du tout nécessaire.

Une méthode de lecture laide évitant l'erreur de fichier inexistante de chat:

v="$(cat "$aRam/globalVariable 2>/dev/null")"

Une fonctionnalité intéressante de ce désordre est que vous pouvez ouvrir un autre terminal et examiner le contenu des fichiers pendant l'exécution du programme.

0
Carmin