¿Pasar todas las variables de un script de shell a otro?

192

Digamos que tengo un script de shell / bash llamado test.sh :

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Mi test2.shaspecto es este:

#!/bin/bash

echo ${TESTVARIABLE}

Esto no funciona. No quiero pasar todas las variables como parámetros, ya que esto es excesivo.

¿Hay una manera diferente?

Toskan
fuente
Una idea es guardar las variables en un archivo y luego cargarlo en otro script.
Rodrigo
@Rodrigo He usado un enfoque similar antes para "guardar" valores entre ejecuciones de un script con cierto éxito. Solo una cosa a tener en cuenta es que si se ejecutan varias instancias de los scripts, las cosas podrían ponerse peligrosas. Pero si los guarda en forma de asignación de bash, puede obtener el archivo para importar las variables
FatalError

Respuestas:

264

Básicamente tienes dos opciones:

  1. Convierta la variable en una variable de entorno (export TESTVARIABLE ) antes de ejecutar el segundo script.
  2. Fuente del segundo script, es decir, . test2.shy se ejecutará en el mismo shell. Esto le permitiría compartir variables más complejas como matrices fácilmente, pero también significa que el otro script podría modificar las variables en el shell de origen.

ACTUALIZAR:

Usar export para establecer una variable de entorno, puede usar una variable existente:

A=10
# ...
export A

Esto debería funcionar en ambos bashy sh. bashTambién permite que se combine así:

export A=10

Esto también funciona en mi sh (que resulta serbash , puede usar echo $SHELLpara verificar). Pero no creo que esté garantizado que funcione en todos sh, así que lo mejor es ir a lo seguro y separarlos.

Cualquier variable que exporte de esta manera será visible en los scripts que ejecute, por ejemplo:

ceniza:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Luego:

$ ./a.sh
The message is: hello

El hecho de que ambos sean scripts de shell también es incidental. Las variables de entorno se pueden pasar a cualquier proceso que ejecute, por ejemplo, si usamos python en su lugar, podría verse así:

ceniza:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Abastecimiento:

En su lugar, podríamos fuente como esta:

ceniza:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Luego:

$ ./a.sh
The message is: hello

Esto más o menos "importa" el contenido de b.shdirectamente y lo ejecuta en el mismo shell . Tenga en cuenta que no tuvimos que exportar la variable para acceder a ella. Esto comparte implícitamente todas las variables que tiene, así como permite que el otro script agregue / elimine / modifique variables en el shell. Por supuesto, en este modelo, ambos scripts deben ser del mismo idioma ( sho bash). Para dar un ejemplo de cómo podríamos pasar mensajes de un lado a otro:

ceniza:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Luego:

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

Esto funciona igualmente bien en bash. También facilita compartir datos más complejos que no podría expresar como una variable de entorno (al menos sin un poco de trabajo pesado de su parte), como las matrices o las matrices asociativas.

Error fatal
fuente
1
¿Qué sucede si necesito pasar $ 1 al sub-shell (porque 'sudo sh -c ...' se llama desde un script)? ¿Debo meter $ 1 en una variable de entorno, exportar eso y usar la variable en el comando?
Urhixidur
Solo para agregar que si necesita derechos de acceso a sudo, puede usar sudo -E ... para preservar las variables de entorno.
yucer
2
@FatalError ¿Puedes explicar la magia en el último a.sh, donde llamas ". ./B.sh". ¿Cuál es el significado del primer punto? Funciona muy bien por cierto!
Deian
También puede establecer las variables y los valores en un archivo y compartirlos de esa manera
newshorts
@Deian, el punto (punto) es una abreviatura para la fiesta "fuente" construida
Kirill Kost
27

Fatal Error dio una posibilidad directa: ¡busca tu segundo script! Si le preocupa que este segundo script pueda alterar algunas de sus valiosas variables, siempre puede obtenerlo en una subshell:

( . ./test2.sh )

Los paréntesis harán que la fuente suceda en una subshell, de modo que la shell principal no vea que las modificaciones test2.shpodrían realizarse.


Hay otra posibilidad que definitivamente debería mencionarse aquí: uso set -a.

De la referencia POSIXset :

-a: Cuando esta opción está activada, el atributo de exportación se establecerá para cada variable a la que se realiza una asignación; vea el volumen de Definiciones básicas de IEEE Std 1003.1-2001, Sección 4.21, Asignación de variables . Si la asignación precede al nombre de una utilidad en un comando, el atributo de exportación no persistirá en el entorno de ejecución actual después de que la utilidad se complete, con la excepción de que la precedente de una de las utilidades integradas especiales hace que el atributo de exportación persista después de la ejecución. en ha completado. Si la asignación no precede a un nombre de utilidad en el comando, o si la asignación es el resultado de la operación de los getopts o la lectura utilidades, el atributo de exportación persistirá hasta que la variable se desarme.

Del Manual de Bash :

-a: Marque las variables y funciones que se modifican o crean para exportarlas al entorno de los comandos posteriores.

Entonces en tu caso:

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

Observe que las especificaciones solo especifican que con set -ala variable está marcada para exportación. Es decir:

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

hará eco cy no una línea vacía ni b(es decir,set +a no desmarca la exportación, ni "guarda" el valor de la asignación solo para el entorno exportado). Este es, por supuesto, el comportamiento más natural.

Conclusión: usar set -a/ set +apuede ser menos tedioso que exportar manualmente todas las variables. Es superior a la obtención del segundo script, ya que funcionará para cualquier comando, no solo los escritos en el mismo lenguaje de shell.

gniourf_gniourf
fuente
18

En realidad, hay una manera más fácil que exportar y desarmar u obtener de nuevo (al menos en bash, siempre y cuando esté de acuerdo con pasar las variables de entorno manualmente):

deja a.sh ser

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

y b.sh be

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

La salida observada es

[prueba de rob @ Archie] $ ./a.sh
Yo, déjame decirte "guiño mi tintineo", b.sh!
Escuché "guiña mi tintineo", yo

La magia se encuentra en la última línea de a.sh, donde Message, solo por la duración de la invocación de ./b.sh, se establece en el valor de secretfrom a.sh. Básicamente, es un poco como parámetros / argumentos con nombre. Sin embargo, más que eso, incluso funciona para variables como$DISPLAY , que controla en qué servidor X se inicia una aplicación.

Recuerde, la longitud de la lista de variables de entorno no es infinita. En mi sistema con un núcleo relativamente vainilla, xargs --show-limitsme dice que el tamaño máximo del búfer de argumentos es 2094486 bytes. Teóricamente, estás usando scripts de shell incorrectos si tus datos son más grandes que eso (tuberías, ¿alguien?)

sorprendentes
fuente
7

Agregando a la respuesta de Fatal Error, hay una forma más de pasar las variables a otro script de shell.

La solución sugerida anteriormente tiene algunos inconvenientes:

  1. using Export : Causará que la variable esté presente fuera de su alcance, lo que no es una buena práctica de diseño.
  2. using Source : Puede provocar colisiones de nombres o sobrescribir accidentalmente una variable predefinida en algún otro archivo de script de shell que haya originado otro archivo.

Hay otra solución simple disponible para que la usemos. Teniendo en cuenta el ejemplo publicado por usted,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

salida

hellohelloheloo

También es importante tener en cuenta que ""son necesarios si pasamos cadenas de varias palabras. Tomando un ejemplo más

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

salida

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

Ocurre debido a las razones descritas adecuadamente en este enlace.

Subham Tripathi
fuente
66
usar sourcees realmente una buena cosa. Con respecto a su preocupación: la sobrescritura accidental de una variable predefinida , siempre puede obtener una subshell. Eso resuelve completamente el problema.
gniourf_gniourf
@gniourf_gniourf cómo fuente en una subshell?
Toskan
@Toskan Esto se menciona en mi respuesta: ( . ./test2.sh ). Los paréntesis harán que Bash ejecute su contenido en una subshell.
gniourf_gniourf
7

En Bash, si exporta la variable dentro de una subshell, usando paréntesis como se muestra, evitará filtrar las variables exportadas:

#!/bin/bash

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

La ventaja aquí es que después de ejecutar el script desde la línea de comandos, no verá un $ TESTVARIABLE filtrado en su entorno:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
fuente
Lo usé pero no parece funcionar. Escribí: #! / Bin / bash para ((i = 1; i <= 3; i ++)) do (export i source ./script2.sh) done ERROR it dice: ./script2.sh:No
existe
La subshell no es realmente necesaria ya que el entorno de nivel superior (la línea de $comandos) nunca verá el exportado $TESTVARIABLE. Exportar solo pasa una copia de una variable a cualquier proceso secundario posterior . No es posible pasar variables de la cadena a los procesos primarios, excepto guardando el valor en el almacenamiento y leyendo ese almacenamiento más adelante en el script del proceso primario. Es posible pasar a una segunda copia del script principal, pero ese no sería el mismo proceso . Una excelente explicación se puede encontrar aquí .
DocSalvager
2

Otra opción está usando eval. Esto solo es adecuado si las cadenas son confiables. El primer script puede hacer eco de las asignaciones variables:

echo "VAR=myvalue"

Luego:

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

Este enfoque es de particular interés cuando el segundo script para el que desea establecer variables de entorno no está en bash y tampoco desea que exportlas variables, tal vez porque sean sensibles y no quiera que persistan.

Mark Stosberg
fuente
1

Otra forma, que es un poco más fácil para mí es usar tuberías con nombre. Las canalizaciones con nombre proporcionan una forma de sincronizar y enviar mensajes entre diferentes procesos.

Avergonzar:

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

B.bash:

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

Uso:

$ 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 esperará el mensaje y tan pronto como A.bash envíe el mensaje, B.bash continuará su trabajo.

Pejman Habashi
fuente