web-dev-qa-db-ja.com

コマンドの前に変数を設定することがbashで合法なのはなぜですか?

区切られたテキストファイルを解析しています... など、いくつかの回答に遭遇しました。

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変数をそのように設定できるようにすることはできますか?それは特別なケースですか、それとも他の変数と同じようなことをすることができますか?

76
Mike Lippert

それはシェル文法の下にあり、シンプルなコマンド(強調が追加されています):

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

したがって、任意の変数を渡すことができます。変数がシェルで設定されずにコマンドに渡されるため、echoの例は機能しません。シェルは$xおよび$ybeforeコマンドを呼び出す。これは機能します、例えば:

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

定義された変数は、フォークされたプロセスの環境変数のようになります。

走れば

A="b" echo $A

次にbashは最初に$A""に展開してから実行します

A="b" echo

これが正しい方法です:

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

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

したがって、bash組み込みの「読み取り」コマンドは環境変数でIFSを探し、,を見つけるため、ループの例は正当です。したがって、

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ループでは文字列が期待されます。したがって、私はそれをコマンドにするためにバックティックを使わなければなりませんでした。ただし、ループはIFS=, read xx yy zzなどのコマンド構文を想定しています。

30
Mike Fairhurst

man bash

環境

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

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

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

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

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

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

8
Hauke Laging

$x$yが展開されているため、提供するコマンドは異なりますbeforeechoコマンドが実行され、その結果current =シェルが使用され、echoがその環境で見た場合に表示される値ではありません。

4
chepner

なぜそれは合法である」の全体像を撮ります

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

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

2
Michael Durrant

;も使用できます。コマンドセパレータであるため、前に評価されます。

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