Shell で次のコマンドを実行すると、なぜ hello が表示されるのか。
$ A='hello' sh -c '$A' hello
調べてみると Simple Commnds というシェルの文法として標準化されていました。つまり基本的なシェルの文法です。これを使うと、コマンド実行前にそのコマンドのために環境変数を割り当てられます。
こんな便利な機能を今まで知りませんでした・・・。
The Open Group Base Specifications Issue 7, 2018 edition [1] の Simple Commnds の説明を読んだところ、コマンドの解析は以下の手順で行われると分かりました。
- 1.Shellの文法規則に従い、変数割当もしくはリダイレクションとして認識された単語を Step3, Step4 の処理のために保存する。
- 2.変数割当でもリダイレクションでもない単語は展開される。もし、あるフィールドがこれら展開されたものの後に残るなら、最初のフィールドがコマンド名だと考えられる。残りのフィールドはコマンドの引数である。
- 3.リダイレクションが処理される。
- 4.各変数割当は値を割り当てるために、チルダ展開、パラメータ展開、コマンドサブスティテューション、算出展開、事前のクォート除去を展開する。
変数割当には注意が必要です。例えば次のコマンドで、変数 $A
の中身は空です。
$ A=1 echo $A
これは、A=1; echo $A
とするか A=1 sh -c 'echo $A'
とすれば回避できました。何故か調べると次のFAQサイト[2]に回答がありました。
[2] によると、A=1 echo $A
とコマンドを入力した場合、Aに1が割り当てられる前に $A
が展開されるので、何も表示されないようです。そのため A=1 sh -c "echo $A"
も同様に、ダブルコーテーション内の $A
が変数割当の前に展開されるため何も表示されません。
ちなみに、A=1 sh -c 'echo $A'
では今現在使っているシェルの環境変数には影響を与えませんが、A=1 B=2
という変数割当のみを含むコマンドを実行した場合、環境変数に A=1
と B=2
が登録されます。このことも The Open Group Base Specifications Issue 7, 2018 edition に記載されていました。