通常、paste
は、次のように隣接する列に2つの名前付き(または同等の)ファイルを出力します。
paste <(printf '%s\n' a b) <(seq 2)
出力:
a 1
b 2
ただし、2つのファイルが/dev/stdin
と/dev/stderr
の場合、同じようには機能しないようです。
blackbboxプログラム標準出力と標準エラーの2行。説明のために、これは次の関数でシミュレートできます。
bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }
ここで、 annotate-output
、(devscriptsパッケージのDebian/Ubuntu/etc。を実行します。 ))、それが機能することを示すために:
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
を使用して実行できますか?そうでない場合は、なぜですか?
問題は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
で示されているように)異なる記述子を通過しますが、最終的には同じ場所に配置されます。
|
と2番目のコマンドを追加すると、パイプラインが作成されます...
bb | paste /dev/stdin /dev/stderr
|
は、bb
の出力をpaste
の入力に接続するようにシェルに指示します。 paste
は最初に/dev/stdin
からの読み取りを試みます。これは、(いくつかのシンボリックリンクを介して)独自の標準入力記述子(シェルが接続したばかり)に解決されるため、行1
が通過します。
しかし、シェル/パイプラインはSTDERRには何もしません。 bb
はまだそれ(e1
e2
など)をコンソールに送信します。その間、paste
は同じコンソールから読み込もうとしますが、ハングします(何かを入力するまで)。
あなたのリンク テキストエディタで/ dev/stdoutを読み取れないのはなぜですか?/dev/stderr
にも同じ制限が適用されるため、ここでも関連性があります。
標準出力と標準エラーの両方を生成するコマンドがあり、これらの2行を隣り合わせにpaste
したいとします。つまり、各列に1つずつ、2つの同時パイプがあります。シェルパイプライン... | ...
はそれらの1つを提供し、2つ目を自分で作成し、2>filename
を使用してSTDERRをそれにリダイレクトする必要があります。
mkfifo RHS
bb 2>RHS | paste /dev/stdin RHS
これがスクリプトで使用する場合は、一時ディレクトリにFIFOを作成し、使用後に削除することをお勧めします。
annotate-output
は何か特別なことをしているので(つまり、コマンドのstderrをfifoにリダイレクトする)、paste
が持っていること絶対に方法がない-単にpaste
はnotコマンドを実行しているため、入力を取得し、入力または出力をリダイレクトする方法がありません。
ただし、annotate-outputが使用しているのとまったく同じトリックを使用するラッパーを作成できます。
pasteout(){
f=$(mktemp -u) || return
mkfifo -m 600 -- "$f" || return
"$@" 2>"$f" | paste -- - "$f"
rm -f -- "$f"
}
pasteout bb
ただし、デッドロックが発生しやすいことに注意してください。たとえば、bb
がパイプに収まるよりも多くの標準出力とpaste
によって最初に読み取られた余分な量を生成するが、エラー出力を生成しない場合、paste
はブロックされますfifoでの入力を待機し、パイプを空にしないbb
はそのstdoutをフィードしているため、パイプへのbb
のwrite()もハングします。
分析する必要のあるライン全体には、いくつかの問題があります。つまり、次のとおりです。
seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
まず、最後のコマンド。 stdoutのみがパイプを通過できます:
$ 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
とまったく同じで、paste
コマンドで使用される2番目のファイル記述子のブロックを回避します。ただし、出力される値は、paste
からではなく、コンソールにリダイレクトされたseq 3 4
から直接取得されます。これは同じことをします:
$ { 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関数には(基本的に)次のものが含まれます。
| 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構文と互換性があります)で移植可能に動作するようです。完全にテストされていないので、他のユーザーに確認を求めてください。まだ未知の驚きがあるかもしれません。