Définir les variables d'environnement variables dans bash (ou autre)

9

Je veux que mon script lise un fichier contenant des paires clé / valeur de variables d'environnement à définir, puis les définit.

Jusqu'à présent, j'ai ceci:

#!/bin/bash

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

Et je veux lire un fichier comme celui-ci:

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

Je n'ai besoin de ces variables dans la portée que pour la durée du script, donc je n'ai pas besoin de résoudre le problème de définition d'une variable dans le script pour la portée parent .

D'après mes tests, je pense que mon problème réside dans export $key="$val", donc je pense que j'ai juste besoin d'un remplacement pour cette ligne.

palswim
la source
un peu hors sujet, mais depuis que je suis coincé en essayant d'imiter votre script, je partage ... lorsque je saisis une clé ... l'utilisation key=${kv%%=*}est meilleure que key=${kv%=*}parce que %% et ## diffèrent de% et # pour savoir si la partie supprimée est la partie amovible la plus courte ou la plus longue
pulkitsinghal

Réponses:

10

export $key=$valdevrait fonctionner très bien bash. Je soupçonne que votre problème est la pipe ( |). Toutes les commandes d'un pipeline sont exécutées dans un sous-shell. Dans votre exemple, l'instance de bashcela exécute votre script va bifurquer 2 sous-coquilles: une pour cat $1et une pour la while read .... Les variables sont attribuées et exportées dans le sous-shell exécutant la boucle while, puis toutes sont rapidement supprimées lorsque le sous-shell se ferme. Une façon de résoudre ce problème est de ne pas générer de sous-coquilles du tout. Au lieu d'une utilisation inutile de cat , essayez plutôt la redirection:

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

BashFAQ 24 explique cela plus en détail.

Une autre approche consiste à simplement source le fichier, avec des exportations intégrées:

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

C'est la <( )substitution de processus.

Comme mise en garde supplémentaire, puisque vous lisez des variables d'environnement à partir d'un argument, vous devez vous assurer que le fichier d'argument $1est une source fiable afin qu'il ne puisse pas faire de mal à votre script.

jw013
la source
J'ai utilisé la redirection précédemment; quelque chose d'autre n'a pas fonctionné lorsque j'ai changé le script à utiliser cat. Mais, le script avec redirection fonctionne maintenant, merci!
palswim
Utilisation inutile de chat considérée comme nuisible!
Scott
2

( jw013 a déjà donné une réponse correcte , mais je tiens à souligner quelques autres problèmes.)

Le côté droit d'un pipeline s'exécute dans son propre sous-shell dans bash (ainsi que la plupart des autres shells, les exceptions étant ATT ksh et zsh). Vous définissez donc les variables d'environnement dans le sous-shell qui exécute la boucle, mais pas dans le processus shell principal.

Voici une solution simple qui consiste à remplacer l'utilisation inutile de catpar une redirection:

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

En général, si vous devez prétraiter l'entrée, placez la boucle et le reste du script (au moins la partie qui a besoin des variables modifiées) dans un bloc.

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

Notez qu'en général, while read line; do …n'itère pas tout à fait les lignes d'entrée. Voir Pourquoi est-il while IFS= readutilisé si souvent au lieu de IFS=; while read..? pour une explication. L'idiome correct pour itérer sur les lignes d'entrée est

while IFS= read -r line; do 

Ici, la suppression des espaces blancs de début et de fin n'a pas d'importance car il ne devrait pas y en avoir étant donné votre entrée d'échantillon, mais vous devrez peut-être -réviter l'expansion de la barre oblique inverse. Il est possible que ce while read linesoit en fait une façon correcte de lire votre entrée (mais je ne soupçonne que si les valeurs des variables ne contiennent pas de nouvelles lignes). Il est également possible que votre format d'entrée soit ambigu ou plus complexe à analyser. Vérifiez ce qui se passe si l'une des valeurs des variables contient un caractère de nouvelle ligne, un guillemet double ou une barre oblique inverse.

Un point où vous modifiez définitivement l'entrée est lorsque vous extrayez la valeur:

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

Tout d' abord, vous devez utiliser des guillemets doubles autour de la substitution de variable: echo "${kv#*=}"; sinon, le shell effectue le fractionnement de mots et la génération de noms de fichiers (c'est-à-dire la globalisation) sur celui-ci. Deuxièmement, ce echon'est pas un moyen fiable d'imprimer une chaîne, car certaines chaînes qui commencent par a -sont traitées comme des options, et certains shells effectuent une expansion de barre oblique inverse sur l'argument. Un moyen fiable et portable d'imprimer une chaîne est printf %s "${kv#*=}". De plus, si la valeur s'étend sur plusieurs lignes, le programme sed agira sur chacune séparément, ce qui est faux. Ceci est réparable, mais il y a une méthode plus simple, qui consiste à utiliser les installations de manipulation de chaînes du shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

En supposant que votre entrée est correctement analysée avec un simple read, voici une façon plus simple de l'analyser, en profitant du IFSparamètre pour diviser chaque ligne:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Gilles 'SO- arrête d'être méchant'
la source
0

Une autre bonne option pourrait être de placer le exportdans le fichier et de le source:

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

et alors:

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

/ env

EXPORT=value
#NOTEXPORT=notvalue

scénario

#!/bin/sh

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