Festlegen von Variablen Umgebungsvariablen in Bash (oder anderen)

9

Ich möchte, dass mein Skript eine Datei liest, die Schlüssel / Wert-Paare von Umgebungsvariablen enthält, die festgelegt werden sollen, und diese dann festlegt.

Bisher habe ich Folgendes:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

Und ich möchte eine Datei wie diese lesen:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Ich benötige diese Variablen nur für die Dauer des Skripts im Gültigkeitsbereich, sodass ich das Problem des Festlegens einer Variablen im Skript für den übergeordneten Gültigkeitsbereich nicht lösen muss .

Nach meinen Tests liegt mein Problem wohl bei export $key="$val", daher brauche ich nur einen Ersatz für diese Leitung.

palswim
quelle
Ein bisschen abseits des Themas, aber da ich beim Versuch, Ihr Skript nachzuahmen, stecken geblieben bin, teile ich ... beim Ergreifen eines Schlüssels ... die Verwendung key=${kv%%=*}ist besser als key=${kv%=*}weil %% und ## sich von% und # unterscheiden, ob der entfernte Teil ist das kürzeste oder längste abnehmbare Teil
pulkitsinghal

Antworten:

10

export $key=$valsollte gut funktionieren in bash. Ich vermute, Ihr Problem ist die Pipe ( |). Alle Befehle in einer Pipeline werden in einer Subshell ausgeführt. In Ihrem Beispiel gibt die Instanz bash, in der Ihr Skript ausgeführt wird, zwei Unterschalen ab: eine für cat $1und eine für die while read .... Die Variablen werden in der Subshell zugewiesen und exportiert, in der die while-Schleife ausgeführt wird. Alle Variablen werden beim Beenden der Subshell sofort verworfen. Eine Möglichkeit, dies zu beheben, besteht darin, überhaupt keine Subshells zu erzeugen. Versuchen Sie statt einer nutzlosen Verwendung von cat stattdessen die Umleitung:

while read ...; do
   ...
done < "$1"

BashFAQ 24 erklärt dies ausführlicher.

Ein alternativer Ansatz besteht darin, nur die Datei mit Exporten zu erstellen:

. <(sed '/^export/!s/^/export /' "$1")

Das <( )ist Prozesssubstitution.

Als zusätzliche Warnung, da Sie Umgebungsvariablen aus einem Argument lesen, müssen Sie sicherstellen, dass die Argumentdatei $1eine vertrauenswürdige Quelle ist, damit sie Ihrem Skript keine schlechten Dinge antun kann.

jw013
quelle
Ich habe zuvor die Umleitung verwendet. etwas anderes muss nicht funktioniert haben, als ich das zu verwendende Skript geändert habe cat. Aber das Skript mit Umleitung funktioniert jetzt, danke!
Palswim
Nutzloser Gebrauch von Katze als schädlich!
Scott
2

( jw013 hat bereits eine richtige Antwort gegeben , aber ich möchte auf einige weitere Probleme hinweisen.)

Die rechte Seite einer Pipeline wird in bash in einer eigenen Subshell ausgeführt (wie auch die meisten anderen Shells, mit Ausnahme von ATT ksh und zsh). Sie legen also die Umgebungsvariablen in der Subshell fest, die die Schleife ausführt, jedoch nicht im Haupt-Shell-Prozess.

Hier gibt es eine einfache Lösung, die die nutzlose Verwendung catdurch eine Umleitung ersetzt:

while read …; do …; done <"$1"

Wenn Sie die Eingabe vorverarbeiten müssen, fügen Sie im Allgemeinen die Schleife und den Rest des Skripts (zumindest den Teil, der die geänderten Variablen benötigt) in einen Block ein.

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Beachten Sie, dass while read line; do …die Eingabezeilen im Allgemeinen nicht vollständig durchlaufen werden. Siehe Warum wird while IFS= readso oft verwendet, anstatt IFS=; while read..? für eine Erklärung. Die richtige Redewendung zum Durchlaufen von Eingabezeilen lautet

while IFS= read -r line; do 

Hier spielt das Entfernen von führenden und nachfolgenden Leerzeichen keine Rolle, da Ihre Beispieleingabe nicht angegeben werden sollte. Möglicherweise müssen Sie -rjedoch eine Backslash-Erweiterung vermeiden. Es ist möglich, dass dies while read linetatsächlich eine korrekte Art ist, Ihre Eingabe zu lesen (aber ich vermute nur, wenn die Variablenwerte keine Zeilenumbrüche enthalten). Es ist auch möglich, dass Ihr Eingabeformat mehrdeutig oder komplexer zu analysieren ist. Überprüfen Sie, was passiert, wenn einer der Variablenwerte ein Zeilenumbruchzeichen, ein doppeltes Anführungszeichen oder einen Backslash enthält.

Ein Punkt, an dem Sie die Eingabe definitiv entstellen, ist, wenn Sie den Wert extrahieren:

val=`echo ${kv#*=} | sed 's/^"\|"$//g'`

Zunächst müssen Sie doppelte Anführungszeichen um die Variablensubstitution verwenden : echo "${kv#*=}"; Andernfalls führt die Shell eine Wortaufteilung und Dateinamengenerierung (dh Globbing) durch. Zweitens echoist dies keine zuverlässige Methode zum Drucken einer Zeichenfolge, da einige Zeichenfolgen, die mit a beginnen, -als Optionen behandelt werden und einige Shells eine Backslash-Erweiterung für das Argument ausführen. Eine zuverlässige und tragbare Methode zum Drucken einer Zeichenfolge ist printf %s "${kv#*=}". Wenn sich der Wert über mehrere Zeilen erstreckt, wirkt das sed-Programm auf jede einzelne Zeile, was falsch ist. Dies kann behoben werden, aber es gibt eine einfachere Methode, nämlich die String-Manipulationsfunktionen der Shell zu verwenden : val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Unter der Annahme, dass Ihre Eingabe korrekt mit einer Ebene readanalysiert wurde, können Sie sie auf einfachere Weise analysieren und dabei die IFSEinstellung zum Teilen jeder Zeile nutzen:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Gilles 'SO - hör auf böse zu sein'
quelle
0

Eine andere gute Option könnte sein, das exportin die Datei zu legen und es zu beschaffen:

export XAUTHLOCALHOSTNAME="localhost"
export DISPLAY=":0"
export XAUTHORITY="/tmp/some-XAuthority"

und dann:

. "$1"
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
0

/ env

EXPORT=value
#NOTEXPORT=notvalue

Skript

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Łukasz Kurowski
quelle