errpipe
フィルタを介して実行される単純なAPIを含むユーティリティスクリプトを作成しようとしています。stderr
最初は、bashのプロセス交換機能を使って実装してみました。
#!/bin/bash
com="$1"
errpipe="$2"
$com 2> >(1>&2 $errpipe)
com
問題は、これが存在しないときに出力が奇妙に見えることです。
入ると
sh-3.2$ ./errpipe foo cat
わかりました。
sh-3.2$ ./errpipe foo cat
sh-3.2$ ./errpipe: line 6: foo: command not found
@
は@
カーソルを表します。つまり、シェルプロンプトがあまりにも早く印刷されます。私はこれがメインシェルスクリプトがプロセス交換プロセスが完了するのを待たないためだと思います。wait
スクリプトの最後に追加しても問題は解決しないようです。
私はを使用するソリューションbash
や、ksh
おそらくzsh
いくつかのクレイジー機能を使用するawk
ソリューションに開いている。プロセスとファイル記述子を操作するためのより豊富なAPIを提供するCやPerlなどを使用してそれらを一緒にリンクする方法を知っていると思いますが、他のオプションがない限り使用を避けたいと思います。
「ほとんど動作する」ソリューションの1つは、フォークされたときにシェルが変更されないという事実を活用し、$$
エラーパイプが完了したら親にシグナルを送信することです。
#!/bin/bash
com="$1"
errpipe="$2"
$com 2> >(1>&2 $errpipe; kill -SIGUSR1 $$)
while true; do
sleep 60
done
これは元の問題を解決しますが、a)醜いです。 b) User defined signal 1: 30
SIGUSR1 のシグナルハンドラがあっても終了する前に印刷され、c) SIGUSR を親に送信するプロセスが何らかの方法で死ぬと永遠に循環します。 。
ベストアンサー1
はい、関数がどこから来たかのように、プロセス交換内のプロセスは待ちませんbash
。ksh
一般的に、次のような人には大丈夫です<(...)
。
cmd1 <(cmd2)
シェルは、交換されるパイプのファイルが終了するのを待ち、通常読み取るのを待ち、通常ファイルの終わりは死亡時にcmd1
発生します。これは、複数のシェル(ではなく)が待機しない理由でもあります。cmd1
cmd2
cmd2
bash
cmd2
cmd2 | cmd1
しかし、cmd1 >(cmd2)
通常の場合にはそうではありません。通常はそこで待機し、通常はcmd2
後でcmd1
終了するからです。
zsh
そこで待つでしょうcmd2
(しかし、あなたが書いたらそうでcmd1 > >(cmd2)
はなく代わりに{cmd1} > >(cmd2)
asを使用してください)録音された)。
ksh
基本的には待ちませんが、組み込み機能を使って待つことができます。wait
(また、pidを使用できるようにしてくれますが、$!
そうすると役に立ちませんcmd1 >(cmd2) >(cmd3)
。)
rc
(構文を使用)、を使用してすべてのバックグラウンドプロセスのpidを取得できることを除いて、他のものとcmd1 >{cmd2}
同じです。ksh
$apids
es
(また同様) in cmd1 >{cmd2}
waitであり、また進行中のリダイレクトを待っています。cmd2
zsh
cmd2
<{cmd2}
bash
は、pid cmd2
(またはより正確にはサブシェルのpid、それがcmd2
最後のコマンドであってもそのサブシェルのサブプロセスで実行されるため)を使用できるようにしますが、$!
ユーザーがそれを待つことを許可しません。
必要に応じて、bash
次の2つのコマンドを待つコマンドを使用して問題を解決できます。
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
これにより、fd3とfd3のcmd1
両方がcmd2
パイプで開かれます。もう一方の端でファイルが終了するのを待つため、通常は両方が発生し、終了したcat
ときにのみcmd1
終了しますcmd2
。シェルはコマンドを待ちますcat
。すべてのバックグラウンドプロセスの終了をキャプチャするネットワークと考えることができます。 (&
デーモンですべて終了しない限り、バックグラウンド自体の、coprocsやコマンドなど、バックグラウンドで開始された他の操作に使用できます。)通常、ファイル記述子を実行します)。
上記の無駄なサブシェルプロセスのため、cmd2
fd 3が閉じられてもまだ機能します(コマンドは通常これを実行しませんが、いくつかのコマンドは同様または実行しますsudo
)ssh
。将来のバージョンはbash
最終的に他のシェルのように最適化される可能性があります。その後、次のようなものが必要です。
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
コマンドを待っているfd 3を持つ追加のシェルプロセスがまだ開いていることを確認してくださいsudo
。
cat
何も読まないことに注意してください(プロセスはfd 3に書かないからです)。まさに同期のためです。システムコールのみを実行しread()
、最終的には何も返しません。
cat
パイプライン同期のコマンド置換を使用すると、実際にこの実行を回避できます。
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
今回は、シェルがfd 3の反対側の端にcat
開いているパイプからデータを読み込みます。変数割り当てを使用するので、終了ステータスを で使用できます。cmd1
cmd2
cmd1
$?
または、プロセス置換を手動で実行してから、システムの置換を使用することもできます。sh
これは標準シェル構文になります。
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
しかし、前述のように、すべての実装が完了するのをsh
待つわけではありません(その逆の場合よりも優れています)。この時点では、make と make の終了状態はそれぞれおよびで使用できますが、終了状態が含まれます (最後のコンポーネント以外のパイプラインコンポーネントエラーを報告できるように、一部のシェルのオプションも参照)。cmd1
cmd2
$?
cmd2
bash
zsh
cmd1
${PIPESTATUS[0]}
$pipestatus[1]
pipefail
$?
yash
プロセスにも同様の問題があります。リダイレクト特徴。そこにcmd1 >(cmd2)
記録されますcmd1 /dev/fd/3 3>(cmd2)
。ただし、待たないと待機することはできず、cmd2
そのpidは変数では使用できません。と同じ回避策を使用します。wait
$!
bash