Passer toutes les variables d'un script shell à un autre?

194

Disons que j'ai un script shell / bash nommé test.shavec:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Mon test2.shressemble à ceci:

#!/bin/bash

echo ${TESTVARIABLE}

Cela ne fonctionne pas. Je ne veux pas passer toutes les variables comme paramètres car à mon humble avis, c'est exagéré.

Y a-t-il une manière différente?

Toskan
la source
Une idée consiste à enregistrer les variables dans un fichier, puis à les charger dans un autre script.
Rodrigo
@Rodrigo J'ai déjà utilisé une approche similaire pour "enregistrer" les valeurs entre les exécutions d'un script avec un certain succès. Juste une chose à garder à l'esprit, c'est que si plusieurs instances des scripts s'exécutent, les choses peuvent devenir risquées. Mais si vous les enregistrez sous forme d'affectation bash, vous pouvez simplement rechercher le fichier pour importer les variables
FatalError

Réponses:

267

Vous avez essentiellement deux options:

  1. Faites de la variable une variable d'environnement ( export TESTVARIABLE) avant d'exécuter le 2ème script.
  2. Source le 2ème script, c'est-à . test2.sh- dire et il fonctionnera dans le même shell. Cela vous permettrait de partager facilement des variables plus complexes telles que des tableaux, mais cela signifie également que l'autre script pourrait modifier des variables dans le shell source.

METTRE À JOUR:

Pour utiliser exportpour définir une variable d'environnement, vous pouvez soit utiliser une variable existante:

A=10
# ...
export A

Cela devrait fonctionner à la fois dans bashet sh. bashpermet également de le combiner comme ceci:

export A=10

Cela fonctionne également dans my sh (ce qui se trouve être bash, vous pouvez utiliser echo $SHELLpour vérifier). Mais je ne pense pas que cela fonctionne dans tous sh, alors mieux vaut jouer la sécurité et les séparer.

Toute variable que vous exportez de cette manière sera visible dans les scripts que vous exécutez, par exemple:

cendre:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Ensuite:

$ ./a.sh
The message is: hello

Le fait que ce soient tous les deux des scripts shell est également accessoire. Les variables d'environnement peuvent être passées à n'importe quel processus que vous exécutez, par exemple si nous avons utilisé python à la place, cela pourrait ressembler à:

cendre:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.py

b.py:

#!/usr/bin/python

import os

print 'The message is:', os.environ['MESSAGE']

Approvisionnement:

Au lieu de cela, nous pourrions nous approvisionner comme ceci:

cendre:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Ensuite:

$ ./a.sh
The message is: hello

Cela "importe" plus ou moins b.shdirectement le contenu de et l'exécute dans le même shell . Notez que nous n'avons pas eu à exporter la variable pour y accéder. Cela partage implicitement toutes les variables que vous avez, ainsi que permet à l'autre script d'ajouter / supprimer / modifier des variables dans le shell. Bien sûr, dans ce modèle, vos deux scripts doivent être dans la même langue ( shou bash). Pour donner un exemple de la façon dont nous pourrions passer des messages dans les deux sens:

cendre:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

echo "[A] The message is: $MESSAGE"

b.sh:

#!/bin/sh

echo "[B] The message is: $MESSAGE"

MESSAGE="goodbye"

Ensuite:

$ ./a.sh
[B] The message is: hello
[A] The message is: goodbye

Cela fonctionne aussi bien dans bash. Cela facilite également le partage de données plus complexes que vous ne pourriez pas exprimer en tant que variable d'environnement (du moins sans une lourde charge de votre part), comme des tableaux ou des tableaux associatifs.

Erreur fatale
la source
1
Que faire si je dois passer $ 1 au sous-shell (parce que 'sudo sh -c ...' est appelé à partir d'un script)? Dois-je pousser $ 1 dans une variable d'environnement, l'exporter et utiliser la variable dans la commande?
Urhixidur
Juste pour ajouter que si vous avez besoin des droits d'accès sudo, vous pouvez utiliser sudo -E ... pour préserver les variables d'environnement.
yucer
2
@FatalError Pouvez-vous s'il vous plaît expliquer la magie dans le dernier a.sh, où vous appelez ". ./B.sh". Quelle est la signification du premier point? Cela fonctionne très bien d'ailleurs!
Deian
vous pouvez également définir les variables et les valeurs dans un fichier et les partager de cette façon
newshorts
@Deian, La période (point) est une main courte pour la bash intégrée "source"
Kirill Kost
27

Fatal Error a donné une possibilité simple: source votre deuxième script! si vous craignez que ce second script puisse altérer certaines de vos précieuses variables, vous pouvez toujours le rechercher dans un sous-shell:

( . ./test2.sh )

Les parenthèses feront apparaître la source dans un sous-shell, de sorte que le shell parent ne verra pas les modifications test2.shpossibles.


Il y a une autre possibilité qui devrait certainement être référencée ici: use set -a.

De la référence POSIXset :

-a: Lorsque cette option est activée, l' attribut d' exportation doit être défini pour chaque variable à laquelle une affectation est effectuée; voir le volume Définitions de base de la norme IEEE 1003.1-2001, Section 4.21, Affectation des variables . Si l'affectation précède un nom d'utilitaire dans une commande, l' attribut d' exportation ne doit pas persister dans l'environnement d'exécution actuel une fois l'utilitaire terminé, à l'exception du fait que précéder l'un des utilitaires intégrés spéciaux entraîne la persistance de l'attribut d' exportation après la en a terminé. Si l'affectation ne précède pas un nom d'utilitaire dans la commande, ou si l'affectation est le résultat de l'opération de getopts ou read utilitaires, l'attribut d'exportation persistera jusqu'à ce que la variable ne soit pas définie.

À partir du manuel Bash :

-a: Marquez les variables et la fonction qui sont modifiées ou créées pour l'exportation vers l'environnement des commandes suivantes.

Donc dans votre cas:

set -a
TESTVARIABLE=hellohelloheloo
# ...
# Here put all the variables that will be marked for export
# and that will be available from within test2 (and all other commands).
# If test2 modifies the variables, the modifications will never be
# seen in the present script!
set +a

./test2.sh

 # Here, even if test2 modifies TESTVARIABLE, you'll still have
 # TESTVARIABLE=hellohelloheloo

Notez que les spécifications spécifient uniquement que set -ala variable est marquée pour l'exportation. C'est:

set -a
a=b
set +a
a=c
bash -c 'echo "$a"'

fera écho cet non une ligne vide ni b(c'est-à-dire set +aqu'il ne désélectionne pas pour l'exportation, ni ne «sauvegarde» la valeur de l'affectation uniquement pour l'environnement exporté). C'est, bien entendu, le comportement le plus naturel.

Conclusion: utiliser set -a/ set +apeut être moins fastidieux que d'exporter manuellement toutes les variables. Il est supérieur à l'approvisionnement du deuxième script, car il fonctionnera pour n'importe quelle commande, pas seulement celles écrites dans le même langage shell.

gniourf_gniourf
la source
18

Il existe en fait un moyen plus simple que d'exporter et de désarmer ou de réapprovisionner (au moins dans bash, tant que vous êtes d'accord pour passer les variables d'environnement manuellement):

laissez a.sh être

#!/bin/bash
secret="winkle my tinkle"
echo Yo, lemme tell you \"$secret\", b.sh!
Message=$secret ./b.sh

et b.sh être

#!/bin/bash
echo I heard \"$Message\", yo

La sortie observée est

[rob @ test Archie] $ ./a.sh
Yo, laissez-moi vous dire "winkle my tinkle", b.sh!
J'ai entendu "winkle my tinkle", yo

La magie réside dans la dernière ligne a.sh, où Message, pour seulement la durée de l'invocation de ./b.sh, est réglé sur la valeur secretde a.sh. En gros, c'est un peu comme des paramètres / arguments nommés. De plus, cela fonctionne même pour des variables telles que $DISPLAY, qui contrôle le serveur X dans lequel une application démarre.

N'oubliez pas que la longueur de la liste des variables d'environnement n'est pas infinie. Sur mon système avec un noyau relativement vanille, xargs --show-limitsme dit que la taille maximale du tampon d'arguments est de 2094486 octets. Théoriquement, vous utilisez mal les scripts shell si vos données sont plus grandes que cela (tuyaux, n'importe qui?)

frappant
la source
7

Ajoutant à la réponse d'erreur fatale, il existe une autre façon de passer les variables à un autre script shell.

La solution suggérée ci-dessus présente certains inconvénients:

  1. using Export : Cela entraînera la présence de la variable hors de sa portée, ce qui n'est pas une bonne pratique de conception.
  2. using Source : Cela peut provoquer des conflits de noms ou un écrasement accidentel d'une variable prédéfinie dans un autre fichier de script shell qui a généré un autre fichier.

Il existe une autre solution simple que nous pouvons utiliser. Compte tenu de l'exemple que vous avez posté,

test.sh

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh "$TESTVARIABLE"

test2.sh

#!/bin/bash

echo $1

production

hellohelloheloo

Il est également important de noter que ""sont nécessaires si nous passons des chaînes de mots multiples. Prenons un autre exemple

master.sh

#!/bin/bash
echo in master.sh
var1="hello world"
sh slave1.sh $var1
sh slave2.sh "$var1"
echo back to master

slave1.sh

#!/bin/bash
echo in slave1.sh
echo value :$1

slave2.sh

#!/bin/bash
echo in slave2.sh
echo value : $1

production

in master.sh
in slave1.sh
value :"hello
in slave2.sh
value :"hello world"

Cela se produit pour les raisons bien décrites dans ce lien

Subham Tripathi
la source
6
l'utilisation sourceest en fait une bonne chose. Concernant votre souci: écrasement accidentel d'une variable prédéfinie , vous pouvez toujours vous procurer dans un sous-shell. Cela résout complètement le problème.
gniourf_gniourf
@gniourf_gniourf comment se procurer dans un sous-shell?
Toskan
@Toskan Ceci est mentionné dans ma réponse: ( . ./test2.sh ). Les parenthèses obligeront Bash à exécuter son contenu dans un sous-shell.
gniourf_gniourf
7

Dans Bash, si vous exportez la variable dans un sous-shell, en utilisant les parenthèses comme indiqué, vous évitez de divulguer les variables exportées:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
(
export TESTVARIABLE    
source ./test2.sh
)

L'avantage ici est qu'après avoir exécuté le script à partir de la ligne de commande, vous ne verrez pas un $ TESTVARIABLE filtré dans votre environnement:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
la source
Je l'ai utilisé mais cela ne semble pas fonctionner - j'ai écrit: #! / Bin / bash for ((i = 1; i <= 3; i ++)) do (export i source ./script2.sh) done ERROR it dit: ./script2.sh: Aucun fichier ou répertoire de ce type
Pranjal Gupta
Le sous-shell n'est pas réellement nécessaire car l' environnement de niveau supérieur (l' $invite de ligne de commande ) ne verra jamais le fichier exporté $TESTVARIABLE. L'exportation transmet simplement une copie d'une variable à tous les processus enfants suivants . Il n'est pas possible de transmettre des variables de sauvegarde de la chaîne aux processus parents, sauf en enregistrant la valeur dans le stockage et en lisant ce stockage ultérieurement dans le script de processus parent. Le transfert vers une deuxième copie du script parent est possible, mais ce ne serait pas le même processus . Une excellente explication peut être trouvée ici .
DocSalvager
2

Une autre option utilise eval. Cela ne convient que si les chaînes sont approuvées. Le premier script peut faire écho aux affectations de variables:

echo "VAR=myvalue"

Ensuite:

eval $(./first.sh) ./second.sh

Cette approche est particulièrement intéressante lorsque le second script pour lequel vous souhaitez définir des variables d'environnement n'est pas dans bash et que vous ne voulez pas non plus exportles variables, peut-être parce qu'elles sont sensibles et que vous ne voulez pas qu'elles persistent.

Mark Stosberg
la source
1

Une autre façon, qui est un peu plus simple pour moi, consiste à utiliser des tubes nommés. Les canaux nommés fournissaient un moyen de synchroniser et d'envoyer des messages entre différents processus.

Rendre confus:

#!/bin/bash
msg="The Message"
echo $msg > A.pipe

B.bash:

#!/bin/bash
msg=`cat ./A.pipe`
echo "message from A : $msg"

Usage:

$ mkfifo A.pipe #You have to create it once
$ ./A.bash & ./B.bash # you have to run your scripts at the same time

B.bash attendra le message et dès que A.bash enverra le message, B.bash continuera son travail.

Pejman Habashi
la source