web-dev-qa-db-ja.com

Bash:コマンドラインでエコーの環境変数を指定しますか?

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

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

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

しかし、その後、echoと同じコマンドラインで変数を指定しようとすると:

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

...期待どおりBBBを取得できません-古い値(AAA)を取得します。

これは物事がどうあるべきか?もしそうなら、どうしてLD_PRELOAD=/... program args ...のような変数を指定して動作させることができますか?私は何が欠けていますか?

80
sdaau

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

直接のオプションは次のとおりです。

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

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

別のオプションは、サブシェル表記を使用することです( answerMarcus Kuhn によっても示唆されています):

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

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

88

問題、再訪

率直に言って、マニュアルはこの点で混乱しています。 GNU Bashマニュアル のコメント:

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

本当に文を解析する場合、コマンド/関数のenvironmentは変更されますが、親プロセスの環境は変更されません。したがって、これは動作します:

$ 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. さまざまなシェル展開(シェル展開を参照)を実行し、展開されたトークンをファイル名(ファイル名展開を参照)およびコマンドと引数のリストに分割します。
  5. 必要なリダイレクト(「リダイレクト」を参照)を実行し、リダイレクト演算子とそのオペランドを引数リストから削除します。
  6. コマンドを実行します(コマンドの実行を参照)。
  7. オプションで、コマンドが完了するまで待機し、終了ステータスを収集します(終了ステータスを参照)。

ここで起こっているのは、ビルトインが独自の実行環境を取得していないため、変更された環境が表示されないことです。さらに、単純なコマンド(例:/ bin/echo)doは変更された環境を取得します(これがenvの例が機能した理由です)が、シェルの展開は手順4でcurrent環境に配置します。

つまり、「aaa $ TESTVAR ccc」を/ bin/echoに渡していないということです。補間された文字列(現在の環境で展開される)を/ bin/echoに渡します。この場合、現在の環境には[〜#〜] testvar [〜#〜]がないため、単に 'aaa ccc'をコマンド。

概要

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

こちらもご覧ください

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

33
Todd A. Jacobs

目的を達成するには、使用します

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

理由:

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

  • subshel​​l環境内で割り当てを行う必要があります。これは、現在の行を超えて保持されないようにするためです。

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

19
Markus Kuhn

これは、これにより1行の環境変数が設定されるためです。ただし、echoは展開を行いませんが、bashは展開します。したがって、echoコマンドのコンテキストではSOME_VARBBBであっても、実際にはコマンドが実行される前に変数が展開されます。

効果を確認するには、次のようなことができます。

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

ここでは、子プロセスが実行されるまで変数は展開されないため、更新された値が表示されます。親シェルでSOME_VARIABLEを再度チェックすると、期待どおりAAAのままです。

10
FatalError

代替案は次のとおりです。

SOMEVAR=BBB && echo zzz $SOMEVAR zzz
1
brian
SOMEVAR=BBB; echo zzz $SOMEVAR zzz

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

1
Kyros