为什么在 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

当然,所有的重定向和嵌套 shell 脱离上下文看起来都很有趣。额外的 shell 是必要的,因为 say-something.sh 实际上会被另一个脚本调用。 fd 2 到 stderr 的冗余重定向是方便可选重定向到文件的“开关”(/dev/stderr,或者不同的路径,实际上是变量的可配置内容)。

似乎这条管道的所有组成部分都是必要的,正如失败示例后的实验所表明的那样:它们都成功了。

  • 我们需要标准输出的最后一个管道
  • 我们需要调用者将 stderr 复制到 stdout
  • 我们需要命令替换的外壳。

答案1

/dev/stderr当重定向到管道时,该名称实际上是有效的。可能不太可能的是/dev/stderr直接打开最终的目标。就看:

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

|由or创建的管道|&通常是匿名管道;显示的名称与文件系统中的对象不对应。作为示例,您可以尝试一些简单的操作,例如:

$ 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

我认为问题在于产生的读取进程。它有自己的重定向管道,当进程停止时该管道会关闭(您获得的 pid 不是 shell 的 pid,而是 readlink 进程的 pid)。当进程退出时,管道将变得无效。尝试使用 fifos/命名管道。

相关内容