コマンドの前に「ワンタイム変数の割り当て」を付けたときに変数を bash 拡張しないのはなぜですか?

コマンドの前に「ワンタイム変数の割り当て」を付けたときに変数を bash 拡張しないのはなぜですか?

このbashコマンドを実行し、ステートメントの前にプレフィックスを付けた場合、変数はfruitこのコマンドの実行中にのみ存在する必要があります。

$ fruit=apple echo $fruit

$

結果は空行です。なぜ?

ワイルドカードを引用したコメントこの問題:

パラメータ拡張はシェルによって実行されます。 「fruit」変数はシェル変数ではなく、「echo」コマンド環境の環境変数にすぎません。

環境変数はまだ変数なので、echoコマンドで使用できますか?

答え1

問題は、現在のシェルが変数を早期に拡張することです。そのコンテキストに設定されていないため、コマンドにecho引数がありません。つまり、コマンドは次のようになります。

$ fruit=apple echo

一重引用符で変数が早期に拡張されないようにする回避策は次のとおりです。

$ fruit=apple sh -c 'echo $fruit'

fruitあるいは、変数が実行されたコマンドに正しく渡されたことを示す1行のシェルスクリプトを使用することもできます。

$ cat /tmp/echof
echo $fruit
$ /tmp/echof

$ fruit=apple /tmp/echof
apple
$ echo $fruit

$

この問題は予期しない議論と議論を引き起こしたため、いくつかのコメントは次のとおりです。

  • 変数fruitをエクスポートしたかどうかは、その動作には影響しません。重要なのは、シェルが変数を拡張する正確な瞬間の変数値が何であるかです。
$輸出フルーツ=バナナ
$fruit=アップルエコ $fruit
バナナ
  • このコマンドが組み込まれているという事実は、echoOPの質問には影響しません。ただし、場合によっては、この構文で組み込み関数またはシェル関数を使用すると、次のような予期しない副作用が発生する可能性があります。
$輸出フルーツ=バナナ
$fruit=apple 評価 'echo $fruit'
りんご
$echo$フルーツ
りんご
  • 2つの間に類似点がありますが、ここに尋ねる質問そしてそれはまったく同じ質問ではありません。もう一つの質問として、IFSシェル噴射 その他$varここで変数、fruit一時変数の値は、シェル実行時にはまだ使用できません。拡大するこれ同じ変える

  • しかも別の問題OPは使用された構文の重要性について尋ねています。ここで、OPは重要性を認識しますが、予期しない動作を報告し、その理由を尋ねます。つまり、「これはなぜ機能しないのですか?」まあ、他の質問に掲示された都合のないスクリーンショットをもう少し注意深く読んだ結果、そこにも実際に同じ状況が説明されているので( BAZ=jake echo $BAZ) はい、結局重複です。

答え2

これを正しく理解するには、まず区別する必要があります。シェル変数~から環境変数

環境変数は、内部で使用しているかどうかに関係なく、すべてのプロセスが所有する属性です。実行中にもsleep 10環境変数があります。すべてのプロセスには、PID(プロセス識別子)、現在の作業ディレクトリ(cwd)、PPID(親PID)、パラメータリスト(空の場合でも)があります。すべてのプロセスには、フォーク時に親プロセスから継承される「環境」もあります。

ユーティリティの作成者(Cでコードを書く人)の観点から、プロセスは環境変数を設定、設定解除、または変更できます。ただし、スクリプト作成者の観点からは、ほとんどのツールはユーザーにこの機能を提供しません。代わりに、あなたはあなたのシェルプロセス環境を変更してから呼び出すコマンド(外部バイナリ)を実行すると、そのプロセス環境を継承します。 (シェル自体の環境は変更することができ、変更は継承するか、フォーク後に呼び出されたコマンドを実行する前にシェルにそれを変更するように指示できます。どちらも環境が継承されます。確認してください。)

シェル変数は別の話です。しかし、シェル内部彼らは同じように動作します。違いは、単純な「シェル変数」は、呼び出すコマンドの動作を変更したり影響を与えたりしないことです。~からあなたの殻。正しい用語で表現すると、これらの区別は実際にはわずかに異なって表現されます。出口シェル変数は呼び出すツール環境の一部になり、シェル変数はいいえ輸出はできません。しかし、「シェル変数」としてエクスポートされないシェル変数を参照し、「シェル変数」としてエクスポートされたシェル変数を参照すると、通信がより役立つことがわかりました。はい「環境変数」としてエクスポートされました。はいシェル作成プロセスの観点から環境変数を表示します。


テキストが多すぎます。いくつかの例を見て、何が起こっているのかを説明します。

$ somevar=myfile
$ ls -l "$somevar"
-rw-r--r--  1 Myname  staff  0 May 29 19:12 myfile
$ 

この例では、somevar特別なものではなく、シェル変数にすぎません。シェルパラメータ拡張(参照LESS='+/Parameter Expansion' man bash)仕事が起こる今後実行可能ファイルはls実際にはロードされますが("exec")lsコマンド(プロセス)はロードされません。より文字列 "ドル記号somevar"。単に "myfile"という文字列を見て、それを現在の作業ディレクトリにあるファイルへのパスとして解釈し、それに関する情報を取得して印刷します。

export somevarコマンドの前に実行すると、ls事実がsomevar=myfile次に表示されます。環境processですが、コマンドはこの変数を使用して何もしないため、ls影響はありません。ls行ってみてください影響環境変数の場合は、呼び出すプロセスが実際に確認し、それを使用して操作を実行する環境変数を選択する必要があります。


bc: 基本電卓

おそらくより良い例があるかもしれませんが、これはあまり複雑ではない私が思いついた例です。最初に知っておくべきことは、これがbc数学表現を処理して計算できる基本的な計算機です。 (入力ファイルの内容を処理した後に標準入力を処理します。例では標準入力を使用しません。Ctrl-Dのみを押すと下のテキストには表示されません。さらに次を使用します。を表示-qしません。

私が説明する環境変数を以下に説明しますman bc

   BC_ENV_ARGS
      This is another mechanism to get arguments to bc.  The format is
      the  same  as  the  command line arguments.  These arguments are
      processed first, so any files listed in  the  environment  argu-
      ments  are  processed  before  any  command line argument files.
      This allows the user to set up "standard" options and  files  to
      be  processed at every invocation of bc.  The files in the envi-
      ronment variables would typically contain  function  definitions
      for functions the user wants defined every time bc is run.

スタート:

$ cat file1
5*5
$ bc -q file1
25
$ cat file2
6*7
8+9+10
$ bc -q file2
42
27
$ bc -q file1 file2
25
42
27
$

これは単にbcそれがどのように機能するかを示すことです。それぞれの場合に「入力終了」信号を送信するには、Ctrl-Dを押す必要がありますbc

それでは、環境変数を渡してみましょう。まっすぐ到着bc:

$ BC_ENV_ARGS=file1 bc -q file2
25
42
27
$ echo "$BC_ENV_ARGS"

$ bc -q file2
42
27
$

この変数に入れた内容は次のとおりです。いいえechoこれは後でコマンドで確認できます。同じコマンドの一部として(セミコロンなしで)割り当てを実行することで、変数を次に割り当てます。部分環境bc- 私たちが実行しているシェル自体は影響を受けません。

BC_ENV_ARGSそれでは設定してみましょう。シェル変える:

$ BC_ENV_ARGS=file1
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
42
27
$

echoここで私たちのコマンドはコンテンツを見ることができますが、環境の一部ではないので、特別なことはbcできbcません。

もちろん、変数自体をbc引数リストに入れると、次のような内容が表示されます。

$ bc -q "$BC_ENV_ARGS"
25
$ 

さてここは、シェル変数を展開し、実際にsfile1に表示される内容を展開します。bcパラメータリスト。 したがって、これはまだ環境変数ではなくシェル変数として使用されます。

これで、シェル変数と環境変数になるように「エクスポート」します。

$ export BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25
42
27
$

ここではコマンドラインでは言及されていませんが、ここではfile1以前に処理されたものを見ることができます。file2これはシェル環境の一部であり、bcプロセスの実行時に環境の一部になるため、この環境変数の値は次のとおりです。遺伝学bcタスクの実行方法に影響します。

コマンドごとにそれを上書きすることも、null値として書き換えることもできます。

$ BC_ENV_ARGS= bc -q file2
42
27
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25 
42
27
$ 

ただし、ご覧のとおり、変数はシェルで設定およびエクスポートされたままになり、シェルbc自体とそれ以降のコマンドで表示できます。いいえこの値をオーバーライドします。 「エクスポートのキャンセル」または「設定解除」しない限り、そのまま残ります。私は後者を選びます:

$ unset BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"

$ bc -q file2
42
27
$ 

他のシェルの作成に関連する別の例:

シェルに次のコマンドを順番に入力し、結果を考慮します。実行する前に結果を予測できることを確認してください。

# fruit is not set
echo "$fruit"
sh -c 'echo "$fruit"'
# fruit is set as a shell variable in the current shell only
fruit=apple
echo "$fruit"
sh -c 'echo "$fruit"'
sh -c "echo $fruit" ### NOT advised for use in scripts, for illustration only
# fruit is exported, so it's accessible in current AND new processes
export fruit
echo "$fruit"
sh -c 'echo "$fruit"'
echo '$fruit' ### I threw this in to make sure you're not confused on quoting
# fruit is unset again
unset fruit
echo "$fruit"
sh -c 'echo "$fruit"'
# setting fruit directly in environment of single command but NOT in current shell
fruit=apple sh -c 'echo "$fruit"'
echo "$fruit"
fruit=apple echo "$fruit"
# showing current shell is unaffected by directly setting env of single command
fruit=cherry
echo "$fruit"
fruit=apricot sh -c 'echo "$fruit"'
echo "$fruit"
sh -c 'echo "$fruit"'

最後はボーナストリックです。順番に実行される次のコマンドの出力を予測できますか? :)

fruit=banana
fruit=orange sh -c 'fruit=lemon echo "$fruit"; echo "$fruit"; export fruit=peach'
echo "$fruit"

コメントに必要な説明を記載してください。これが役に立つと確信しています。しかし、それでも役に立ちます。

答え3

変数の割り当て前にコマンドライン拡張が発生するためです。標準にはそのように記載されています。:

与えられた単純なコマンドを実行する必要がある場合は、次のことを行う必要があります。

  1. 変数割り当てによって識別された単語は、ステップ3と4で処理するために保存されます。

  2. 変数の割り当てやリダイレクト以外の単語は拡張する必要があります。 [...]

  3. リダイレクトは、リダイレクトで説明されているように実行する必要があります。

  4. 各変数の割り当ては、割り当て前に拡張[...]する必要があります。

順序に注意してください。最初のステップでは、割り当てのみが実行されます。保存済みその後、別の単語を展開し、最後にのみ変数割り当てを実行します。

もちろん、標準はいつもそうだったので、単にそう言うこともでき、既存の動作を性文化するだけであるからです。歴史やその理由については何も言及しません。シェルは特定の時点で割り当てのように見える単語を認識する必要があります(コマンドの一部として含まれていない場合のみ)。したがって、最初に変数を割り当ててから、コマンドラインから何でも拡張するなど、さまざまな順序で動作できると思います。 (または左から右に進んでください...)

関連情報