`test.sh & | test.sh` が間違っているのはなぜですか

`test.sh & | test.sh` が間違っているのはなぜですか

以下のような名前の bash スクリプトがありますtest.sh

#!/bin/bash

while :
do
    echo xxx
    sleep 1
done

./test.sh & | ./test.shなぜエラーが発生するのかわかりません: -bash: syntax error near unexpected token |'、一方、 および./test.sh | ./test.sh./test.sh | ./test.sh &機能します。

答え1

それはシェルの文法:

%start  complete_command
%%
complete_command : list separator
                 | list
                 ;
list             : list separator_op and_or
                 |                   and_or
                 ;
...

pipe_sequence    :                             command
                 | pipe_sequence '|' linebreak command
                 ;
...
separator_op     : '&'
                 | ';'
                 ;

&または は;リストを終了することしかできず、リストは complete_command の一部にすぎず、他の生成物の一部になることはできないことに注意してください。

{ foo & }これは単なる文法上の癖です。 が と意味的に似ているのと同様にfoo &(括弧とは異なり、中括弧はコマンドをグループ化するためにのみ使用され、それ自体ではサブシェルや何らかのスコープを作成しません)、が文法で許可されている場合は は{ foo & } | barと意味的に似ていると考えることができますfoo & | bar

したがって、あなたの例の回避策は次のようになります。

{ ./test.sh & } | ./test.sh

これを対話型シェルから実行すると&ない作成するバックグラウンドジョブただし、パイプラインの右側は常にサブシェルで実行され、サブシェルからのコマンドはジョブ制御が無効な状態で実行されるため、コマンドは非同期で実行されます。

これは次のものと似ています:

(sleep 3600 &)

サブシェル内の非同期リスト ( によって終了されるコマンド&) は、stdin が からリダイレクトされ/dev/nullSIGINTおよびシグナルが無視されて実行されます。これらはバックグラウンドで実行され続けますが、通常のジョブのように や などを使用してSIGQUITフォアグラウンドにすることはできません。また、これらが停止または終了されたときに通知されません。fgdisown

スクリプトでは、ジョブ制御が無効になっている状態でスクリプトが実行されるため、これはまったく違いはありません。

しかし、背景の出力をパイプしたい場合は仕事前景に仕事相互の作用シェルの場合、私が考えられる唯一の移植可能なソリューションは、名前付きパイプを使用することです。

$ mkfifo fifo
$ command | filter1 > fifo &  # this is the background job
$ < fifo sed 10q | filter2    # this is the foreground job

関連情報