我正在使用一个菲菲对于nodejs,这在很大程度上与这个问题无关,这实际上是为了更好地理解管道,但确实提供了一些上下文。
function exec(cmd) {
var buffer = new Buffer(32);
var result = '';
var fp = libc.popen('( ' + cmd + ') 2>&1', 'r');
var code;
if (!fp) throw new Error('execSync error: '+cmd);
while( !libc.feof(fp) ){
libc.fgets(buffer, 32, fp)
result += buffer.readCString();
}
code = libc.pclose(fp) >> 8;
return {
stdout: result,
code: code
};
}
这让我看到了这段代码,当我使用这个 exec 函数运行时
tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}
我收到错误:
write error: Broken pipe
tr: write error
但我确实得到了我期望的输出:8 个随机数。这让我很困惑,但后来在一些疯狂的谷歌搜索中我发现这堆栈答案完全适合我的情况。
不过,我还有一些问题。
为什么:
tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}
使用我的 exec 命令调用时抛出损坏的管道错误,但从 shell 调用时不会抛出损坏的管道错误?我不明白为什么当我打电话时:
tr -dc "[:alpha:]" < /dev/urandom
它会无休止地读取,但是当我通过管道将其传输到:
head -c ${1-8}
它可以正常工作,不会引发管道损坏错误。看起来这head
将需要它所需要的并且tr
会永远阅读。至少应该扔掉破管子;head
将消耗前 8 个字节,然后仍会输出输出,并且由于已停止运行tr
而抛出损坏的管道。tr
head
这两种情况对我来说都有道理,但似乎它们是互斥的。我不明白调用之间有什么不同:
exec(tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8})
和
tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}
直接从命令行,特别是为什么<
一个无休止的文件到某个东西,然后|
它到另一个东西使它不会无休止地运行。我这样做已经很多年了,从来没有质疑过为什么会这样。
最后,是否可以忽略这个损坏的管道错误?有办法解决吗?我在我的 C++ ish javascript 代码中做错了什么吗?我是否缺少某种 popen 基础知识?
- - - 编辑
乱搞一些代码
exec('head -10 /dev/urandom | tr -dc "[:alpha:]" | head -c 8')
不会抛出任何管道错误!
答案1
通常,tr
不应该能够写入该错误消息,因为在head
.
您收到该错误消息是因为不知何故,正在运行的进程tr
已配置为忽略 SIGPIPE。我怀疑这可能是通过用popen()
你的语言实现来完成的。
您可以通过执行以下操作来重现它:
sh -c 'trap "" PIPE; tr -dc "[:alpha:]" < /dev/urandom | head -c 8'
您可以通过执行以下操作来确认正在发生的情况:
strace -fe signal sh your-program
(或者如果不使用 Linux,则在您的系统上具有同等功能)。然后你会看到类似的内容:
rt_sigaction(SIGPIPE, {SIG_IGN, ~[RTMIN RT_1], SA_RESTORER, 0x37cfc324f0}, NULL, 8) = 0
或者
signal(SIGPIPE, SIG_IGN)
在同一进程或其后代之一执行/bin/sh
解释该命令行并启动tr
和 之前,在一个进程中完成head
。
如果你执行 a strace -fe write
,你会看到类似的内容:
write(1, "AJiYTlFFjjVIzkhCAhccuZddwcydwIIw"..., 4096) = -1 EPIPE (Broken pipe)
系统write
调用失败并出现 EPIPE 错误,而不是触发 SIGPIPE。
无论如何tr
都会退出。当忽略 SIGPIPE 时,因为该错误(但这也会触发错误消息)。如果不是,则在收到 SIGPIPE 后退出。您确实希望它退出,因为您不希望它/dev/urandom
在这 8 个字节之后read
继续读取head
。
为了避免出现该错误消息,您可以使用以下命令恢复 SIGPIPE 的默认处理程序:
trap - PIPE
致电之前tr
:
popen("trap - PIPE; { tr ... | head -c 8; } 2>&1", ...)
答案2
这对我也有用。只需添加 tr 即可抑制错误消息2>/dev/null
:
tr -dc "[:alpha:]" < /dev/urandom 2>/dev/null | head -c 8