「A = 10 echo $ A」が10を印刷しないのはなぜですか?


25

このコマンド:

A=10 echo $A

空の行を出力します。どうして10?インプレースの一時的な環境設定が機能しないのはなぜですか?

解決策ではなく、理由と説明を知りたい。

使った

LANG=C gcc ...

gccにシステム言語(中国語)の代わりにフォールバック言語(英語)を使用するよう強制します。したがって、VAR=value接頭辞は、それに続くコマンドの一時的な環境をセットアップすると想定しています。しかし、誤解があるように見えます。

回答:


22

コマンドを評価するさまざまなステップが発生する順序の問題です。

A=10 echo $A最初に、コマンドを3つの単語A=10からなる単純なコマンドに解析します、echoそして$A。次に、各単語は変数置換、つまり$A値への変換などの変数展開の変換を受けます(目に見えないステップは省略しています)。

Afoo最初に値を持っている場合、展開手順の結果は、3つの単語がまだある単純なコマンドA=10echoなります:、およびfoo。(シェルはこの時点で、最初に引用符で囲まれた文字を記憶します。この場合はどれもありません。)次のステップはコマンドの実行です。以来、A=10等号、続いて有効な変数名で始まり、それが割り当てとして扱われます。変数A10、コマンドの実行中にシェルと環境の両方で設定されます。(通常、シェル変数としてだけでなく、環境内で使用するように記述export Aする必要がありAます。これは例外です。)次の単語は割り当てではないため、コマンド名(組み込みコマンド)として扱われます。のechoコマンドは変数に依存しないためA=10 echo $A、と同じ効果がありecho $Aます。

コマンドの実行中のみ変数を設定したいが、コマンドの実行中に割り当てを考慮する場合は、サブシェルを使用できます。括弧で示されたサブシェルは、すべての状態変更(変数の割り当て、現在のディレクトリ、関数定義など)をサブシェルに対してローカルにします。

(A=10; echo $A)

ことを確認してくださいexport A=10あなたはそれが外部プログラムによって見られるように、環境に変数をエクスポートする場合。


おかげで、言っA=10 (echo $A)てもらうことができます10か?
地球エンジン

2
@EarthEngineいいえ、構文エラーになります。割り当ては、単純なコマンドの先頭にある必要があります(つまり、コマンド名といくつかのパラメーター、およびオプションでいくつかの初期割り当てといくつかのリダイレクト)。A=10; (echo $A)出力します10A、スクリプトの残りの部分も設定します。
ジル「SO-悪であるのをやめる」

2
@EarthEngineしかし、あなたは言うことができますA=10 eval 'echo $A'$A行全体が評価されるまで、単一引用符は解釈されなくなります...その時点までにA = 10になります。この答えは受け入れられたものよりも正しいと思います。
オリ

これは正しい説明だと思います。振る舞いの理由は、の拡張$Aと割り当てが発生する順序だけAです。たとえば、A=5; A=6 let 'a=A'; echo $a戻り値65ありませんlet。組み込みコマンドなので、サブシェルを開始するとは思いません。
デビッドオンガロ

@EarthEngine:正しい説明があると言われていたときの順序評価の、誤解を招くことがありますA=10 echo $AでしょうではないA=10、その後の任意のコマンドのために、彼らは別の行にあっても(割り当てが時に明確にして、すでに評価されます)。それはについてです、順序についてではありませんスコープ
MestreLion

37

あなたは使用する場合LANG=C gcc ... 、何が起こるかのためにシェルが設定されることLANGあるgcc環境の唯一の、そしてないため、現在の環境自体は、(注を参照)。したがって、gcc終了後LANG、以前の値に戻ります(または設定解除されます)。

さらに、使用するA=10 echo $Aときは、エコーではなく$ Aを置換するシェルであり、この代入(「展開」と呼ばれる)はステートメントが評価される前(割り当てを含む)に発生するため、期待どおりに動作するにはA値が既に設定されている必要がありますその文の前の現在の環境で。

そのためA=10 echo $A、期待どおりに機能しません。A=10エコーが設定されますが、エコーは内部的に環境変数の値を無視しますA。そして、$Aの値のセットに置き換えられる現在のシェル(noneです)、そして次いでエコーに引数として渡されます。

したがって、あなたの仮定は正しいです:VAR=value command 動作しますが、これはcommand内部でVARを使用する場合にのみ関連します。そうでない場合、あなたはまだ渡すことができますvalueよう、引数command、しかし、引数が置き換えられている現在のシェルので、それらは使用前に設定する必要があります。VAR=value; command "$VAR"

実行可能スクリプトの作成方法がわかっている場合は、テストとしてこれを試すことができます。

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

名前を付けて保存してtestscriptみてください:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

最後になりましたが、違いを知ることの価値がシェル環境変数とプログラムの引数を

以下に参考資料を示します。

(*)注:技術的にはシェルがない、あまりにも現在の環境に設定され、そしてなぜここにある:いくつかのコマンドのようなechoreadtestしているシェル組み込みコマンド、そのように彼らは、子プロセスを生成しません。現在の環境で実行されます。ただし、シェルは、コマンドが実行されるまで割り当てを継続するだけなので、すべての実際的な目的で効果は​​同じです。割り当てはその単一のコマンドによってのみ表示されます。


2
この説明は実際には間違っていますが、少数の例外を除いてすべて正しい結論に至ります。実際の説明は展開の順序です:$A割り当てが行われる前に評価されます。あなたの説明は、その動作が変数の値に依存する通常の組み込みユーティリティの場合にのみ失敗すると思います:組み込みは割り当てられた値を見ます。一般的な例はでIFS=: read one two three rest、コロンで区切られたフィールドを読み取ります:read組み込みはの値を見ますIFS
ジル 'SO-悪であるのをやめる'

「現在のシェル自体ではない」のは誤りです。変数は現在のシェルで設定されますが、現在の単純なコマンドでのみ有効です。気にするならecho、の値10を見るでしょうA
ジル 'SO-悪であるのをやめる'

@Gilles:説明をありがとう。私はこの微妙さを知りませんでした。したがって、正しく理解できれば、bash 現在の環境に設定する必要があります。そうしないと、ビルトイン(新しいを生成しないpid)が他の「通常の」コマンドのように割り当てを見ることができません。ただし、割り当てスコープを制限するためにコマンドが実行されると、設定が解除されます。これは正しいですか、それに応じて答えを修正します。PS:技術的なことはさておき、答えは評価の順序ではなくスコープの側面に重点を置くべきだと思います。そうでなければ、A=10 test; echo $A10
MestreLion 14年

3

あなたが明らかに望んでいることをする1つのおそらくきれいな方法は、コマンドを発行することです:

A=10 eval 'echo $A'

これは、実際には、値10の置換を$ Aの場所へ後のコンテキスト(つまり、割り当てについて既に知っているevalの「内部」)に延期します。単一引用符は必須です。このような構成は、現在の環境を汚染するリスクを冒すことなく、割り当てを目的のコマンド(この場合はエコー)に明確に伝えます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.