コマンドのbashスクリプトの一時的な値


11

以下のコマンドのように、

if true; then
   IFS=":" read a b c d e f <<< "$test"

本は、IFS ":"メインコマンド(read a b c d e f <<< "$value")の前に値割り当てコマンド()を使用すると、その値が一時的にメインコマンドで有効になると述べています。したがって、readコマンドは区切り文字を使用します:

しかし、このコマンドのように、

if true; then
   HOME="hello" echo "$HOME"

エコーメッセージはhelloではありません。上記のコマンドの本当の意味は何ですか?

回答:


5

これは、評価がどのように機能するかという問題に帰着します。どちらの例も同じように機能し、シェル(bash、ここ)が変数を展開する方法が原因で問題が発生します。

このコマンドを書くとき:

HOME="foo" echo $HOME

$HOME展開されているコマンドが実行される前に。したがって、コマンドに設定した新しい値ではなく、元の値に展開されます。HOME変数は、実際にその環境で変更されたechoコマンドは、しかし、あなたが印刷され、中に実行されている$HOME親から。

説明のために、これを考慮してください:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

上記のように、最初のコマンドはの一時的に変更された値をHOME出力し、2番目のコマンドは元の値を出力して、変数が一時的にのみ変更されたことを示しています。bash -c ...コマンドは' '二重引用符()ではなく単一引用符()で囲まれているため、" "変数は展開されず、そのまま新しいbashプロセスに渡されます。この新しいプロセスはそれを展開し、設定された新しい値を出力します。これを使用すると、これが発生することがわかりますset -x

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

上記のように、変数 $HOMEがに渡されることはありませんecho。拡張された値のみが表示されます。と比べて:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

ここでは、単一引用符のため、変数ではなく変数が新しいプロセスに渡されます。


7

シェルが行を解析しているとき、それは行を単語にトークン化し、単語に対してさまざまな展開を(順番に)実行してから、コマンドを実行します。

と思います test=1:2:3:4:5:6

このコマンドを見てみましょう: IFS=":" read a b c d e f <<< "$test"

トークン化後、パラメーターの展開が行われます。IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

シェルは、readコマンドの実行中にIFS変数を設定し、read$ IFSをそのinputに適用する方法を知っており、変数名に値を与えます。

このコマンドのストーリーは似ていますが、結果は異なります。 HOME="hello" echo "$HOME"

コマンドの開始前にパラメーターの展開が行われるため、シェルには次の機能があります。

HOME="hello" echo "/home/username"

そして、echoコマンドの実行中、$ HOMEの新しい値はまったく使用されません。

あなたがやろうとしていることを達成するために、

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

または

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

ただし、最初のものは選択しないでください。


最初のものを選ぶ方が良いでしょう。少なくともそれはかなり高速です。evalが答えである場合、おそらく間違った質問をしているでしょう。しかし、誰かが何らかの理由でそれをしなければならない場合、答えを変更しても、質問自体の誤りが少なくなることはありません。別の解決策は、それを関数にラップして使用することlocalです。
user23013

なぜeval解決策を避けるべきですか?
DarkHeart 2016

入力を厳密に制御しない場合は、他の人がプログラムに挿入できるようにするコードに非常に注意する必要があります。
グレン・ジャックマン2016

-1

スコープには、環境変数とローカル変数の2つがあります。環境変数は、すべてのプロセス(参照のために有効なsetenvgetenvローカル変数のみがこのシェルセッション内でアクティブになっている一方で、)。(それは明白な区別ではありません...)

暗黙的env(例のように)は環境を変更しますecho ...が、ローカルのものを使用します-影響envはありません。

ローカル変数を変更するには、たとえば、

( HOME="foo" ; echo "$HOME" )

ここで、括弧はこの割り当ての範囲を定義します。


1
これは変数のスコープとは関係ありません。問題は、変数が子シェルに渡される前に展開されていることです。
terdon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.