104

次のスニペットを検討してください。

$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz

ここでは 1 行目に設定$SOMEVARAAAました。2 行目にエコーすると、AAA期待どおりの内容が得られます。

しかし、同じコマンドラインで変数を指定しようとすると、次のようになりますecho

$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz

BBB...期待どおりに取得できません- 古い値 ( AAA) が取得されます。

これが本来あるべき姿ですか?もしそうなら、どうして次のような変数を指定LD_PRELOAD=/... program args ...して機能させることができるのでしょうか? 私は何が欠けていますか?

4

9 に答える 9

111

表示されるのは、期待される動作です。問題は、変更された環境でコマンドを呼び出す前に、親シェル$SOMEVARがコマンド ラインで評価することです。$SOMEVAR環境が設定されるまで、deferredの評価を取得する必要があります。

当面のオプションは次のとおりです。

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz.
  2. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'.

これらはどちらも一重引用符を使用して、親シェルが評価されないようにし$SOMEVARます。環境に設定された後にのみ評価されます(一時的に、単一のコマンドの期間中)。

別のオプションは、サブシェル表記を使用することです ( Marcus Kuhn回答でも提案されています)。

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

変数はサブシェルでのみ設定されます

于 2012-06-07T19:24:36.403 に答える
41

問題、再訪

率直に言って、マニュアルはこの点で混乱しています。GNU Bash のマニュアルには次のように書かれています。

単純なコマンドまたは関数の環境 [これはビルトインを除外することに注意してください] は、シェル パラメータで説明されているように、パラメータの割り当てを前に付けることで一時的に拡張できます。これらの代入ステートメントは、そのコマンドによって表示される環境のみに影響します。

文を実際に解析すると、コマンド/関数の環境は変更されていますが、親プロセスの環境は変更されていません。したがって、これは機能します:

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

env コマンドの環境は、実行前に変更されているためです。ただし、これは機能しません。

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

シェルによってパラメーター展開が実行されるためです。

通訳者の手順

問題の別の部分は、Bashがインタープリターに対して次の手順を定義していることです。

  1. ファイル (「シェル スクリプト」を参照)、-c 呼び出しオプションの引数として指定された文字列 (「Bash の呼び出し」を参照)、またはユーザーの端末から入力を読み取ります。
  2. 引用で説明されている引用規則に従って、入力を単語と演算子に分割します。これらのトークンは、メタ文字で区切られています。エイリアスの展開は、このステップで実行されます (エイリアスを参照)。
  3. トークンを単純なコマンドと複合コマンドに解析します (シェル コマンドを参照)。
  4. さまざまなシェル展開 (Shell Expansions を参照) を実行し、展開されたトークンをファイル名 (Filename Expansion を参照) とコマンドと引数のリストに分割します。
  5. 必要なリダイレクト (「リダイレクト」を参照) を実行し、リダイレクト演算子とそのオペランドを引数リストから削除します。
  6. コマンドを実行します (コマンドの実行を参照)。
  7. オプションで、コマンドが完了するまで待機し、その終了ステータスを収集します (「終了ステータス」を参照)。

ここで起こっていることは、ビルトインが独自の実行環境を取得しないため、変更された環境が表示されないということです。さらに、単純なコマンド (例: /bin/echo)変更された環境を取得します (これが env の例が機能した理由です) が、シェルの拡張はステップ 4 で現在の環境で行われています。

つまり、'aaa $TESTVAR ccc' を /bin/echo に渡していません。補間された文字列 (現在の環境で展開されたもの) を /bin/echo に渡します。この場合、現在の環境にはTESTVARがないため、コマンドに「aaa ccc」を渡すだけです。

概要

ドキュメントはより明確になる可能性があります。スタック オーバーフローがあるのは良いことです。

関連項目

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment

于 2012-06-07T20:21:20.490 に答える
24

あなたが望むものを達成するために、

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

理由:

  • 次のコマンドからセミコロンまたは改行で割り当てを区切る必要があります。そうしないと、次のコマンド(エコー)のパラメーター展開が行われる前に割り当てが実行されません。

  • 現在の行を超えて持続しないように、サブシェル環境内で割り当てを行う必要があります。

このソリューションは、他のいくつかの提案よりも短く、すっきりとしていて効率的です。特に、新しいプロセスを作成することはありません。

于 2013-01-18T14:25:01.817 に答える
11

その理由は、これが 1 行の環境変数を設定するためです。でも、echo拡張はbashしないです。したがって、 echo コマンドのコンテキスト内であってSOME_VARも、コマンドが実行される前に変数が実際に展開されます。BBB

効果を確認するには、次のようにします。

$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB

ここでは、変数は子プロセスが実行されるまで展開されないため、更新された値が表示されます。SOME_VARIABLE親シェルでもう一度確認するとAAA、予想どおり、まだです。

于 2012-06-07T19:31:05.430 に答える
11

POSIX 仕様を調べて、これが bash だけでなく準拠シェルでこのように動作する理由を理解しましょう。


2.10.2、シェルの文法規則

規則 7(b) から、割り当てが単純なコマンドの前にある場合をカバーします。

「=」の前にあるすべての文字が有効な名前を形成する場合 (IEEE Std 1003.1-2001、セクション 3.230、名前の基本定義の巻を参照)、トークン ASSIGNMENT_WORD が返されます。(引用符で囲まれた文字は、有効な名前の形成に参加できません。)

[...]

NAME への割り当ては、Simple Commands で指定されているとおりに行われます。

したがって、POSIX 準拠のシェルでは、この割り当てを解析する必要があります。


2.9.1、簡単なコマンド

  1. リダイレクションは、リダイレクションで説明されているとおりに実行されるものとします。

  2. 各変数の割り当ては、値を割り当てる前に、チルダ展開、パラメーター展開、コマンド置換、算術展開、および引用符の削除のために展開されます。

[...]

コマンド名が返されない場合、変数の割り当ては現在の実行環境に影響を与えます。それ以外の場合、変数の割り当てはコマンドの実行環境用にエクスポートされ、現在の実行環境には影響しません (特殊な組み込みを除く)。変数割り当てのいずれかが読み取り専用変数に値を割り当てようとすると、変数割り当てエラーが発生します。これらのエラーの結果については、シェル エラーの結果を参照してください。

したがって、単純なコマンドのプレフィックスの一部で指定された割り当てはエクスポートする必要があり、呼び出されるコマンドが特別な組み込みコマンドでない限り、「現在のシェル環境」に影響を与えてはなりません。さらに、これらのステップは、本来、コマンド呼び出しプロセスの後半で発生する必要があるリダイレクトに従います。


2.12、シェル実行環境

特別な組み込みユーティリティ (「特別な組み込みユーティリティ」を参照) 以外のユーティリティは、以下で構成される別の環境で呼び出されます。これらのオブジェクトの初期値は、以下に示す場合を除いて、親シェルの値と同じでなければなりません。

[...]

export 属性を持つ変数は、コマンドの実行中に明示的にエクスポートされた変数とともに、ユーティリティ環境変数に渡されます。


したがって、これらの変数は、fork の後、呼び出されたコマンドを実行する前に、サブシェルによって展開され、仕様により、子の環境だけに影響を与える必要があります。


さて、いくつかの異なる動作について:

SOMEVAR=BBB sh -c 'echo "$SOMEVAR"'

sh...起動時に環境変数からシェル変数を作成するインスタンスの恩恵を受けます(POSIX仕様のセクション2.5.3で要求されているように)。


ちなみに、あなたが求めている構文は、 subshel​​l内の代入ではなく、単純な command内の代入のためのものであることに注意してください。次のように、パイプラインに関連するサブシェルで割り当てを制御できます。

{ SOMEVAR=BBB; echo "$SOMEVAR"; } | somecommand ...

...パイプラインの最初のコンポーネントを実行しているサブシェルに割り当てを入れます(シェルが実際にそのコンポーネントをサブシェルで実行している場合、これはPOSIXに関する限り未定義の動作です;仕様から:「拡張として、ただし、パイプライン内の一部またはすべてのコマンドが現在の環境で実行される可能性があります" )。

于 2015-06-22T15:52:18.033 に答える
6

簡単に言えば、はコマンドが呼び出される$SOMEVARに評価され、コマンドの前に追加すると、実行中のコマンドの環境が変更されます。SOMEVAR=BBB

Charles Duffy が言ったように、同様の構文で変数を評価する中間shプロセスを追加できますが、おそらくもう少し複雑なことをしたいと思うでしょう。

于 2015-06-22T19:00:42.910 に答える
1
SOMEVAR=BBB echo zzz $SOMEVAR zzz

SOMEVAR=BBB環境変数に追加してから実行しecho zzz $SOMEVAR zzzます。は、事前に設定し$SOMEVARたシェル変数を参照します。SOMEVARAAA

セミコロンを追加するとSOMEVAR=BBB; echo zzz $SOMEVAR zzz、シェル変数が に設定され、セミコロンBBBの後のコマンドが実行されます。つまりecho zzz $SOMEVAR zzz、 and が生成されzzz BBB zzzます。

このコマンドを試してください:

SOMEVAR=BBB env | less

そして環境を見る。

于 2015-06-22T14:47:14.060 に答える
1

1 つの代替手段を次に示します。

SOMEVAR=BBB && echo zzz $SOMEVAR zzz
于 2014-10-28T17:24:15.163 に答える
1
SOMEVAR=BBB; echo zzz $SOMEVAR zzz

使う ; 同じ行にあるステートメントを分離します。

于 2012-06-07T19:29:27.363 に答える