( 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:
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= read
utilisé 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 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:
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 echo
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 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 IFS
paramètre pour diviser chaque ligne:
while read name value; do
value=${value#\"}; value=${value%\"}
export name="$value"
done <"$1"
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 longue