Passar todas as variáveis ​​de um script de shell para outro?

192

Digamos que eu tenho um script shell / bash nomeado test.shcom:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Minha test2.shaparência é assim:

#!/bin/bash

echo ${TESTVARIABLE}

Isso não funciona. Eu não quero passar todas as variáveis ​​como parâmetros, porque imho isso é um exagero.

Existe uma maneira diferente?

Toskan
fonte
Uma idéia é salvar as variáveis ​​em um arquivo e carregar em outro script.
19312 Rodrigo
@ Rodrigo Eu já usei uma abordagem semelhante antes para "salvar" valores entre execuções de um script com algum sucesso. Apenas uma coisa a ter em mente é que, se várias instâncias dos scripts forem executadas, as coisas podem ficar arriscadas. Mas se você salvá-los em forma de atribuição bash, você pode apenas fonte o arquivo para importar as variáveis
FatalError

Respostas:

264

Você tem basicamente duas opções:

  1. Torne a variável uma variável de ambiente ( export TESTVARIABLE) antes de executar o segundo script.
  2. Crie o 2º script, ou seja, . test2.she ele será executado no mesmo shell. Isso permitiria compartilhar facilmente variáveis ​​mais complexas, como matrizes, mas também significa que o outro script poderia modificar variáveis ​​no shell de origem.

ATUALIZAR:

Para usar exportpara definir uma variável de ambiente, você pode usar uma variável existente:

A=10
# ...
export A

Isso deve funcionar em ambos bashe sh. bashtambém permite que ele seja combinado da seguinte maneira:

export A=10

Isso também funciona no meu sh (que por acaso é bash, você pode usar echo $SHELLpara verificar). Mas eu não acredito que isso funcione de maneira garantida sh, então é melhor jogar com segurança e separá-los.

Qualquer variável que você exportar dessa maneira será visível nos scripts executados, por exemplo:

cinza:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Então:

$ ./a.sh
The message is: hello

O fato de que esses dois scripts de shell também é incidental. As variáveis ​​de ambiente podem ser passadas para qualquer processo que você executa, por exemplo, se usamos o python, pode parecer com:

cinza:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Abastecimento:

Em vez disso, poderíamos fonte como esta:

cinza:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Então:

$ ./a.sh
The message is: hello

Isso "importa" mais ou menos b.shdiretamente o conteúdo e o executa no mesmo shell . Observe que não precisamos exportar a variável para acessá-la. Isso compartilha implicitamente todas as variáveis ​​que você possui, além de permitir que o outro script adicione / exclua / modifique variáveis ​​no shell. Obviamente, nesse modelo, os dois scripts devem ter o mesmo idioma ( shou bash). Para dar um exemplo de como podemos passar mensagens para frente e para trás:

cinza:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Então:

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

Isso funciona igualmente bem bash. Também facilita o compartilhamento de dados mais complexos que você não pode expressar como uma variável de ambiente (pelo menos sem muita sobrecarga de sua parte), como matrizes ou matrizes associativas.

Erro fatal
fonte
1
E se eu precisar passar $ 1 para o sub-shell (porque 'sudo sh -c ...' é chamado de um script)? Devo inserir $ 1 em uma variável de ambiente, exportá-la e usar a variável no comando?
Urhixidur
Apenas para adicionar que, se você precisar de direitos de acesso ao sudo, poderá usar o sudo -E ... para preservar as variáveis ​​de ambiente.
yucer
2
@FatalError Você pode explicar a mágica no último a.sh, onde você chama ". ./B.sh". Qual é o significado do primeiro ponto? Funciona muito bem!
Deian
você também pode definir as variáveis e valores para um arquivo e compartilhá-los dessa forma
newshorts
@Deian, o período (ponto) é a mão curta para a festança construída em "fonte"
Kirill Kost
27

Erro fatal deu uma possibilidade direta: forneça seu segundo script! se você está preocupado que esse segundo script possa alterar algumas de suas variáveis ​​preciosas, você sempre pode fonte-lo em uma subshell:

( . ./test2.sh )

Os parênteses farão com que a fonte aconteça em um subshell, para que o shell pai não veja que as modificações test2.shpoderiam ser executadas.


Há outra possibilidade que definitivamente deve ser referenciada aqui: use set -a .

Da referência POSIXset :

-a: Quando esta opção está ativada , o atributo de exportação deve ser definido para cada variável para a qual uma atribuição é executada; consulte o volume Definições básicas da IEEE Std 1003.1-2001, Seção 4.21, Atribuição de variáveis . Se a atribuição preceder um nome de utilitário em um comando, o atributo de exportação não persistirá no ambiente de execução atual após a conclusão do utilitário, com a exceção de que a precedência de um dos utilitários internos especiais faz com que o atributo de exportação persista após a instalação. em foi concluído. Se a atribuição não preceder um nome de utilitário no comando, ou se a atribuição for resultado da operação dos getopts ou read utilitários, o atributo export deve persistir até que a variável seja desabilitada.

No Manual do Bash :

-a: Marque variáveis ​​e funções modificadas ou criadas para exportar para o ambiente de comandos subseqüentes.

Então, no seu 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 as especificações especificam apenas que set -aa variável está marcada para exportação. Isso é:

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

ecoará ce não uma linha vazia nem b(isto é, set +anão desmarca para exportação, nem "salva" o valor da atribuição apenas para o ambiente exportado). Este é, obviamente, o comportamento mais natural.

Conclusão: usar set -a/ set +apode ser menos tedioso do que exportar manualmente todas as variáveis. É superior ao fornecimento do segundo script, pois funcionará para qualquer comando, não apenas para os escritos na mesma linguagem shell.

gniourf_gniourf
fonte
18

Na verdade, existe uma maneira mais fácil do que exportar e desconfigurar ou buscar novamente (pelo menos no bash, desde que você esteja bem em passar as variáveis ​​de ambiente manualmente):

seja a.sh

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

e b.sh seja

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

A saída observada é

[rob @ Archie test] $ ./a.sh
Yo, deixa eu te dizer "pisca meu toque", b.sh!
Eu ouvi "piscadela minha tilintar", yo

As mentiras mágicas na última linha a.sh, onde Message, apenas para a duração da invocação ./b.sh, é definido para o valor de secretpartir a.sh. Basicamente, é um pouco como parâmetros / argumentos nomeados. Mais do que isso, porém, funciona até para variáveis ​​como$DISPLAY , que controla em qual X Server um aplicativo inicia.

Lembre-se, o comprimento da lista de variáveis ​​de ambiente não é infinito. No meu sistema com um kernel relativamente baunilha, xargs --show-limitsdiz-me que o tamanho máximo do buffer de argumentos é 2094486 bytes. Teoricamente, você está usando scripts de shell errados se seus dados forem maiores que isso (canais, alguém?)

impressionante
fonte
7

Adicionando à resposta de Erro fatal, há mais uma maneira de passar as variáveis ​​para outro script de shell.

A solução sugerida acima tem algumas desvantagens:

  1. using Export : Fará com que a variável esteja presente fora do escopo, o que não é uma boa prática de design.
  2. using Source : Pode causar colisões de nomes ou substituição acidental de uma variável predefinida em algum outro arquivo de script de shell que originou outro arquivo.

Existe outra solução simples disponível para nós usarmos. Considerando o exemplo postado por você,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

resultado

hellohelloheloo

Também é importante observar que ""são necessários se passarmos seqüências de várias palavras. Tomando mais um exemplo

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

resultado

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

Isso acontece devido aos motivos descritos adequadamente neste link

Subham Tripathi
fonte
6
usar sourceé realmente uma coisa boa. Em relação à sua preocupação: sobrescrever acidentalmente uma variável predefinida , você sempre pode procurar em uma subshell. Isso resolve completamente o problema.
gniourf_gniourf
@gniourf_gniourf como fazer a origem em um subshell?
Toskan 13/02/15
@Toskan Isso é mencionado na minha resposta: ( . ./test2.sh ). Os parênteses farão com que o Bash execute seu conteúdo em uma subshell.
Gnourf_gniourf
7

No Bash, se você exportar a variável dentro de um subshell, usando parênteses, como mostrado, evite vazar as variáveis ​​exportadas:

#!/bin/bash

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

A vantagem aqui é que, depois de executar o script na linha de comando, você não verá um $ TESTVARIABLE vazado em seu ambiente:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
fonte
Eu usei, mas ele parece não funcionar - eu escrevi: #! / Bin / bash para ((i = 1; i <= 3; i ++)) do (exportar fonte i ./script2.sh) done ERROR it diz: ./script2.sh:Neste arquivo ou diretório
Pranjal Gupta
O subshell não é realmente necessário, pois o ambiente de nível superior (o $prompt da linha de comandos) nunca verá os exportados $TESTVARIABLE. Exportar apenas passa uma cópia de uma variável para qualquer processo filho subsequente . Não é possível passar variáveis ​​de backup da cadeia para processos pai, exceto salvando o valor no armazenamento e lendo esse armazenamento posteriormente no script do processo pai. É possível canalizar para uma segunda cópia do script pai, mas esse não seria o mesmo processo . Uma excelente explicação pode ser encontrada aqui .
DocSalvager 16/02
2

Outra opção está usando eval. Isso é adequado apenas se as strings forem confiáveis. O primeiro script pode ecoar as atribuições de variáveis:

echo "VAR=myvalue"

Então:

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

Essa abordagem é de particular interesse quando o segundo script para o qual você deseja definir variáveis ​​de ambiente é não no bash e você também não deseja exportas variáveis, talvez porque sejam sensíveis e você não queira que elas persistam.

Mark Stosberg
fonte
1

Outra maneira, que é um pouco mais fácil para mim, é usar pipes nomeados. Os pipes nomeados forneceram uma maneira de sincronizar e enviar mensagens entre diferentes processos.

A.bash:

#!/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

O B.bash aguardará a mensagem e, assim que o A.bash enviar a mensagem, o B.bash continuará seu trabalho.

Pejman Habashi
fonte