Passare tutte le variabili da uno script shell a un altro?


194

Diciamo che ho uno script shell / bash chiamato test.shcon:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Il mio test2.shassomiglia a questo:

#!/bin/bash

echo ${TESTVARIABLE}

Questo non funziona. Non voglio passare tutte le variabili come parametri poiché imho questo è eccessivo.

C'è un modo diverso?


Un'idea è salvare le variabili in un file e quindi caricarle in un altro script.
Rodrigo

@Rodrigo Ho già usato un approccio simile per "salvare" i valori tra le esecuzioni di uno script con un certo successo. Una cosa da tenere a mente è che se vengono eseguite più istanze degli script, le cose potrebbero diventare rischiose. Ma se li salvi nel modulo di assegnazione bash, puoi semplicemente eseguire il sorgente del file per importare le variabili
FatalError

Risposte:


267

Hai sostanzialmente due opzioni:

  1. Trasforma la variabile in una variabile d'ambiente ( export TESTVARIABLE) prima di eseguire il secondo script.
  2. Sorgente il 2o script, ovvero . test2.she verrà eseguito nella stessa shell. Ciò consentirebbe di condividere facilmente variabili più complesse come array, ma significa anche che l'altro script potrebbe modificare le variabili nella shell di origine.

AGGIORNARE:

Per utilizzare exportper impostare una variabile di ambiente, è possibile utilizzare una variabile esistente:

A=10
# ...
export A

Questo dovrebbe funzionare in entrambi bashe sh. bashconsente inoltre di combinarlo in questo modo:

export A=10

Questo funziona anche nel mio sh (che sembra essere bash, è possibile utilizzare echo $SHELLper controllare). Ma non credo che sia garantito per funzionare in tutto sh, quindi è meglio giocarci in modo sicuro e separarli.

Qualsiasi variabile esportata in questo modo sarà visibile negli script eseguiti, ad esempio:

cenere:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Poi:

$ ./a.sh
The message is: hello

Il fatto che questi siano entrambi script di shell è anche casuale. Le variabili di ambiente possono essere passate a qualsiasi processo eseguito, ad esempio se utilizzassimo Python invece potrebbe apparire come:

cenere:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Reperimento:

Invece potremmo procurarci in questo modo:

cenere:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Poi:

$ ./a.sh
The message is: hello

Questo più o meno "importa" b.shdirettamente il contenuto e lo esegue nella stessa shell . Si noti che non è stato necessario esportare la variabile per accedervi. Ciò condivide implicitamente tutte le variabili che hai, oltre a consentire agli altri script di aggiungere / eliminare / modificare le variabili nella shell. Naturalmente, in questo modello entrambi gli script dovrebbero avere la stessa lingua ( sho bash). Per fare un esempio di come possiamo passare i messaggi avanti e indietro:

cenere:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Poi:

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

Funziona ugualmente bene in bash. Inoltre, semplifica la condivisione di dati più complessi che non è possibile esprimere come una variabile di ambiente (almeno senza un pesante sollevamento da parte dell'utente), come array o array associativi.


1
Cosa succede se devo passare $ 1 alla sotto-shell (perché "sudo sh -c ..." è chiamato da uno script)? Devo spingere $ 1 in una variabile d'ambiente, esportarla e usare la variabile nel comando?
Urhixidur,

Solo per aggiungere che se hai bisogno dei diritti di accesso sudo puoi usare sudo -E ... per preservare le variabili d'ambiente.
yucer

2
@FatalError Puoi per favore spiegare la magia nell'ultimo a.sh, dove chiami ". ./B.sh". Qual è il significato del primo punto? Funziona benissimo!
Deian,

potresti anche impostare variabili e valori su un file e condividerli in questo modo
newshorts

@Deian, Il punto (punto) è una scorciatoia per il bash incorporato in "source"
Kirill Kost

27

L'errore fatale ha dato una possibilità chiara: procurati la tua seconda sceneggiatura! se sei preoccupato che questo secondo script possa alterare alcune delle tue preziose variabili, puoi sempre procurartelo in una subshell:

( . ./test2.sh )

Le parentesi faranno accadere l'origine in una subshell, in modo che la shell padre non vedrà le modifiche che test2.shpotrebbero essere eseguite.


C'è un'altra possibilità che dovrebbe sicuramente fare riferimento qui: usare set -a.

Dal riferimento POSIXset :

-a: Quando questa opzione è attiva, l' attributo di esportazione deve essere impostato per ogni variabile a cui viene eseguita un'assegnazione; vedere il volume Definizioni di base di IEEE Std 1003.1-2001, Sezione 4.21, Assegnazione variabile . Se l'assegnazione precede un nome di utilità in un comando, l' attributo di esportazione non deve persistere nell'ambiente di esecuzione corrente dopo il completamento dell'utilità, con l'eccezione che la precedente di una delle utilità integrate speciali fa sì che l' attributo di esportazione persista dopo il in è stato completato. Se l'assegnazione non precede un nome di utilità nel comando o se l'assegnazione è il risultato dell'operazione di getopts o di lettura utilità, l'attributo export deve persistere fino a quando la variabile non viene disattivata.

Dal manuale di Bash :

-a: Contrassegna variabili e funzioni che vengono modificate o create per l'esportazione nell'ambiente di comandi successivi.

Quindi nel tuo 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

Si noti che le specifiche specificano solo che con set -ala variabile è contrassegnato per l'esportazione. Questo è:

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

echeggerà ce non una riga vuota né b(ovvero, set +anon deseleziona per l'esportazione, né "salva" il valore dell'assegnazione solo per l'ambiente esportato). Questo è, ovviamente, il comportamento più naturale.

Conclusione: l'utilizzo di set -a/ set +apuò essere meno noioso rispetto all'esportazione manuale di tutte le variabili. È superiore all'approvvigionamento del secondo script, poiché funzionerà con qualsiasi comando, non solo con quelli scritti nella stessa lingua di shell.


18

Esiste in realtà un modo più semplice di esportare, disinserire o approvare di nuovo (almeno in bash, purché tu sia d'accordo con il passaggio manuale delle variabili di ambiente):

lascia che a.sh sia

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

e b.sh essere

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

L'output osservato è

[rob @ Archie test] $ ./a.sh
Yo, lascia che ti dica "ammiccare il mio tintinnio", b.sh!
Ho sentito "ammiccare il mio tintinnio", yo

La magia sta nell'ultima riga di a.sh, dove Message, solo per la durata dell'invocazione di ./b.sh, è impostato il valore di secretda a.sh. Fondamentalmente, è un po 'come parametri / argomenti. Inoltre, funziona anche con variabili simili $DISPLAY, che controllano in quale X Server viene avviata un'applicazione.

Ricorda, la lunghezza dell'elenco delle variabili di ambiente non è infinita. Sul mio sistema con un kernel relativamente vanilla, xargs --show-limitsmi dice che la dimensione massima del buffer degli argomenti è 2094486 byte. Teoricamente, stai usando gli script di shell sbagliati se i tuoi dati sono più grandi di così (pipe, chiunque?)


7

Aggiungendo alla risposta di errore irreversibile, c'è un altro modo per passare le variabili a un altro script di shell.

La soluzione sopra suggerita presenta alcuni inconvenienti:

  1. using Export : Causerà la presenza della variabile fuori dal suo ambito di applicazione, il che non è una buona pratica di progettazione.
  2. using Source : Può causare collisioni di nomi o sovrascrittura accidentale di una variabile predefinita in qualche altro file di script shell che ha originato un altro file.

C'è un'altra semplice soluzione disponibile per noi da usare. Considerando l'esempio pubblicato da te,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

produzione

hellohelloheloo

Inoltre è importante notare che ""sono necessari se passiamo stringhe con più parole. Prendendo un altro esempio

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

produzione

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

Succede a causa dei motivi opportunamente descritti in questo link


6
l'utilizzo sourceè in realtà una buona cosa. Per quanto riguarda la tua preoccupazione: sovrascrittura accidentale di una variabile predefinita , puoi sempre effettuare il source in una subshell. Questo risolve completamente il problema.
gniourf_gniourf,

@gniourf_gniourf come eseguire il source in una subshell?
Toskan,

@Toskan Questo è menzionato nella mia risposta: ( . ./test2.sh ). Le parentesi consentiranno a Bash di eseguire il suo contenuto in una subshell.
gniourf_gniourf,

7

In Bash se esporti la variabile all'interno di una subshell, usando le parentesi come mostrato, eviti di perdere le variabili esportate:

#!/bin/bash

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

Il vantaggio qui è che dopo aver eseguito lo script dalla riga di comando, non vedrai $ TESTVARIABLE trapelato nel tuo ambiente:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$

L'ho usato ma non sembra funzionare- Ho scritto: #! / Bin / bash per ((i = 1; i <= 3; i ++)) do (export i source ./script2.sh) fatto ERRORE dice: ./script2.sh:Nessun file o directory del genere
Pranjal Gupta,

La subshell non è effettivamente necessaria poiché l' ambiente di livello superiore (il $prompt della riga di comando ) non vedrà mai l'esportazione $TESTVARIABLE. L'esportazione passa semplicemente una copia di una variabile a qualsiasi figlio successivo processi . Non è possibile trasferire le variabili di backup della catena ai processi padre se non salvando il valore in memoria e leggendo tale memoria in un secondo momento nello script del processo padre. È possibile eseguire il piping su una seconda copia dello script principale ma non sarebbe lo stesso processo . Una spiegazione eccellente può essere trovata qui .
DocSalvager,

2

Un'altra opzione sta usando eval. Questo è adatto solo se le stringhe sono affidabili. Il primo script può fare eco alle assegnazioni delle variabili:

echo "VAR=myvalue"

Poi:

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

Questo approccio è di particolare interesse quando il secondo script per cui si desidera impostare le variabili di ambiente non è in bash e non si vogliono anche exportle variabili, forse perché sono sensibili e non si desidera che persistano.


1

Un altro modo, che è un po 'più semplice per me, è usare le pipe con nome. Le pipe denominate hanno fornito un modo per sincronizzare e inviare messaggi tra processi diversi.

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

B.bash attenderà il messaggio e non appena A.bash invierà il messaggio, B.bash continuerà a funzionare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.