Wie speichere ich die Ausgabe eines Befehls, der die Umgebung in eine Variable ändert?
Ich benutze Bash Shell.
Angenommen, ich habe:
function f () { a=3; b=4 ; echo "`date`: $a $b"; }
Und jetzt kann ich Befehle verwenden, um Folgendes auszuführen f
:
$ a=0; b=0; f; echo $a; echo $b; echo $c
Sat Jun 28 21:27:08 CEST 2014: 3 4
3
4
aber ich möchte die Ausgabe von f
in Variable speichern c
, also habe ich versucht:
a=0; b=0; c=""; c=$(f); echo $a; echo $b; echo $c
aber leider habe ich:
0
0
Sat Jun 28 21:28:03 CEST 2014: 3 4
Ich habe hier also keine Umgebungsänderung.
Wie speichere ich die Ausgabe des Befehls (nicht nur der Funktion) in einer Variablen und speichere Umgebungsänderungen?
Ich weiß, dass dies eine $(...)
neue Subshell öffnet und das ist das Problem, aber ist es möglich, eine Problemumgehung durchzuführen?
bash
environment-variables
output
Faramir
quelle
quelle
$a
und$b
sind lokale Variablen in Ihrerf
Funktion. Sie könntenexport
sie, aber das scheint lückenhaft.…; f; echo $a; …
führt zu3
einem Echo, ebensof
wie das Ändern einer Shell-Variablen (und nicht nur ihrer eigenen lokalen Variablen).Antworten:
Wenn Sie Bash 4 oder höher verwenden, können Sie Coprozesse verwenden :
wird ausgegeben
coproc
Erstellt einen neuen Prozess, in dem ein bestimmter Befehl ausgeführt wird (hiercat
). Es speichert die PID inCOPROC_PID
und Standard-Deskriptoren für Ausgabe- / Eingabedateien in einem ArrayCOPROC
(genau wiepipe(2)
oder siehe hier oder hier ).Hier führen wir die Funktion mit Standardausgabe aus, die auf unseren laufenden Coprozess zeigt
cat
, und dannread
von dort aus. Dacat
nur die Eingabe zurückgespuckt wird, erhalten wir die Ausgabe der Funktion in unsere Variable.exec {COPROC[1]}>&-
Schließt einfach den Dateideskriptor,cat
damit Sie nicht ewig warten müssen.Beachten Sie, dass jeweils
read
nur eine Zeile benötigt wird. Sie könnenmapfile
ein Array von Zeilen abrufen oder einfach den Dateideskriptor verwenden, wie Sie ihn auch verwenden möchten.exec {COPROC[1]}>&-
funktioniert in aktuellen Versionen von Bash, aber in früheren Versionen der 4er-Serie müssen Sie den Dateideskriptor zuerst in einer einfachen Variablen speichern :fd=${COPROC[1]}; exec {fd}>&-
. Wenn Ihre Variable nicht gesetzt ist, wird die Standardausgabe geschlossen.Wenn Sie eine 3er-Version von Bash verwenden, können Sie den gleichen Effekt erzielen
mkfifo
, aber es ist nicht viel besser, als zu diesem Zeitpunkt eine tatsächliche Datei zu verwenden.quelle
exec {COPROC[1]}>&-
Befehl (zumindest manchmal) den Coprozess sofort , sodass seine Ausgabe nicht mehr zum Lesen verfügbar ist.coproc { cat; sleep 1; }
scheint besser zu funktionieren. (Möglicherweise müssen Sie die erhöhen,1
wenn Sie mehr als eine Zeile lesen.)Wenn Sie bereits auf die
f
Ausführung in derselben Shell wie der Aufrufer zählen und somit Variablen wiea
und ändern könnenb
, können Sie die Funktion auch einfach festlegenc
. Mit anderen Worten:Ein möglicher Grund kann sein, dass die Ausgabevariable unterschiedliche Namen (c1, c2 usw.) haben muss, wenn sie an verschiedenen Stellen aufgerufen wird. Sie können dies jedoch beheben, indem Sie die Funktion c_TEMP festlegen und die Aufrufer
c1=$c_TEMP
usw.quelle
Es ist ein Kluge, aber versuchen Sie es
(und optional
rm c.file
).quelle
mktemp
, aber ich möchte keine zusätzlichen Dateien erstellen.Weisen Sie einfach alle Variablen zu und schreiben Sie die Ausgabe gleichzeitig.
Wenn Sie dies tun:
Ihre Ausgabe ist:
Beachten Sie, dass dies vollständig tragbarer POSIX-Code ist. Ich habe anfangs
c=
die''
Nullzeichenfolge festgelegt, weil die Parametererweiterung die gleichzeitige Variablenzuweisung + Auswertung entweder nur auf numerische (wie$((var=num))
) oder von null oder nicht vorhandenen Werten beschränkt - oder mit anderen Worten, Sie können eine Variable nicht gleichzeitig auf eine beliebige Zeichenfolge setzen und auswerten wenn dieser Variablen bereits ein Wert zugewiesen ist. Also stelle ich einfach sicher, dass es leer ist, bevor ich es versuche. Wenn ichc
vor dem Versuch, es zuzuweisen, nicht leer wäre, würde die Erweiterung nur den alten Wert zurückgeben.Nur um zu demonstrieren:
newval
wird nicht$c
inline zugewiesen, daoldval
in erweitert wird${word}
, während die inline-$((
arithmetische=
Zuweisung))
immer erfolgt. Aber wenn$c
nein hatoldval
und leer oder nicht gesetzt ist ...... wird dann
newval
sofort zugeordnet und erweitert$c
.Alle anderen Möglichkeiten, dies zu tun, beinhalten irgendeine Form der sekundären Bewertung. Nehmen wir zum Beispiel an, ich wollte die Ausgabe
f()
einer Variablen zuweisen, diename
an einem Punkt undvar
an einem anderen benannt ist. Wie derzeit geschrieben, funktioniert dies nicht, ohne die Variable im Bereich des Aufrufers festzulegen. Ein anderer Weg könnte jedoch so aussehen:Ich habe unten ein besser formatiertes Beispiel bereitgestellt, aber wie oben genannt lautet die Ausgabe:
Oder mit anderen
$ENV
oder Argumenten:Bei der zweimaligen Auswertung ist es wahrscheinlich am schwierigsten, sicherzustellen, dass Variablen keine Anführungszeichen brechen und zufälligen Code ausführen. Je öfter eine Variable ausgewertet wird, desto schwieriger wird es. Die Parametererweiterung hilft hier sehr, und die Verwendung
export
im Gegensatz zueval
ist weitaus sicherer.Im obigen Beispiel wird
f()
zuerst$fout
die''
Nullzeichenfolge zugewiesen und dann Positionsparameter festgelegt, um auf gültige Variablennamen zu testen. Wenn beide Tests nicht bestanden werden, wird eine Nachricht ausgegebenstderr
und der Standardwert vonfout
zugewiesen$fout_name
. Unabhängig von den Tests wird jedoch$fout_name
immer entwederfout
oder dem von Ihnen$fout
angegebenen Namen zugewiesen , und optional wird Ihrem angegebenen Namen immer der Ausgabewert der Funktion zugewiesen. Um dies zu demonstrieren, habe ich diese kleinefor
Schleife geschrieben:Es spielt einige mit Variablennamen und Parametererweiterungen herum. Wenn Sie eine Frage haben, fragen Sie einfach. Das läuft nur die wenigen Zeilen in der hier bereits dargestellten Funktion. Zumindest ist zu erwähnen, dass sich die Variablen
$a
und$b
unterschiedlich verhalten, je nachdem, ob sie beim Aufruf definiert oder bereits festgelegt wurden. Trotzdemfor
macht der fast nichts anderes, als den Datensatz zu formatieren und bereitzustellen vonf()
. Guck mal:quelle