まあ、私はBashで(デフォルトでは「lastpipe」bashオプションが有効になっていない場合)、パイプの後に割り当てられたすべての変数が実際にサブシェルで実行され、変数自体がサブシェルが実行された後に消えることを知っています。親プロセスでは使用できなくなります。しかし、いくつかのテストをしてみると、次のような動作が見つかりました。
A)2番目のコマンド(a = 2)は値を割り当て、次を返します。
[root@centos01]# a=1; a=2; a=10 | echo $a
2
B)3番目のコマンド(a = 10)は値を割り当て、次を返します。
[root@centos01]# a=1; a=2; a=10; a=20 | echo $a
10
C)4番目のコマンド(a = 20)は、以下を割り当てて返します。
[root@centos01]# a=1; a=2; a=10; a=20; touch fileA.txt | echo $a
20
だから:
コマンドシーケンスの最後の変数割り当てが実際に実行されないのはなぜですか? (それではなぜサブシェルに捕捉され、echoコマンドによって返されないのですか?)
テストCでは、「touch」コマンドは実際にディレクトリに「fileA.txt」ファイルを生成します。それでは、ステップAとステップBで行われた変数割り当て順序の最後のコマンドが機能しないのはなぜですか?これについての技術的な説明を知っている人はいますか?
答え1
まず、いくつかの名前に同意するためにシェルが入力を解釈する方法は次のとおりです。
$ a=1; a=2; a=10 | echo $a
^^^ ^^^ ^^^^^^^^^^^^^^
\ \ \_ Pipeline
\ \_ Simple command
\_ Simple command
パイプラインは2つの簡単なコマンドで構成されています。
$ a=10 | echo $a
^^^^ ^^^^^^^
\ \_ Simple command
\_ Simple command
(Bashマニュアルには明示的に言及されていないかもしれませんが、POSIXシェル構文変数の割り当てだけで簡単なコマンドを設定できます。
a=1;
a=2;
パイプラインの一部ではありません。 A は;
パイプの一部として存在しない限りパイプを終了します。複合コマンド。たとえば、
{ a=1; a=2; a=10; } | echo $a
あなたの例では、以下を実行してくださいa=10
。echo $a
2つの異なる、独立サブシェル環境1は、すべて基本環境のコピーとして作成されます。サブシェルは親実行環境を変更しないでください2。関連コンテンツの引用POSIX部分:
サブシェル環境は、シェル環境のコピーとして作成する必要があります。 [...]サブシェル環境への変更は、シェル環境に影響を与えてはいけません。
そして
さらに、マルチコマンドパイプラインの各コマンドはサブシェル環境にありますが、拡張機能によってパイプラインの一部またはすべてのコマンドを現在の環境で実行できます。他のすべてのコマンドは、現在のシェル環境内で実行する必要があります。
したがって、例のすべてのコマンドが実際に実行されている間、パイプラインの左側の部分の割り当ては目立つ効果はありません。a
そのサブシェル環境のコピーのみが変更され、そのコピーはサブシェルが終了すると失われます。
パイプの両端にあるサブシェルが互いに直接対話できる唯一の方法は、パイプ自体を介することです。左側の標準出力は右側の標準入力に接続されます。a=10
パイプを介して何も送信されないため、に影響を与える可能性はありませんecho $a
。
1 このオプションが設定されている場合(デフォルトではオフ、組み込みコマンドを使用して有効)、Bashは現在のシェルのパイプラインで最後のコマンドを実行できます。バラよりlastpipe
shopt
管路Bashのマニュアルにあります。しかし、これはあなたの質問とは関係ありません。
2 実際/歴史的観点からU&Lの詳細を見ることができます。この回答到着POSIXシェルスクリプトパイプラインで実行された最後の関数が変数値を保持しないのはなぜですか?
答え2
私の英語をご了承ください。私はまだ学んでいます。私もBash初心者なので、答えの間違いを修正してください。ありがとうございます。
まず、エラーを指摘したいと思います。
パイプを介してコマンドをエコーするとき
a=10 | echo $a
(パイプ演算子;使用|
)a=10
パイプはstdout
コマンドをstdin
command2、つまりcommand | command2
。a=10
stdout
変数の割り当てです。コマンドではないため、変数の割り当てがないとします。変数の割り当て内でコマンド置換を実行しても機能しません。以下を試してみると:user@host$ a=$(b=10); echo $a
echo $a
値を返しません10
。修正したらuser@host$ a=$(b=10; echo $b)
電話
$ echo $a
後ろに
10
。したがって、変数の割り当てがコマンドではないと仮定することが正しいかもしれません(bashを介して手動で定義してもコマンドではありません)。
次に、echo
コマンドはから入力を受け取りませんが、stdin
その引数を出力します。
user@host$ echo "I love linux" | echo
何も返されません。xargs
この問題を解決するには、次のコマンドを使用できます。
user@host$ echo "I love linux" | xargs echo
返されますI love linux
。したがって、パイプはecho
代わりに引数を印刷するため、コマンドでは直接機能しませんstdin
。
今テストしてみましょう
あなたの命令に従って
user@host$ a=1; a=2; a=10 | echo $a
変数には最初に
a
値が割り当てられ、次に現在のシェル環境内で1
変数値がに変更されます。2
コマンドは通常サブシェルで実行されます。a=10 | echo $a
リストです。つまり、サブシェルで実行するのと同じですが、引数を取らずに印刷するだけで(a=10 | echo $a)
動作しません。ここで、パラメータはで、サブシェルの変数値はです。echo
stdin
$a
a
2
また、
a=10
変数の割り当てなので、出力は生成されません。したがって、実際には引数から何も取得せずにecho $a
引数の値を印刷します。したがって、ここではパイプ演算子を使用しないでください。代わりに、ターミネータ(セミコロン)を使用してコマンド名と変数の割り当てを区別すると、同様に機能します。2
a=10 | < ... >
(a=10; echo $a)
これをよりよく理解するには、bashデバッグオプションを有効にして次の例を試してください。
user@host$ a=1; a=2; a=10; echo $a;
上記のコマンドラインから
echo $a
。10
私がそれに変えると
user@host$ a=1; a=2; (a=10; echo $a)
最初と2番目の変数割り当ては現在のシェルで設定され、3番目の変数割り当てとコマンド
echo
はサブシェルで実行されます。したがって、値はコマンドをa
実行する10
サブシェルにもあり、echo
返されます10
。プロンプトを受け取った後にコマンドを実行すると、サブシェルの変数割り当てが親シェルを再インポートしないため、echo $a
コマンドが返されます。2
- さらに注目すべき点は、
;
命令区切り文字が命令を順次実行することである。
テストケース「A」と「B」では実際に最後の変数割り当てが行われますが(a=10
テストAとa=20
テストBでは)コマンドの後に実行されるため、シェル環境変数echo $a
の以前の値の結果が得られます。a
次に、最終変数の割り当てを実行します。パイプでは、コマンドが実行される前に2つのコマンドのstdin
合計が連結され、変数の割り当ては標準出力に何も生成しません。stdout
長すぎます。:パイプラインでは変数割り当てを使用しないでください。echo
パイプラインで直接作業しません。
答え3
ここでは、
a=20 | echo $a
パイプは混乱を加重するだけです。左側の割り当ては標準出力として何も印刷せず、echo
標準入力から何も読み取らないため、データはパイプを通過しません。パラメータはecho
以前に設定されたものから単に拡張されます。
おわかりのように、パイプラインのさまざまな部分は異なるサブシェルで実行されるため、左側の割り当てはa
右側の拡張に影響を与えず、この通信はその逆方向でも発生しません。
代わりに、次のようにします。
{ a=999; echo a=$a; } | cat
チューブはより意味があり、ロープはチューブをa=999
通過します。
最後の例のコードは、touch fileA.txt
シェルの外部システムに影響を与えるために機能します。同様に、パイプのコマンドでstderrに書き込んで、端末に表示される結果を見ることができます。
$ echo a >&2 | echo b >&2 | echo c >&2
b
c
a
(これは実際に私のシステムでBashを使用して得られる出力の順序です。)