web-dev-qa-db-ja.com

bash(またはその他)で変数環境変数を設定する

スクリプトで、設定する環境変数のキーと値のペアを含むファイルを読み取ってから、それらを設定する必要があります。

これまでのところ、私はこれを持っています:

#!/bin/bash

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

そして、私はこのようなファイルを読みたいです:

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

スクリプトの実行中はスコープにこれらの変数のみが必要なので、 スクリプトで変数を親スコープに設定する の問題を解決する必要はありません。

私のテストから、私の問題はexport $key="$val"にあると思うので、その行の交換だけが必要だと思います。

9
palswim

_export $key=$val_はbashで問題なく動作するはずです。あなたの問題はパイプ(_|_)だと思います。パイプライン内のすべてのコマンドはサブシェルで実行されます。あなたの例では、スクリプトを実行しているbashのインスタンスが2つのサブシェルを分岐します。1つは_cat $1_用で、もう1つは_while read ..._用です。変数は、whileループを実行するサブシェルで割り当てられてエクスポートされ、サブシェルが終了すると、すべての変数が即座にスローされます。これを修正する1つの方法は、サブシェルをまったく生成しないことです。 猫の無駄な使用 の代わりに、代わりにリダイレクトを試してください:

_while read ...; do
   ...
done < "$1"
_

BashFAQ 24 これについて詳しく説明しています。

別の方法は、ファイルをソースするだけで、エクスポートがスローされます。

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

<( )はプロセス置換です。

追加の注意事項として、引数から環境変数を読み取っているので、引数ファイル_$1_が信頼できるソースであることを確認して、スクリプトに悪影響を及ぼすことがないようにする必要があります。

10
jw013

jw013はすでに正しい答えを出している が、さらにいくつかの問題を指摘したい。)

パイプラインの右側は、bashの独自のサブシェルで実行されます(他のほとんどのシェルと同様、例外はATT kshとzshです)。つまり、ループを実行するサブシェルで環境変数を設定していますが、メインのシェルプロセスでは設定していません。

ここに、catの無用な使用をリダイレクトで置き換える簡単な修正があります:

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

一般に、入力を前処理する必要がある場合は、ループとスクリプトの残りの部分(少なくとも変更された変数を必要とする部分)をブロック内に配置します。

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

一般に、while read line; do …は入力行を完全に反復しないことに注意してください。説明については、 while IFS= read ではなくIFS=; while read..がなぜ頻繁に使用されるのかを参照してください。入力行を反復する正しいイディオムは

while IFS= read -r line; do …

ここでは、サンプル入力が指定されていないため、先頭と末尾の空白の削除は重要ではありませんが、バックスラッシュの展開を避けるために-rが必要になる場合があります。 while read lineが実際に入力を読み取る正しい方法である可能性があります(ただし、変数の値に改行が含まれていない場合にのみ疑います)。入力形式が曖昧であるか、解析が複雑である可能性もあります。変数の値の1つに改行文字、二重引用符、またはバックスラッシュが含まれている場合にどうなるかを確認します。

入力を間違いなく変換する1つのポイントは、値を抽出するときです。

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

まず、変数置換の前後に二重引用符を使用する必要があります:echo "${kv#*=}";それ以外の場合、シェルはWord分割とファイル名生成(つまり、グロビング)を実行します。次に、echoは文字列を出力する信頼できる方法ではありません。-で始まる一部の文字列はオプションとして扱われ、一部のシェルは引数に対してバックスラッシュ展開を実行するためです。文字列を印刷するための信頼できる移植可能な方法はprintf %s "${kv#*=}"です。また、値が複数行にわたる場合、sedプログラムはそれぞれ個別に動作しますが、これは誤りです。これは修正可能ですが、シェルの文字列操作機能を使用する簡単な方法があります:val=${kv#*=}; val=${val#\"}; val=${val%\"}

入力がプレーンなreadで正しく解析されると仮定すると、IFS設定を利用して各行を分割することにより、入力を解析する簡単な方法を次に示します。

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"

/ env

EXPORT=value
#NOTEXPORT=notvalue

脚本

#!/bin/sh

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

別の適切なオプションは、exportをファイルに配置してソースすることです。

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

その後:

. "$1"