$! > >(...)外部コマンドで使用するためのプロセスオーバーライドにPIDが設定されていません。

$! > >(...)外部コマンドで使用するためのプロセスオーバーライドにPIDが設定されていません。

Bash 4.4.19(1) - リリース

以下は、ロギングアプリケーションの基礎となる簡単なスクリプトです。さまざまな理由でプロセス置換を使用する必要がありました。

これはrunnerアプリケーションの中心であり、プロセスの交換は非同期であるため、ループを使用して一貫性を維持することに成功しましたwhile。完璧に動作します。

bash <filename> <function>残念ながら動作しない状況を発見しました。 「」を実行するとき

したがって、再生するには2つのファイルが必要です。

必要:

  1. なぜこれが起こるのですか?
  2. 同様の状況に対応するためにwhileループをどのように変更できますか?

簡略化されたスクリプトは次のとおりです。

test.sh

#!/bin/bash

2sub() {
local in=$(cat); echo -e "$in";
}   
runner () {
 "${@}" 1> >(2sub)
 while [ -e /proc/$! ]; do sleep 0.1; done     # <<< LOOP WAIT FOR $!
}
remotesub() {
 bash ./test2.sh remotesub2
}

echo -e "running\n"; 
    runner bash ./test2.sh remotesub2 # LOOPS
    # runner remotesub # A POSSIBLE BYPASS/SOLUTION? But why?
echo -e "done!\n"

test2.sh

     remotesub2() {
         echo -e "'${BASH_VERSION}'"
         return 0
     }

     "$@"

バイパス:

bash <filename> <function>スクリプトが示すように、問題を関数内にラップして問題を回避することができます。伝達関数to runner。これが直接的な方法の代わりに機能する理由は、ここで誰かが知っていると確信しています。

この問題を解決し、これらのケースを処理するために待機ループを実行するより良い方法があるかどうかを教えてください。

解決策:

最良の解決策はmosvyによって提案された解決策です。ありがとうございます。を使用すると、 { "${@}"; }コマンドを別の小さな機能にラップする必要がなくなり、痛みを伴う可能性があります。さらに、より大きなコードで数時間テストした後、サブプロセスを慎重に終了すると、この作業は不要であると結論付けましたwhile [ -e /proc/$! ]; do sleep 0.1; done。行は次に置き換えられます。wait $!;

答え1

私が正確に理解するならば、なぜ$!内部で実行されるプロセスのPIDが組み込みコマンドや関数のコマンドラインの一部であるときにだけ設定され、組み込みコマンドライン>(...)の一部であるときは設定されないのだろうか。コマンドまたは機能の一部は外部コマンドです。

単純化された例:

$ bash -c 'true > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
psubst=12392
in=12392

$ bash -c '/bin/true > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=12751
psubst=

これは、外部コマンドを使用している場合はbash別々のプロセスが分岐し、そのプロセスで実行されているプロセスが>(...)そのプロセスの子として実行されるために発生します。素晴らしい完全に制御できないスクリプトの子です。

外部コマンドが終了すると、そのサブコマンド(まだ実行中の場合)はpid 1(init)によって受け入れられるため、スクリプトがそのPIDを取得するためにまだ使用できるすべてのリンクが失われます。

回避策は、コマンドライン内のすべてのプロセスの置き換えがスクリプトの子プロセスとして実行され、経由で渡されるようにするラッパー関数を使用することですpgrep -P "$$"

また、外部コマンドを{...}ブロックに入れ、ブロックの出力をリダイレクトするのがうまくいくようです。

$ bash -c 'func(){ /bin/true; }; func > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=3574
psubst=3574
$  bash -c '{ /bin/true; } > >(echo in=$BASHPID; sleep .1); echo psubst=$!'
in=3435
psubst=3435

どちらの解決策も、現在の実装の仕組みによって異なります。bashいつか、マイナーなグループコマンドや機能を最適化し、これらの仮定を破ることにすることもできます。

PIDを最後のプロセス交換に設定することは、$!NETでは利用できない文書化されていない機能ですbash

関連情報