web-dev-qa-db-ja.com

環境変数を設定するコマンドのプレフィックスとして変数を使用できないのはなぜですか?

通常、次のように接頭辞を付けることで、コマンドの環境変数を設定できます。

hello=hi bash -c 'echo $hello'

また、変数を使用して、次のようなコマンド呼び出しの任意の部分を置き換えることができることも知っています。

$ cmd=bash
$ $cmd -c "echo hi" # equivalent to bash -c "echo hi"

環境変数を設定するコマンドの前に変数を使用できないことを知って、私は非常に驚きました。テストケース:

$ prefix=hello=hi
$ echo $prefix # prints hello=hi
$ $prefix bash -c 'echo $hello'
hello=hi: command not found

変数を使用して環境変数を設定できないのはなぜですか?プレフィックス部分は特別な部分ですか?前にevalを使用することで機能させることができましたが、それでも理由がわかりません。私はbash 4.4を使用しています。

11
wbkang

これはあなたを捕まえているシーケンスの一部だと思います:

変数の割り当てまたはリダイレクトではない単語は展開されます(シェル展開を参照)。展開後に単語が残っている場合、最初の単語がコマンドの名前と見なされ、残りの単語が引数になります。

これは、単純なコマンド展開に関するセクションの Bashリファレンスマニュアル からの抜粋です。

cmd=bashの例では、環境変数は設定されておらず、bashはパラメーター展開を通じてコマンドラインを処理し、bash -c "echo hi"を残します。

prefix=hello=hiの例では、最初のパスに変数の割り当てがないため、処理はパラメーターの展開に進み、最初のワードはhello=hiになります。

変数の割り当てが処理されると、それらはコマンドの実行中に再処理されません。

set -xで処理とその結果を確認します。

$ prefix=hello=hi
+ prefix=hello=hi
$ $prefix bash -c 'echo $hello'
+ hello=hi bash -c 'echo $hello'
-bash: hello=hi: command not found
$ hello=42 bash -c 'echo $hello'
+ hello=42
+ bash -c 'echo $hello'
42

evalよりも「変数展開」-「環境変数」のより安全なバリエーションについては、 wjandreaのenv の提案を考慮してください。

prefix=hello=hi
env "$prefix" bash -c 'echo "$hello"'
hi

envユーティリティの環境変数をコマンドに割り当てるという主な機能を使用しているため、これは厳密にはコマンドライン変数の割り当てではありませんが、同じ目的を達成します。 $prefix変数はコマンドラインの処理中に展開され、envにname = valueを渡し、bashに渡します。

14
Jeff Schaller

$prefixは割り当てではありません。 @ Jeffの方が説明が長い

代わりに関数を使用して同様のことを行うことができます。

$ prefix() { hello=hi "$@"; }
$ prefix bash -c 'echo "$hello"'
hi

...そしてあなたが好きならそれらを積み重ねることもできます:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
3
ilkkachu