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.
key=${kv%%=*}
est meilleure quekey=${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 longueRéponses:
export $key=$val
devrait fonctionner très bienbash
. 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 debash
cela exécute votre script va bifurquer 2 sous-coquilles: une pourcat $1
et une pour lawhile 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:BashFAQ 24 explique cela plus en détail.
Une autre approche consiste à simplement source le fichier, avec des exportations intégrées:
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
$1
est une source fiable afin qu'il ne puisse pas faire de mal à votre script.la source
cat
. Mais, le script avec redirection fonctionne maintenant, merci!( 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
cat
par une redirection: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.
Notez qu'en général,
while read line; do …
n'itère pas tout à fait les lignes d'entrée. Voir Pourquoi est-ilwhile IFS= read
utilisé si souvent au lieu deIFS=; while read..
? pour une explication. L'idiome correct pour itérer sur les lignes d'entrée estIci, 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 cewhile read line
soit 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:
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, ceecho
n'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 estprintf %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 duIFS
paramètre pour diviser chaque ligne:la source
Une autre bonne option pourrait être de placer le
export
dans le fichier et de le source:et alors:
la source
/ env
scénario
la source