cygwin でパイプにリダイレクトするときに /dev/stderr が無効になるのはなぜですか?

cygwin でパイプにリダイレクトするときに /dev/stderr が無効になるのはなぜですか?

/dev/stderr最近、最新の cygwin で無効な問題が発生し、これは十分に成熟した Debian インストールでも発生するという驚くべき問題に遭遇しました。(編集: 当初考えていたのとは反対に、私の Debian システムではこのエラーは発生せず、単に必要な出力が生成されます。これは cygwin のバグであると推測せざるを得ません。)

背景: 私は何千行もの出力を生成するツールを使用しています (具体的には、大規模な実稼働システムのバージョン管理システム)。私はそれらをスクリプト制御で実行しており、オプションでノイズの多いツール出力をログ ファイルにリダイレクトしたいと考えていました。簡単な解決策は、それらの (stderr および stdout) 出力を常に環境変数に格納されているファイル システムの宛先にリダイレクトすることのようです。端末 (またはユーザーが制御する宛先) への出力が必要な場合、宛先はDBG_STDERR単に "/dev/stderr" になります。そうでない場合は、一時ファイル名になります。一般的なツール実行行は、次のようになりますnoisy_command >> "$DBG_STDERR" 2>&1

スクリプトの出力をパイプしない限り、これは正常に動作します。以下は最小限の再現です。

$ uname -a
CYGWIN_NT-6.1-WOW xxxxxxx 2.8.1(0.312/5/3) 2017-07-03 14:06 i686 Cygwin

$ bash --version
GNU bash, version 4.4.12(3)-release (i686-pc-cygwin)

$ cat say-something.sh
#!/bin/sh
echo something > /dev/stderr

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1 |cat
./say-something.sh: line 2: /dev/stderr: No such file or directory

$ (x=$(./say-something.sh 2> /dev/stderr)) 2>&1
something

$ (x=$(./say-something.sh 2> /dev/stderr))  |cat
something

$ x=$(./say-something.sh 2> /dev/stderr) 2>&1 |cat
something

もちろん、すべてのリダイレクトとネストされたシェルは、文脈から外れるとおかしく見えます。say-something.sh は実際には別のスクリプトによって呼び出されるため、追加のシェルが必要です。fd 2 から stderr への冗長なリダイレクトは、ファイルへのオプションのリダイレクトを容易にするための「スイッチ」です (/dev/stderr または別のパスは、実際には変数の構成可能な内容です)。

失敗した例の後の実験が示すように、このパイプラインのすべての構成要素が必要であるかのように思われます。それらはすべて成功します。

  • stdoutの最後のパイプが必要です
  • 呼び出し側でstderrをstdoutにコピーする必要がある
  • コマンド置換の周囲に外側のシェルが必要です。

答え1

名前はパイプにリダイレクトするときに実際に有効です。最終ターゲットを直接/dev/stderr開くことはできない可能性があります。次を参照してください。/dev/stderr

$ (echo Testing testing > /dev/stderr) |& cat
Testing testing

|またはによって作成されたパイプ|&は通常、匿名パイプ; 表示される名前ファイルシステム内のオブジェクトに対応していません例として、次のような簡単なものを試してみてください。

$ ls -la /dev/fd/ |& cat
total 0
dr-x------ 2 alexp alexp  0 Jul  6 18:23 .
dr-xr-xr-x 9 alexp alexp  0 Jul  6 18:23 ..
lrwx------ 1 alexp alexp 64 Jul  6 18:23 0 -> /dev/pts/4
l-wx------ 1 alexp alexp 64 Jul  6 18:23 1 -> pipe:[1058859]
l-wx------ 1 alexp alexp 64 Jul  6 18:23 2 -> pipe:[1058859]
lr-x------ 1 alexp alexp 64 Jul  6 18:23 3 -> /proc/4335/fd

の(最終)ターゲットを開こうとするのは非常に珍しいことです/dev/stderr。名前/dev/stderrは順番に提供されています。避けるために実際のターゲットを見つけ出すのに苦労する。

答え2

問題は、生成された readline プロセスにあると思います。プロセスが停止すると閉じられるリダイレクト用の独自のパイプを取得します (取得する pid はシェルの pid ではなく、readlink プロセスの pid です)。プロセスが終了するとパイプは無効になります。fifo/名前付きパイプを使用してみてください。

関連情報