パイプライン後に複数のコマンドとサブシェルを実行する

パイプライン後に複数のコマンドとサブシェルを実行する

まあ、私は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=10echo $a2つの異なる、独立サブシェル環境1は、すべて基本環境のコピーとして作成されます。サブシェルは親実行環境を変更しないでください2。関連コンテンツの引用POSIX部分:

サブシェル環境は、シェル環境のコピーとして作成する必要があります。 [...]サブシェル環境への変更は、シェル環境に影響を与えてはいけません。

そして

さらに、マルチコマンドパイプラインの各コマンドはサブシェル環境にありますが、拡張機能によってパイプラインの一部またはすべてのコマンドを現在の環境で実行できます。他のすべてのコマンドは、現在のシェル環境内で実行する必要があります。

したがって、例のすべてのコマンドが実際に実行されている間、パイプラインの左側の部分の割り当ては目立つ効果はありません。aそのサブシェル環境のコピーのみが変更され、そのコピーはサブシェルが終了すると失われます。

パイプの両端にあるサブシェルが互いに直接対話できる唯一の方法は、パイプ自体を介することです。左側の標準出力は右側の標準入力に接続されます。a=10パイプを介して何も送信されないため、に影響を与える可能性はありませんecho $a


1 このオプションが設定されている場合(デフォルトではオフ、組み込みコマンドを使用して有効)、Bashは現在のシェルのパイプラインで最後のコマンドを実行できます。バラよりlastpipeshopt管路Bashのマニュアルにあります。しかし、これはあなたの質問とは関係ありません。

2 実際/歴史的観点からU&Lの詳細を見ることができます。この回答到着POSIXシェルスクリプトパイプラインで実行された最後の関数が変数値を保持しないのはなぜですか?

答え2

私の英語をご了承ください。私はまだ学んでいます。私もBash初心者なので、答えの間違いを修正してください。ありがとうございます。

まず、エラーを指摘したいと思います。

  • パイプを介してコマンドをエコーするときa=10 | echo $a(パイプ演算子;使用|a=10パイプはstdoutコマンドをstdincommand2、つ​​まりcommand | command2

  • a=10stdout変数の割り当てです。コマンドではないため、変数の割り当てがないとします。変数の割り当て内でコマンド置換を実行しても機能しません。以下を試してみると:

    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)動作しません。ここで、パラメータはで、サブシェルの変数値はです。echostdin$aa2

  • また、a=10変数の割り当てなので、出力は生成されません。したがって、実際には引数から何も取得せずにecho $a引数の値を印刷します。したがって、ここではパイプ演算子を使用しないでください。代わりに、ターミネータ(セミコロン)を使用してコマンド名と変数の割り当てを区別すると、同様に機能します。2a=10 | < ... >(a=10; echo $a)

これをよりよく理解するには、bashデバッグオプションを有効にして次の例を試してください。

user@host$ a=1; a=2; a=10; echo $a;
  • 上記のコマンドラインからecho $a10

  • 私がそれに変えると

    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を使用して得られる出力の順序です。)

関連情報