web-dev-qa-db-ja.com

スペースを含む値をevalを使用してbashの変数に割り当てる方法

evalを使用して変数に値を動的に割り当てたい。次のダミーの例は機能します:

var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange

ただし、変数値にスペースが含まれている場合、$var_valueが二重引用符で囲まれていても、evalはエラーを返します。

var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found

これを回避する方法はありますか?

19

Evalを使用しないでください declare を使用します

$ declare "$var_name=$var_value"
$ echo "fruit: >$fruit<"
fruit: >blue orange<
13
glenn jackman

これにはevalを使用しないでください。 declareを使用します。

var_name="fruit"
var_value="blue orange"
declare "$var_name=$var_value"

=に続くすべてのものがdeclareによって値として扱われ、最初のWordだけでなく、Word分割が問題にならないことに注意してください。

bash 4.3では、名前付き参照によってこれが少し簡単になりました。

$ declare -n var_name=fruit
$ var_name="blue orange"
$ echo $fruit
blue orange

あなたはcanevalを機能させますが、それでもいけません:) evalを使用することは悪い習慣です。

$ eval "$(printf "%q=%q" "$var_name" "$var_value")"
11
chepner

evalで作業する良い方法は、テストのためにechoで置き換えることです。 echoevalは同じように機能します(特定の条件下でechoのようなbashの実装によって行われる_\x_拡張を別にしておくと)。

どちらのコマンドも、引数を1つのスペースで結合します。違いは、echodisplays結果であるのに対し、evalevaluates/interpretsはシェルが結果をコーディングすることです。

だから、どのシェルコードを見るには

_eval $(echo $var_name=$var_value)
_

評価します、あなたは実行することができます:

_$ echo $(echo $var_name=$var_value)
fruit=blue orange
_

それはあなたが望むものではありません、あなたが望むものは:

_fruit=$var_value
_

また、ここで$(echo ...)を使用しても意味がありません。

上記を出力するには、次を実行します。

_$ echo "$var_name=\$var_value"
fruit=$var_value
_

だから、それを解釈するには、それは簡単です:

_eval "$var_name=\$var_value"
_

個々の配列要素の設定にも使用できることに注意してください。

_var_name='myarray[23]'
var_value='something'
eval "$var_name=\$var_value"
_

他の人が言ったように、コードがbash固有であることを気にしない場合は、declareを次のように使用できます。

_declare "$var_name=$var_value"
_

ただし、いくつかの副作用があることに注意してください。

変数のスコープを、それが実行される関数に制限します。たとえば、次のような場合には使用できません。

_setvar() {
  var_name=$1 var_value=$2
  declare "$var_name=$var_value"
}
setvar foo bar
_

それはfooにローカルなsetvar変数を宣言するため、役に立たないからです。

_bash-4.2_は、_global変数を宣言するためにdeclareに_-g_オプションを追加しましたが、setvarが設定するので、これは望みのものではありませんa global varこれは、次のように、呼び出し元が関数の場合、呼び出し元の変数ではありません。

_setvar() {
  var_name=$1 var_value=$2
  declare -g "$var_name=$var_value"
}
foo() {
  local myvar
  setvar myvar 'some value'
  echo "1: $myvar"
}
foo
echo "2: $myvar"
_

出力されます:

_1:
2: some value
_

また、declaredeclareと呼ばれますが(実際にはbashはKornシェルのtypesetビルトインから概念を取り入れました)、変数がすでに設定されている場合、 declareは新しい変数を宣言せず、割り当ての方法は変数の型によって異なります。

例えば:

_varname=foo
varvalue='([PATH=1000]=something)'
declare "$varname=$varvalue"
_

varnameが以前にscalararrayまたはassociative array)として宣言されていた場合、異なる結果が生成されます(また、厄介な副作用が発生する可能性があります

4

もし、するなら:

eval "$name=\$val"

...および$nameには;またはシェルが単純なコマンドを区切ると解釈する可能性のある他のいくつかのトークンが含まれます-適切なシェル構文が前に付いて実行されます。

name='echo hi;varname' val='be careful with eval'
eval "$name=\$val" && echo "$varname"

出力

hi
be careful with eval

ただし、そのようなステートメントの評価と実行を分離することが可能な場合もあります。たとえば、aliasは、コマンドを事前評価するために使用できます。次の例では、変数の定義がaliasに保存されます。これは、評価する$nm変数に一致しないバイトが含まれていない場合にのみ正常に宣言できますASCII =英数字または_

LC_OLD=$LC_ALL LC_ALL=C
alias "${nm##*[!_A-Z0-9a-z]*}=_$nm=\$val" &&
eval "${nm##[0-9]*}" && unalias "$nm"
LC_ALL=$LC_OLD

evalは、varnameからの新しいaliasの呼び出しを処理するためにここで使用されます。しかし、前のaliasの定義が成功した場合にのみ呼び出されます。多くの異なる実装がaliasの名前にさまざまな種類の値を受け入れることはわかっていますが、まだ完全に空のものを受け入れるものに出くわします。

alias内の定義は_$nm用ですが、これは重要な環境値が上書きされないようにするためです。 _で始まる注目すべき環境値は知らないので、通常はセミプライベート宣言の安全策です。

とにかく、aliasの定義が成功すると、$nmの値に対応する名前のaliasが宣言されます。また、evalは、数字でも始まらない場合にのみaliasを呼び出します。それ以外の場合、evalはnull引数のみを取得します。したがって、両方の条件が満たされた場合、evalはエイリアスを呼び出し、エイリアスに保存された変数定義が作成されます。その後、新しいaliasがハッシュテーブルから即座に削除されます。

1
mikeserv