通常paste
印刷二つ隣接列の名前付き(またはそれに対応する)ファイルは次のとおりです。
paste <(printf '%s\n' a b) <(seq 2)
出力:
a 1
b 2
しかし、両方のファイルがあります /dev/stdin
。/dev/stderr
私たちが持っているとしましょう。雨部族雨箱2行を出力するプログラム標準出力そして2行標準エラー。説明のために、次の関数を使用してこれをシミュレートできます。
bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }
今走るannotate-output
、(内部に開発スクリプトカプセル化Debian/Ubuntu/など。)動作していることを示すために:
annotate-output bash -c 'bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }; bb'
22:06:17 I: Started bash -c bb() { seq 2 | tee >(sed s/^/e/ > /dev/stderr) ; }; bb
22:06:17 O: 1
22:06:17 E: e1
22:06:17 O: 2
22:06:17 E: e2
22:06:17 I: Finished with exitcode 0
だから動作します。フィードbb
:paste
bb | paste /dev/stdin /dev/stderr
出力:
1 e1
e2
^C
停止 -^C
押されたという意味Control-Cやめる。
|
次のいずれかに変更して;
も機能しません。
bb ; paste /dev/stdin /dev/stderr
出力:
1
2
e1
e2
^C
また、一時停止 -^C
押されたことを意味します。Control-Cやめる。
希望の出力:
1 e1
2 e2
を使用して実行できますかpaste
?そうでなければなぜならないのですか?
答え1
/dev/stderrをパイプとして使用できないのはなぜですか?
問題はそうpaste
ではありません/dev/stdin
。それと一緒に行きます/dev/stderr
。
すべてのコマンドは、1つのオープン入力記述子(0:標準入力)と2つの出力(1:標準出力と2:標準エラー)で生成されます。通常、それぞれと/dev/stdin
名前を使用してアクセスできますが、以下を参照してください。/dev/stdout
/dev/stderr
/dev/stdin、/dev/stdout、および/dev/stderrの移植性はどのくらいですか?。 (含むpaste
)多くのコマンドもファイル名を-
STDINとして解釈します。
個別に実行すると、bb
STDOUTとSTDERRの両方がコンソールであり、通常はコマンド出力が表示されます。行は(あなたの説明のように)別の記述子を通過しますannotate-output
が、同じ位置に終わります。
1 つ目と 2 番目のコマンドを追加すると、|
パイプが生成されます。
bb | paste /dev/stdin /dev/stderr
|
の出力をbb
の入力に接続するようにシェルに指示しますpaste
。 paste
まず読んでください/dev/stdin
。これは(一部のシンボリックリンクを介して)独自の標準入力記述子(シェルが接続したばかりのもの)として解釈され、行が渡されるようにし1
ます。
ただし、シェル/パイプラインはSTDERRには影響しません。 bb
それでもe1
e2
コンソールに転送中です。同時にpaste
同じコンソールからデータを読み取ろうとすると、コンソールは停止します(何かを入力するまで)。
あなたのリンクテキストエディタを使用して/dev/stdoutを読み取れないのはなぜですか?/dev/stderr
.
2番目のパイプを作成する方法
標準出力と標準エラーを生成するコマンドがあり、paste
これら2行を互いに隣に置きたいです。これは、各列に1つずつ2つの同時パイプラインを意味します。シェルパイプは... | ...
これらのうちの1つを提供します。 2番目のパイプを直接作成し、.redirectを使用してSTDERRをそのパイプにリダイレクトする必要があります2>filename
。
mkfifo RHS
bb 2>RHS | paste /dev/stdin RHS
これがスクリプト内で使用されている場合は、FIFOを一時ディレクトリに配置し、使用後に削除することをお勧めします。
答え2
annotate-output
paste
これを行うことができる理由は、特別な作業(コマンドの標準エラーをfifoにリダイレクトするなど)を実行しているためです。完全に絶対にしない -paste
ただいいえ入力を受け取るコマンドを実行し、入力または出力をリダイレクトできません。
ただし、annotate-outputで使用するのと同じトリックを使用してラッパーを作成できます。
pasteout(){
f=$(mktemp -u) || return
mkfifo -m 600 -- "$f" || return
"$@" 2>"$f" | paste -- - "$f"
rm -f -- "$f"
}
pasteout bb
しかし、デッドロックに陥りやすいので注意してください。たとえば、bb
パイプが保持できるものよりも多くのstdoutを生成し、最初に読み取った追加量を加えた値を生成しますが、paste
エラー出力を生成しない場合、paste
fifoへの入力を待ってブロックされ、そのstdout出力は削除されませんではありませんbb
。bb
パイプの write() も中断されます。
答え3
全生産ラインには分析が必要ないくつかの問題があります。つまり:
seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
標準エラー
最初は最後のコマンドです。ただ標準出力パイプ接続可能:
$ seq2 | paste -
1
2
$ seq2 | paste - -
1 2
読む内容はありませんstderr
:
$ seq 2 | paste - /dev/stderr
1 ^C
^C
読む内容をブロックして残さないので必要ですstderr
。
いくつかの出力を生成しても、stderr
パイプされません。
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
1 3
4
以前と同様に1
印刷され、paste
ブロックは待ちますstderr
。
他の2つの数字はコンソールに直接移動して(独立して)印刷されます。
stderr
パイプラインの最後のコマンドにいくつかの入力を提供できます。
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
1
2
3
4
2>/dev/null
ちなみに、これはブロックコマンドで使用される2番目のファイル記述子を避けるのとまったく同じですpaste
。ただし、印刷された値seq 3 4
はからではなくリダイレクトからコンソールに直接インポートされますpaste
。
$ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
1
2
3
4
これは次のことを防ぎません。
$ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) |
paste /dev/stdin /dev/stderr 2</dev/null
1
2
e1
e2
注文する
第二に、 の出力がtee
「順番に」行われる必要はありません。`tee`と`bash`プロセスの交換順序
そして、実際にプロセス置換の出力は「順番に」である必要はありません。 プロセス交換出力が故障しました。
$ echo one; echo two > >(cat); echo three;
one
three
two
実際、いくつかの例では、何度も試してみると、他の注文を受け取ることがあります。プロセス交換を介して同時に実行されている独立プロセスの非決定的出力
$ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
1000
999
998
0
1
したがって、いいえ。手続き型の交換と貼り付けでは実行できません。
いくつかのコマンドを実行する必要があります。
$ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
1 e1
2 e2
BB
したがって、bb関数には基本的に次のものが含まれます。
| tee >(sed 's/^/e/')
これは以下を使用してテストできます。
$ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
0
1
291
290
289
0、1、1000、999、998をこの順番で印刷する必要がありますが、そうでない場合が多いです。
つまり:はい本質的に不安定。
安定した実際のソリューション。
bbの唯一の安全な解決策は、プロセスの交換を避けることです。
そして{…}
stdoutとstderrキャプチャを活用してください。たとえば、次のようになります。
$ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null
出力がありません。 2つを削除して確認してください。
これはbbで動作します。
$ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }
そしてfifoを使って以下を貼り付けます。
$ mkfifo out2
$ bb 2>out2 | paste out2 -
1 e1
2 e2
3 e3
4 e4
5 e5
fifoファイルを削除するには、トラップを設定してfifoファイルを作成する前に、そのファイルが存在するかどうかをテストする必要があります。
私がテストしたすべてのシェルで移植可能なようです(Almquist構文と互換性があります)。まだ完全にテストされていません。他のユーザーに確認してください。未知の驚きがあるかもしれません。