web-dev-qa-db-ja.com

Bashファイルのリダイレクトのバグ?

プログラムIOについて理解していることから、stdinstdout、およびstderrのデータストリーム(および戻りコード)があります。 stdoutstderrは、2つのデータ出力ストリームです。したがって、bashリダイレクトを使用して出力ストリームの1つを閉じると、テキストが送信されるストリームを絞り込むことができます。右?

Ubuntu 18.04.1 LTSを実行していますが、bashリダイレクトに関するこの奇妙な問題に直面しています。

その例を説明しましょう。私のコマンドは次のとおりです。

# apt-cache show php5
N: Can't select versions from package 'php5' as it is purely virtual
N: No packages found
# |

php5パッケージはUbuntu 18.04に存在しないため、apt-cacheはエラーを表示します。このテキストはstderrストリームに送信されると想定するため、そのストリームを閉じようとしました。

# apt-cache show php5 2>&-
# |

これにより、テキストがstderrを介して送信されたことを確認できます。これまでのところすべて良い!しかし、今では健全性チェックとして、stdoutを閉じようとしました(エラーテキストが表示されるはずです)。

# apt-cache show php5 1>&-
# |

何!?今回はstdoutをリダイレクトしましたが、stderrも表示されませんか?

インターネットによると、私は正しいファイル記述子を持っています: https://www.gnu.org/software/bash/manual/html_node/Redirections.html

ここで何が起きているのかわかりません。

スクリーンショットの証拠:

Bash redirect bug

3
Bradley Odell

TL; DR:それはbashではなく、apt-cacheであり、ファイル記述子をいじっています。

apt-cacheは非常に興味深いことを行っています-not stdout用のN:文字で始まる行を書き出す傾向があります。

このことを考慮:

$ apt-cache show nonexistent
N: Unable to locate package nonexistent
E: No packages found

N:で始まる2行が表示されます。1行はE:で始まります。 N:行はstdoutに移動します。この例では、2つのN:行があります。

# apt-cache show php5
N: Can't select versions from package 'php5' as it is purely virtual
N: No packages found

strace -e write -f bash -c 'apt-cache show randomtext >&-'経由でシステムコールをトレースすると、E:行の書き込みが発生することがわかりますが、N行はありません。

[pid 12450] write(2, "E", 1E)            = 1
[pid 12450] write(2, ": ", 2: )           = 2
[pid 12450] write(2, "No packages found", 17No packages found) = 17
[pid 12450] write(2, "\n", 1
)           = 1
[pid 12450] +++ exited with 100 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12450, si_uid=1000, si_status=100, si_utime=5, si_stime=2} ---
+++ exited with 100 +++

したがって、apt-cacheは、リダイレクトされたstdoutをチェックするのに十分スマートです。しかし、stderrはどうですか?どうやらまだ書き込みがあります:strace -e write,openat,dup2 -f bash -c 'apt-cache show randomtext 2>&-を実行すると、apt-cache/dev/nullを開いて、stderrに何かが残っていることがわかります。

[pid 12543] openat(AT_FDCWD, "/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 2
....
[pid 12543] write(2, "N", 1)            = 1
[pid 12543] write(2, ": ", 2)           = 2
[pid 12543] write(2, "Unable to locate package randomt"..., 35) = 35
[pid 12543] write(2, "\n", 1)           = 1
[pid 12543] write(2, "E", 1)            = 1
[pid 12543] write(2, ": ", 2)           = 2
[pid 12543] write(2, "No packages found", 17) = 17
[pid 12543] write(2, "\n", 1)           = 1
[pid 12543] +++ exited with 100 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12543, si_uid=1000, si_status=100, si_utime=5, si_stime=3} ---
+++ exited with 100 +++

Bashの他のプログラムで同じことを実行すると、期待どおりに動作します。

# stdout closed, stderr not
$ ls -l /proc/self/fd >&-
ls: write error: Bad file descriptor
# stdout open , stderr closed, and it's number is assigned to whatever command is trying to open - in this case /proc/self/fd directory
$ ls -l /proc/self/fd 2>&-
total 0
lrwx------ 1 xie xie 64 Oct  6 11:32 0 -> /dev/pts/1
lrwx------ 1 xie xie 64 Oct  6 11:32 1 -> /dev/pts/1
lr-x------ 1 xie xie 64 Oct  6 11:32 2 -> /proc/12723/fd
3