Alle Variablen von einem Shell-Skript an ein anderes übergeben?

192

Nehmen wir an, ich habe ein Shell / Bash-Skript mit dem Namen test.sh:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Mein test2.shsieht so aus:

#!/bin/bash

echo ${TESTVARIABLE}

Das funktioniert nicht. Ich möchte nicht alle Variablen als Parameter übergeben, da dies imho übertrieben ist.

Gibt es einen anderen Weg?

Toskan
quelle
Eine Idee ist, die Variablen in einer Datei zu speichern und dann in ein anderes Skript zu laden.
Rodrigo
@Rodrigo Ich habe zuvor einen ähnlichen Ansatz zum "Speichern" von Werten zwischen Skriptläufen mit einigem Erfolg verwendet. Eine Sache, die Sie beachten sollten, ist, dass es schwierig werden kann, wenn mehrere Instanzen der Skripte ausgeführt werden. Aber wenn Sie sie in Bash-Zuweisungsform speichern, können Sie einfach die Datei zum Importieren der Variablen als Quelle verwenden
FatalError

Antworten:

264

Sie haben grundsätzlich zwei Möglichkeiten:

  1. Machen Sie die Variable zu einer Umgebungsvariablen ( export TESTVARIABLE), bevor Sie das 2. Skript ausführen.
  2. Quelle das 2. Skript, dh . test2.shund es wird in der gleichen Shell ausgeführt. Auf diese Weise können Sie komplexere Variablen wie Arrays problemlos gemeinsam nutzen, das andere Skript kann jedoch auch Variablen in der Quellshell ändern.

AKTUALISIEREN:

Zum exportFestlegen einer Umgebungsvariablen können Sie entweder eine vorhandene Variable verwenden:

A=10
# ...
export A

Dies sollte in beide arbeiten bashund sh. bashermöglicht auch die Kombination wie folgt:

export A=10

Dies funktioniert auch in meinem sh (was zufällig ist bash, können Sie verwenden, um echo $SHELLzu überprüfen). Aber ich glaube nicht, dass das garantiert in allen funktioniert sh, also gehen Sie am besten auf Nummer sicher und trennen Sie sie.

Jede Variable, die Sie auf diese Weise exportieren, wird in von Ihnen ausgeführten Skripten angezeigt, zum Beispiel:

Asche:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Dann:

$ ./a.sh
The message is: hello

Die Tatsache, dass dies beide Shell-Skripte sind, ist ebenfalls nur ein Zufall. Umgebungsvariablen können an jeden von Ihnen ausgeführten Prozess übergeben werden. Wenn wir stattdessen Python verwenden, sieht dies möglicherweise folgendermaßen aus:

Asche:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Beschaffung:

Stattdessen könnten wir so beschaffen:

Asche:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Dann:

$ ./a.sh
The message is: hello

Dies "importiert" mehr oder weniger den Inhalt von b.shdirekt und führt ihn in derselben Shell aus . Beachten Sie, dass wir die Variable nicht exportieren mussten, um darauf zuzugreifen. Dies teilt implizit alle Variablen, die Sie haben, und ermöglicht es dem anderen Skript, Variablen in der Shell hinzuzufügen / zu löschen / zu ändern. Natürlich sollten in diesem Modell beide Skripte dieselbe Sprache ( shoder bash) haben. Um ein Beispiel zu geben, wie wir Nachrichten hin und her weitergeben können:

Asche:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Dann:

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

Dies funktioniert genauso gut in bash. Es macht es auch einfach, komplexere Daten wie Arrays oder assoziative Arrays zu teilen, die Sie nicht als Umgebungsvariable ausdrücken können (zumindest ohne Ihr schweres Heben).

Fataler Fehler
quelle
1
Was ist, wenn ich $ 1 an die Sub-Shell übergeben muss (weil 'sudo sh -c ...' von einem Skript aufgerufen wird)? Muss ich $ 1 in eine Umgebungsvariable schieben, diese exportieren und die Variable im Befehl verwenden?
Urhixidur
Nur um hinzuzufügen, dass Sie sudo -E ... verwenden können, um die Umgebungsvariablen beizubehalten, wenn Sie sudo-Zugriffsrechte benötigen.
Yucer
2
@FatalError Kannst du bitte die Magie im letzten a.sh erklären, wo du "./b.sh" nennst? Was bedeutet der erste Punkt? Es funktioniert übrigens großartig!
Deian
Sie können auch die Variablen und Werte auf eine Datei setzen und sie auf diese Weise teilen
newshorts
@Deian, Der Punkt (Punkt) ist eine kurze Hand für die in "Quelle" eingebaute Bash
Kirill Kost
27

Fatal Error gab eine einfache Möglichkeit: Quelle dein zweites Skript! Wenn Sie befürchten, dass dieses zweite Skript einige Ihrer wertvollen Variablen verändert, können Sie es jederzeit in einer Unterschale abrufen:

( . ./test2.sh )

Durch die Klammern wird die Quelle in einer Subshell ausgeführt, sodass die übergeordnete Shell die Änderungen nicht sehen kann test2.sh.


Es gibt noch eine andere Möglichkeit, auf die hier unbedingt Bezug genommen werden sollte: Verwenden set -a.

Aus der POSIX- setReferenz :

-a: Wenn diese Option aktiviert ist, der Export soll Attribut für jede Variable , auf die eine Zuordnung durchgeführt werden eingestellt; Weitere Informationen finden Sie im Band Basisdefinitionen von IEEE Std 1003.1-2001, Abschnitt 4.21, Variablenzuweisung . Wenn die Zuweisung vorangeht in einem Befehl ein Dienstprogramm Name, der Exports wird Attribut bestehen bleibt nicht in der aktuellen Ausführungsumgebung nach dem Dienstprogramm abgeschlossen ist , mit der Ausnahme , dass vorhergehende den speziellen Einbau-Dienstprogramme , die Ursachen export - Attribute nach dem eingebauten anhalten in hat abgeschlossen. Wenn die Zuweisung nicht vor einem Dienstprogrammnamen im Befehl steht oder wenn die Zuweisung ein Ergebnis der Operation der getopts oder read ist Dienstprogramme, das Exportattribut bleibt bestehen, bis die Variable deaktiviert wird.

Aus dem Bash-Handbuch :

-a: Markieren Sie Variablen und Funktionen, die für den Export nachfolgender Befehle in die Umgebung geändert oder erstellt wurden.

Also in deinem Fall:

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

Beachten Sie, dass in den Spezifikationen nur angegeben ist, dass set -adie Variable für den Export markiert ist . Das ist:

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

wird wiedergegeben cund weder eine leere Zeile noch b(das heißt, set +adie Markierung für den Export wird nicht aufgehoben, noch wird der Wert der Zuweisung nur für die exportierte Umgebung "gespeichert"). Dies ist natürlich das natürlichste Verhalten.

Fazit: Die Verwendung von set -a/ set +akann weniger mühsam sein als das manuelle Exportieren aller Variablen. Es ist der Beschaffung des zweiten Skripts überlegen, da es für jeden Befehl funktioniert, nicht nur für diejenigen, die in derselben Shell-Sprache geschrieben sind.

gniourf_gniourf
quelle
18

Es gibt tatsächlich einen einfacheren Weg als das Exportieren und Deaktivieren oder erneutes Sourcing (zumindest in Bash, solange Sie die Umgebungsvariablen manuell übergeben können):

lass a.sh sein

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

und b.sh sein

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

Beobachtete Ausgabe ist

[rob @ Archie test] $ ./a.sh
Yo, lass mich dir sagen "zwinkere mir zu", b.sh!
Ich hörte "zwinker mein Geklingel", yo

Die Magie liegt in der letzten Zeile von a.sh, wo Messagenur für die Dauer des Aufrufs von ./b.shauf den Wert von secretvon gesetzt wird a.sh. Im Grunde ist es ein bisschen wie benannte Parameter / Argumente. Darüber hinaus funktioniert es sogar für Variablen wie $DISPLAY, die steuern, auf welchem ​​X Server eine Anwendung gestartet wird.

Denken Sie daran, dass die Liste der Umgebungsvariablen nicht unendlich lang ist. Auf meinem System mit einem relativ Vanille-Kernel xargs --show-limitswird mir mitgeteilt , dass die maximale Größe des Argumentpuffers 2094486 Byte beträgt. Theoretisch verwenden Sie Shell-Skripte falsch, wenn Ihre Daten größer sind (Pipes, irgendjemand?)

auffällig
quelle
7

Neben der Antwort auf Fatal Error gibt es noch eine Möglichkeit, die Variablen an ein anderes Shell-Skript zu übergeben.

Die oben vorgeschlagene Lösung weist einige Nachteile auf:

  1. using Export : Dadurch wird die Variable außerhalb ihres Gültigkeitsbereichs angezeigt, was keine gute Entwurfspraxis ist.
  2. using Source : Es kann zu Namenskollisionen oder versehentlichem Überschreiben einer vordefinierten Variablen in einer anderen Shell-Skriptdatei kommen, die eine andere Datei bezogen hat.

Es gibt eine andere einfache Lösung, die wir verwenden können. In Anbetracht des von Ihnen veröffentlichten Beispiels

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

Ausgabe

hellohelloheloo

Es ist auch wichtig zu beachten, dass dies ""notwendig ist, wenn wir Mehrwortzeichenfolgen übergeben. Nehmen wir noch ein Beispiel

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

Ausgabe

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

Dies geschieht aus den in diesem Link treffend beschriebenen Gründen

Subham Tripathi
quelle
6
Verwenden sourceist eigentlich eine gute Sache. In Bezug auf Ihre Sorge: Versehentliches Überschreiben einer vordefinierten Variablen können Sie immer in einer Subshell eingeben. Damit ist das Problem vollständig gelöst.
gniourf_gniourf
@gniourf_gniourf wie in eine Subshell zu quellen?
Toskan
@Toskan Dies wird in meiner Antwort erwähnt : ( . ./test2.sh ). Durch die Klammern wird Bash seinen Inhalt in einer Unterschale ausführen.
gniourf_gniourf
7

Wenn Sie in Bash die Variable innerhalb einer Unterschale in Klammern wie gezeigt exportieren, vermeiden Sie, dass die exportierten Variablen verloren gehen:

#!/bin/bash

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

Der Vorteil hierbei ist, dass nach dem Ausführen des Skripts über die Befehlszeile kein $ TESTVARIABLE in Ihre Umgebung gelangt:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
quelle
Ich habe es benutzt, aber es scheint nicht zu funktionieren. Ich schrieb: #! / Bin / bash für ((i = 1; i <= 3; i ++)) do (export i source ./script2.sh) hat ERROR it sagt: ./script2.sh:Keine solche Datei oder Verzeichnis
Pranjal Gupta
Die Subshell wird eigentlich nicht benötigt, da in der Umgebung der obersten Ebene (der $Eingabeaufforderung) die exportierte Datei nie angezeigt wird $TESTVARIABLE. Beim Exportieren wird lediglich eine Kopie einer Variablen an alle nachfolgenden untergeordneten Prozesse übergeben. Es ist nicht möglich, Variablen in der Kette an übergeordnete Prozesse zu übergeben, es sei denn, Sie speichern den Wert im Speicher und lesen diesen Speicher später im übergeordneten Prozessskript. Das Weiterleiten an eine zweite Kopie des übergeordneten Skripts ist möglich, dies wäre jedoch nicht der gleiche Vorgang . Eine ausgezeichnete Erklärung finden Sie hier .
DocSalvager
2

Eine andere Option ist die Verwendung eval. Dies ist nur geeignet, wenn die Zeichenfolgen vertrauenswürdig sind. Das erste Skript kann die Variablenzuweisungen wiedergeben:

echo "VAR=myvalue"

Dann:

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

Dieser Ansatz ist von besonderem Interesse, wenn sich das zweite Skript, für das Sie Umgebungsvariablen festlegen möchten, nicht in Bash befindet und Sie exportdie Variablen auch nicht verwenden möchten , möglicherweise weil sie vertraulich sind und Sie nicht möchten, dass sie bestehen bleiben.

Mark Stosberg
quelle
1

Eine andere Möglichkeit, die für mich etwas einfacher ist, ist die Verwendung von Named Pipes. Named Pipes boten die Möglichkeit, Nachrichten zwischen verschiedenen Prozessen zu synchronisieren und zu senden.

Beschämen:

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

B.bash:

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

Verwendung:

$ 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 wartet auf eine Nachricht und sobald A.bash die Nachricht sendet, setzt B.bash seine Arbeit fort.

Pejman Habashi
quelle