
複数のプロセス置換を含むかなり複雑なコマンドを含むスクリプトがあり、プロセス置換内のコマンドから終了コードを取得する必要があります。名前付きパイプを介してこれを実行しようとしていますが、以前に名前付きパイプを使用したことがないため、問題があります。
たとえば、次のコマンドが与えられた場合:
somecommand <(someothercommand; echo $? > named_pipe) <(someothercommand; echo $? > named_pipe) &
somecommand
完了するのを待ってすべての終了コードを読み取る正しい方法は何ですかnamed_pipe
(実際にどのように多くの終了コードが書かれているのかわからないと仮定named_pipe
)。これまでの試みはブロックsomecommand
(で何かを読むのを待っていると仮定するためnamed_pipe
)や終了コードだけを読む結果をもたらしました。
答え1
実際、これはそれほど単純ではないようです。シェルがプロセスの交換待機をサポートしている場合、これは最善かもしれませんが、Bashはそうしないようです。
もう一つの問題は、名前付きパイプがそこに書きたい行数がわからないことです。すべてのライターが閉じると、パイプはEOFを読み取りますが、すべての書き込みでEOFを取得できますecho
。同時に攻撃しない限り、そのような場合はそうではありません。
ただし、最初から書き込みfdを開くようにプロセス置換をスケジュールし、すべての操作が完了した後にEOFが一度だけ発生するようにすることは可能です。
このように、echo somecommand
およびを使用して実際のコマンドをtrue
表します。false
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# whole subshell sent to the background
(exec 3> "$p";
# both process substitutions get a copy of fd 3
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
) &
# read the exit statuses, this will see EOF once all the three
# background processes above finish
cat "$p"
rm -rf "$dir" 2>/dev/null
パイプに印刷されるラインの順序は時間によって異なり、本質的にランダムです。
echo somecommand
また、ゆっくり実行すると、最初に出力される出力が得られます。cat "$p"
パイプから変数としてデータを読み取り、wait
それをバックグラウンドプロセスで使用する必要があります。
コンテキストなしで可能ですsomecommand
が、ファイルハンドルを使用するにはさらに練習が必要です。
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# open an fd for read+write (doesn't block because both open)
exec 3<>"$p"
# process substs inherit the fd, closing it when they exit
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
# open another reader to keep the pipe live
exec 4<"$p"
# now we can close the writing handle
exec 3>&-
# read the data off
cat <&4
exec 4<&-
rm -rf "$dir" 2>/dev/null
終了ステータスを通常のファイルに収集し、既知の行数まで読み取る方が簡単です。
#!/bin/bash
f=$(mktemp)
# number of process substitutions
n=2
echo somecommand \
<(false; echo "cmd1: $?" >>"$f") \
<(true; echo "cmd2: $?" >>"$f") \
exec 3< "$f"
# read that many lines
for ((i = 0; i < n; i++)) do
# if the data isn't there yet, retry reading until a new line appears
until read line <&3; do sleep 1; done
echo "$line";
done
exec 3<&-
rm -f "$f"
テストしたところ、3つすべてが機能しているようですが、プロセスの置き換えとパイプの使用は面倒である可能性があるため、一部の障害モードが欠落している可能性があります。