バックグラウンドプロセスがシェルスクリプトの実行順序を混同する

バックグラウンドプロセスがシェルスクリプトの実行順序を混同する

誰かがこのスクリプトの理由を説明できますか?

echo one && 
echo two & 
echo three && 
echo four

私にください

[1] 3692
three
four
one
two
[1]+  Done                    echo one && echo two

当然、バックグラウンドプロセス以降のすべての内容が最初に出力され、その後、バックグラウンドプロセスの前のコード行が時系列で印刷されます。私は私が書こうとしたシナリオでそれを偶然見つけましたが、それがなぜそうなったのかわかりませんでした(ただし、後ろに少し独創性があると思いますが)。

私はかっこを使ってこれを防ぐことができることを発見しました:

echo one && 
(echo two &) 
echo three && 
echo four

与えられた

one
two
three
four

そして

echo one && 
(sleep 2 && echo two &)
echo three && 
echo four

与えられた

one
three
four
two

2行目はバックグラウンドでスタンバイ状態なので、1行目はすでに実行中に出力されます。

しかし、なぜかっこがこのような効果をもたらすのでしょうか?

追加の質問:括弧がバックグラウンドPID出力を妨げるのはなぜですか?

答え1

echo one && 
echo two & 
echo three && 
echo four

覚えておいてください。これは次のように解析されます。

echo one && echo two &
echo three && echo four

私はこのechoコマンドが成功したと仮定します(フルディスクのファイルにリダイレクトするなどの極端な場合にのみ失敗します)。したがって、このコードスニペットは2つのタスクを並列に実行します。 1 つは行の合計を印刷しone、もう 1 つは行のtwo合計を印刷します。合計と合計の分散は保証されず、システム、シェル、および呼び出しによって異なる場合があります。このような簡単な例を使用すると、負荷の低いシステムで再現可能な結果を​​見ることができますが、これは信頼できるものではありません。threefouronetwothreefour

echo one && 
(echo two &) 
echo three && 
echo four

解析を変更しました。ここでは、コマンドのみがecho twoバックグラウンドで実行されます。これはone最初に出力され、beforeと同様にthree常にbeforeになりますが、four場所はtwoafterのどこにでも配置できますone

シェルは、デフォルトのシェルプロセスで開始された場合にのみバックグラウンドプロセスのメッセージを印刷し、サブシェルで起動された場合にはそのメッセージを印刷しません。括弧はサブシェルを生成するので、シェルは(echo two &)メッセージを出力しません。これによりバックグラウンドプロセスが作成されますが、バックグラウンドは生成されません。働く

echo one && 
(sleep 2 && echo two &)
echo three && 
echo four

このコードでは、バックグラウンドジョブは出力する前に2秒間休止状態になりますtwo。これにより、two後に出力がある可能性が非常に高くなります(実際には保証されません)。threefour

答え2

パート1

echo one && 
echo two & 
echo three && 
echo four

これは次のように書き直すことができます。

echo one && echo two & 
echo three && echo four

three最初にandを取得する理由は、単にandを印刷するよりも、サブシェルfouroneandを処理して実行するのに時間がかかるためです。twothreefour

このように見れば、より確実にわかります。

echo one && echo two &
sleep 1
echo three && echo four

この場合、あなたoneはとを取得しますtwo。しばらくするとあなたはとthreeを得ますfour

もともとのシナリオでは、 と が and の出力oneと混在しないという保証はなく、または同じ単語が挿入される場合にも可能です。twothreefourthonreeetwfouro

パート2

echo one && 
(echo two &) 
echo three && 
echo four

これは次のように書き直すことができます。

echo one && (echo two &) 
echo three && echo four

ここで何が起こるのかは、oneすぐに印刷してtwoサブシェルでトリガーされることです。その後、次の行が(連続して)実行され、threesumが生成されますfour。特別な場合、サブシェルはtwo出力する前に印刷できるほど小さく、高速です。threeしかし、これに対する保証はありません。

3番目の部分

ステートメントをサブシェルにグループ化します。 &記号は&ステートメントに対して機能しますが、この場合は2つのコマンドを一緒にリンクするために使用したので、単一の&&ステートメントとして扱う必要があります。

echo one; echo twoたぶん代わりに使うべきですかecho one && echo two?彼らは非常に異なります。セミコロンは、;2つのコマンドを区別して独立して順次実行されます。デュアルアンパサンドは&&2つのコマンドを一緒に接続します。論理AND、最初が正常に完了するまで2番目が実行されないようにします。false; echo yes、、、false && echo yesおよびを比較しますtrue && echo yes。それから&&論理AND)と||論理的または)。

ボーナス

サブシェルにはジョブ制御がないため、ジョブ制御通知が失われます。

関連情報