Czy przekazać wszystkie zmienne z jednego skryptu powłoki do drugiego?

193

Powiedzmy, że mam nazwany skrypt powłoki / bash test.sh z:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Mój test2.shwygląda tak:

#!/bin/bash

echo ${TESTVARIABLE}

To nie działa. Nie chcę przekazywać wszystkich zmiennych jako parametrów, ponieważ imho to przesada.

Czy jest inny sposób?

Toskan
źródło
Jednym z pomysłów jest zapisanie zmiennych w pliku, a następnie załadowanie w innym skrypcie.
Rodrigo
@Rodrigo Użyłem wcześniej podobnego podejścia do „zapisywania” wartości między uruchomieniami skryptu z pewnym powodzeniem. Należy pamiętać tylko o tym, że jeśli uruchomionych zostanie wiele instancji skryptów, sytuacja może się pogorszyć. Ale jeśli zapiszesz je w formularzu przypisania bash, możesz po prostu pobrać plik, aby zaimportować zmienne
FatalError

Odpowiedzi:

266

Masz w zasadzie dwie opcje:

  1. Ustaw zmienną jako zmienną środowiskową ( export TESTVARIABLE) przed uruchomieniem drugiego skryptu.
  2. Źródło drugiego skryptu, tzn . test2.sh. Będzie działał w tej samej powłoce. Umożliwiłoby to łatwe dzielenie się bardziej złożonymi zmiennymi, takimi jak tablice, ale oznacza również, że drugi skrypt może modyfikować zmienne w powłoce źródłowej.

AKTUALIZACJA:

Aby użyć exportdo ustawienia zmiennej środowiskowej, możesz użyć istniejącej zmiennej:

A=10
# ...
export A

To powinno działać w obie bashi sh. bashumożliwia także łączenie go w następujący sposób:

export A=10

Działa to również w moim sh (tak się składa bash, że możesz użyć echo $SHELLdo sprawdzenia). Ale nie wierzę, że to gwarantuje, że w ogóle zadziała sh, więc najlepiej grać bezpiecznie i rozdzielić je.

Każda zmienna eksportowana w ten sposób będzie widoczna w wykonywanych skryptach, na przykład:

popiół:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Następnie:

$ ./a.sh
The message is: hello

Fakt, że są to oba skrypty powłoki, jest również przypadkowy. Zmienne środowiskowe można przekazać do dowolnego wykonywanego procesu, na przykład jeśli zamiast tego użyjemy Pythona, może to wyglądać:

popiół:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Pozyskiwanie:

Zamiast tego moglibyśmy uzyskać takie źródło:

popiół:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Następnie:

$ ./a.sh
The message is: hello

To mniej więcej „importuje” zawartość b.shbezpośrednio i wykonuje ją w tej samej powłoce . Zauważ, że nie musieliśmy eksportować zmiennej, aby uzyskać do niej dostęp. To domyślnie udostępnia wszystkie zmienne, które masz, a także pozwala drugiemu skryptowi dodawać / usuwać / modyfikować zmienne w powłoce. Oczywiście w tym modelu oba skrypty powinny być w tym samym języku ( shlub bash). Aby podać przykład, w jaki sposób możemy przekazywać wiadomości w tę iz powrotem:

popiół:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Następnie:

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

Działa to równie dobrze w bash. Ułatwia także udostępnianie bardziej złożonych danych, których nie można wyrazić jako zmienną środowiskową (przynajmniej bez dużego obciążenia z twojej strony), takich jak tablice lub tablice asocjacyjne.

Błąd krytyczny
źródło
1
Co jeśli muszę przekazać 1 $ do podpowłoki (ponieważ „sudo sh -c ...” jest wywoływane ze skryptu)? Czy muszę wcisnąć 1 $ do zmiennej środowiskowej, wyeksportować ją i użyć tej zmiennej w poleceniu?
Urhixidur
Wystarczy dodać, że jeśli potrzebujesz uprawnień dostępu sudo, możesz użyć sudo -E ... do zachowania zmiennych środowiskowych.
yumper
2
@ FatalError Czy możesz wyjaśnić magię w ostatnim a.sh, w którym nazywasz „./b.sh”. Jakie jest znaczenie pierwszej kropki? Działa świetnie btw!
Deian
możesz także ustawić zmienne i wartości w pliku i udostępnić je w ten sposób
newshorts
@Deian, kropka (kropka) to krótka ręka dla basha wbudowanego w „źródło”
Kirill Kost
27

Błąd krytyczny dał bezpośrednią możliwość: zdobądź swój drugi skrypt! jeśli obawiasz się, że ten drugi skrypt może zmienić niektóre z twoich cennych zmiennych, zawsze możesz go zdobyć w podpowłoce:

( . ./test2.sh )

Nawiasy sprawią, że źródło stanie się w podpowłoce, dzięki czemu powłoka nadrzędna nie zobaczy modyfikacji, które test2.shmogłyby zostać wykonane.


Jest jeszcze jedna możliwość, z którą należy zdecydowanie się tutaj odwołać: use set -a .

Z odniesienia do POSIXset :

-a: Gdy ta opcja jest włączona, atrybut eksportu należy ustawić dla każdej zmiennej, której przypisanie jest wykonywane; patrz tom Definicje podstawowe IEEE Std 1003.1-2001, Rozdział 4.21, Przypisywanie zmiennych . Jeśli przypisanie poprzedza nazwę narzędzia w poleceniu, atrybut eksportu nie powinien pozostać w bieżącym środowisku wykonywania po zakończeniu narzędzia, z wyjątkiem tego, że poprzedzenie jednego ze specjalnych wbudowanych narzędzi powoduje, że atrybut eksportu pozostaje po wbudowanym zakończono. Jeśli przypisanie nie poprzedza nazwy narzędzia w poleceniu lub jeśli przypisanie jest wynikiem operacji getopts lub odczytu narzędzia, atrybut eksportu będzie trwał do momentu rozbrojenia zmiennej.

Z podręcznika Bash :

-a: Zaznacz zmienne i funkcje, które są modyfikowane lub tworzone w celu eksportu do środowiska kolejnych poleceń.

Więc w twoim przypadku:

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

Zauważ, że specyfikacje określają tylko, że set -azmienna jest oznaczona do eksportu. To jest:

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

odbije się echem, ca nie pustą linią ani b(czyliset +a nie odznacza dla eksportu, ani nie „zapisuje” wartości przypisania tylko dla eksportowanego środowiska). Jest to oczywiście najbardziej naturalne zachowanie.

Wniosek: użycie set -a/ set +amoże być mniej uciążliwe niż ręczne eksportowanie wszystkich zmiennych. Jest lepszy niż pozyskiwanie drugiego skryptu, ponieważ będzie działał dla każdego polecenia, nie tylko dla tego napisanego w tym samym języku powłoki.

gniourf_gniourf
źródło
18

Jest tak naprawdę łatwiejszy sposób niż ponowne eksportowanie, rozbrajanie lub pozyskiwanie zasobów (przynajmniej w bash, o ile jesteś w stanie ręcznie przekazać zmienne środowiskowe):

niech a.sh będzie

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

i b.sh be

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

Zaobserwowana moc wyjściowa to

[rob @ Archie test] $ ./a.sh Witam
, powiem ci „winkle my tinkle”, b.sh!
Słyszałem, „mrugnij, mruczę”, yo

Magia leży w ostatnim wierszu a.sh, gdzie Messagetylko na czas trwania wywołania ./b.shjest ustawiona na wartość secretz a.sh. Zasadniczo przypomina trochę nazwane parametry / argumenty. Co więcej, działa nawet w przypadku zmiennych takich jak$DISPLAY , które kontrolują, w którym X serwerze uruchamia się aplikacja.

Pamiętaj, że długość listy zmiennych środowiskowych nie jest nieskończona. W moim systemie ze względnie waniliowym jądrem xargs --show-limitsmówi mi, że maksymalny rozmiar bufora argumentów to 2094486 bajtów. Teoretycznie źle używasz skryptów powłoki, jeśli Twoje dane są większe (rury, ktoś?)

uderzający
źródło
7

Dodając do odpowiedzi Fatal Error, istnieje jeszcze jeden sposób na przekazanie zmiennych do innego skryptu powłoki.

Powyższe sugerowane rozwiązanie ma pewne wady:

  1. using Export : Spowoduje to, że zmienna będzie poza zakresem, co nie jest dobrą praktyką projektową.
  2. using Source : Może to powodować kolizje nazw lub przypadkowe zastąpienie predefiniowanej zmiennej w innym pliku skryptu powłoki, który pozyskał inny plik.

Jest jeszcze jedno proste rozwiązanie, którego możemy użyć. Biorąc pod uwagę opublikowany przez Ciebie przykład,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

wynik

hellohelloheloo

Ważne jest również, aby pamiętać, że ""są one konieczne, jeśli przekazujemy ciągi wielosłówowe. Biorąc jeszcze jeden przykład

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

wynik

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

Dzieje się tak z powodów dokładnie opisanych w tym linku

Subham Tripathi
źródło
6
używanie sourcejest w rzeczywistości dobrą rzeczą. Jeśli chodzi o twoje zmartwienie: przypadkowe zastąpienie predefiniowanej zmiennej zawsze możesz uzyskać źródło w podpowłoce. To całkowicie rozwiązuje problem.
gniourf_gniourf
@gniourf_gniourf jak uzyskać dostęp do podpowłoki?
Toskan
@Toskan ta jest wymieniona w moją odpowiedź: ( . ./test2.sh ). Nawiasy powodują, że Bash uruchamia swoją zawartość w podpowłoce.
gniourf_gniourf
7

W Bash, jeśli eksportujesz zmienną w podpowłoce, używając nawiasów, jak pokazano, unikniesz wycieku eksportowanych zmiennych:

#!/bin/bash

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

Zaletą jest to, że po uruchomieniu skryptu z wiersza poleceń nie zobaczysz wycieku $ TESTVARIABLE do twojego środowiska:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
źródło
Użyłem go, ale wydaje się, że nie działa - napisałem: #! / Bin / bash for ((i = 1; i <= 3; i ++)) do (eksport i source ./script2.sh) zrobiłem BŁĄD to mówi: ./script2.sh:Nie ma takiego pliku ani katalogu
Pranjal Gupta
Podkładka nie jest tak naprawdę potrzebna, ponieważ środowisko najwyższego poziomu (wiersz $polecenia) nigdy nie zobaczy wyeksportowanego $TESTVARIABLE. Export tylko przekazuje kopię zmiennej do kolejnych procesów potomnych . Nie jest możliwe przekazywanie zmiennych z powrotem do łańcucha do procesów nadrzędnych, chyba że zapisuje się wartość w pamięci i odczytuje tę pamięć później w skrypcie procesu nadrzędnego. Przesyłanie potokowe do drugiej kopii skryptu nadrzędnego jest możliwe, ale nie byłby to ten sam proces . Doskonałe wyjaśnienie można znaleźć tutaj .
DocSalvager
2

Inną opcją jest użycie eval. Jest to odpowiednie tylko wtedy, gdy ciągi są zaufane. Pierwszy skrypt może powtórzyć przypisania zmiennych:

echo "VAR=myvalue"

Następnie:

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

To podejście jest szczególnie interesujące, gdy drugi skrypt, dla którego chcesz ustawić zmienne środowiskowe, nie znajduje się w bashu, a ty też nie chcesz exportzmiennych, być może dlatego, że są wrażliwe i nie chcesz, aby trwały.

Mark Stosberg
źródło
1

Innym sposobem, który jest dla mnie trochę łatwiejszy, jest stosowanie nazwanych potoków. Nazwane potoki umożliwiły synchronizację i wysyłanie wiadomości między różnymi procesami.

Wstydzić:

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

B.bash:

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

Stosowanie:

$ 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 będzie czekał na wiadomość i gdy tylko A.bash wyśle ​​wiadomość, B.bash będzie kontynuował pracę.

Pejman Habashi
źródło