コマンドの前に変数をbashで有効に設定するのはなぜですか?


68

私はちょうどそのようになど、いくつかの答え遭遇した区切りのテキストファイル...解析する構文を使用します。

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

ここで、IFS変数はreadコマンドの前に設定されます。

私はbashのリファレンスを読んでいますが、なぜこれが合法であるのか分かりません。

私は試した

$ x="once upon" y="a time" echo $x $y

bashコマンドプロンプトから、しかし何もエコーされませんでした。誰かがその構文がIFS変数をそのように設定できるようにする参照で定義されている場所を教えてもらえますか?それは特別な場合ですか、他の変数と同様の何かを行うことができますか?


回答:


69

Shell Grammar、Simple Commands(強調を追加)の下にあります:

単純なコマンドは、一連のオプションの変数割り当ての 後に空白で区切られた単語とリダイレクトが続き 、制御演算子で終了します。最初のワードは、実行するコマンドを指定し、引数ゼロとして渡されます。残りの単語は、引数として呼び出されたコマンドに渡されます。

したがって、任意の変数を渡すことができます。あなたのecho変数はシェルで設定されていない、コマンドに渡されるので、例は動作しません。シェルは、コマンドを呼び出す前に展開$xします。これは、たとえば次のように機能します。$y

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time

ありがとう!私は尋ねる前に多くのグーグルをしました、そして、それが参照でそれが言った場所を見つけようとしました、運なしで。私はbashスクリプトを書くのをより良くしようとしています、そしてあなたの答えは助けます。
マイクリッパー14

1
うーん、私はより良いリファレンスを見つける必要があると思います、私の(上記のリンクを参照)は、シェル文法セクションがないとは言いません。
マイクリッパー14

1
@MikeLippertそのリファレンスの3.7.4を参照してください(「単純なコマンドまたは関数の環境は、パラメーター割り当てをプレフィックスとして付けることにより一時的に拡張できます」)。参照は古いバージョンのbashからのものだと思います。私はちょうど走ったman bash...私のシステムで
derobert

29

定義された変数は、分岐したプロセスの環境変数のようになります。

走ったら

A="b" echo $A

その後、bashは最初に展開$A""てから実行されます

A="b" echo

正しい方法は次のとおりです。

x="once upon" y="a time" bash -c 'echo $x $y'

の単一引用符に注意してbash -cください。そうしないと、上記と同じ問題が発生します。

そのため、bashの組み込み「読み取り」コマンドは環境変数でIFSを探し、findを実行するため、ループの例は合法,です。したがって、

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

印刷します TEST is and I is test

最後に、構文については、forループに文字列が必要です。したがって、バッククォートを使用してコマンドにする必要がありました。ただし、whileループはなどのコマンド構文を想定していますIFS=, read xx yy zz


1
おかげで、私はあなたの答えから尋ねたよりも少し多くを学びました。私もあなたの答えに投票しますが、私はまだ許可されていません。
マイクリッパー14

1
ありがとう、私はそれが私が望んでいたことです。そして、賛成票はあなたの感謝を聞くよりも意味がありません!
マイクフェアハースト

コードの最初の行でコメントを明確にする:はい、設定されていないA変数bashは$A空の文字列に展開されますが、混乱を避ける""ため、コードはに等しくないため使用しませんA="b" echo ""。への引数はありませんecho
パブーク

8

man bash

環境

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

変数は、変数の割り当てが行われる前に展開されます。明らかな理由でvar=x、それは他の方法でも機能しますが、機能しvar=$othervarません。すなわち、$xそれが利用可能になる前にあなたが必要です。しかし、それは主な問題ではありません。主な問題は、コマンドラインはシェル環境でのみ変更できますが、割り当てはシェル環境の一部にならないことです。

複数の機能を混在させます。コマンドラインの置換が必要ですが、変数定義をコマンド環境に配置します。コマンドラインの置換は、シェルによって行われる必要があります。環境は、呼び出されたコマンドによって明示的に使用される必要があります。これが行われるかどうか、およびその方法は、コマンドによって異なります。

この使用法の利点は、シェル環境に影響を与えずにサブプロセスの環境を設定できることです。

x="once upon" y="a time" bash -c 'echo $x $y'

その場合、両方の機能が組み合わされているため、期待どおりに機能します。コマンドラインの置換は、呼び出し元のシェルではなく、サブプロセスのシェルによって行われます。


1
この例は組み込みx="once upon" y="a time" eval 'echo $x $y'プロセスなので、サブプロセスが含まれていない場合でも動作するため、それよりも少し微妙ですeval。マンページからの関連する引用はであると思いThe environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignmentsます。質問の例を考えると、この方法でなければならないのreadは、組み込みであり、の一時的に変更された状態で動作するためですIFS
デビッドオンガロ

4

ので、あなたが提供するコマンドが異なっている$x$y展開される前にechoコマンドを実行すると、そうではそれらの値の現在値のシェルが使用されている、ではないecho、それは見ていた場合、その環境で見るでしょう。


コマンドの実行後にコマンドラインの内容を展開するにはどうすればよいですか?
ハウケレイジング14

プリコマンドの割り当てへxy環境のためのものechoで動作します、ではない引数がどのような環境echoに拡張されています。の場合IFS=, read xx yy zz、文字列全体がreadコマンドによって読み取られ、分割されません。次いで、その文字列がの値に応じて分割されIFSに割り当てられた対応片と、xxyy、およびzz
chepner

コマンドの開始後は何も展開されないため、「コマンドの実行前に展開される」という言葉は意味をなさないことを指摘したかっただけです。さらに:あなたは私の答えを一目見たことがありませんか、それとも私は何が起こっているのかの説明が必要だと信じていますか?
ホークレイジング14

1
私は、あなたが説明を必要とすることも、コマンドの実行後に何かを拡張できると主張することもありません。しかし、それは事実であるbash、最初に与えられたコマンドラインを解析してくるコマンドの環境に適用するには、2通の変数の割り当てがあることを認識し、(実行するコマンドを識別echo)、引数で見つかったすべてのパラメータを拡張し、その後、コマンドを実行しますecho拡張された引数を使用します。
chepner

echo私の場合、変数を「見る」ことができるかどうかはわかりませんでした。変数は組み込みコマンドであり、独自の環境を持つサブシェルでは実行されないためです。しかし、私はそれevalも組み込みで試してみましたが、それは実際にそれについて知っています。たとえば、pidが同じであり、eval中に環境が変更されていない場合でも、それだけが設定されてa=xyz eval 'echo $BASHPID $a; grep -z ^a /proc/$BASHPID/{,task/*}/environ'; echo $BASHPID $aいることを示すtry 。(アクセスするには、Linuxでこれを実行する必要があります。)bashは、ここでいくつかの追加の魔法を行うようです。aeval/proc
デビッドオンガロ

2

なぜ合法なの」の全体像を見るつもりです

回答:プログラムを呼び出したり呼び出したりできるように、その呼び出しには特定の変数を持つ変数のみを使用します。

例として、「db_connection」というデータベース接続のパラメーターがあり、通常はテストデータベース接続の名前として「test」を渡します。実際、デフォルトとして設定することもできますが、明示的に渡す必要はありません。ただし、ciデータベースを使用したい場合もあります。したがって、paramを 'ci'として渡すと、呼び出されるプログラムは、すべてのデータベース呼び出しに使用するdbの名前としてそのデータベースパラメーターを使用します。次回の実行では、アプローチを繰り返さずにプログラムを呼び出すだけで、変数は以前のデフォルト値に戻ります。


0

を使用することもできます;。コマンド区切り文字であるため、前に評価されます。

x="once upon" y="a time"; echo $x $y

1
これにより、2つのシェル変数が作成され、指定されたユーティリティで環境変数は作成されません。これは非常に異なるものです。また、現在のシェルに新しい変数が含まれるという副作用もあります。
クサラナナンダ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.