popen 和 JS ffi 出现管道损坏错误

popen 和 JS ffi 出现管道损坏错误

我正在使用一个菲菲对于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而抛出损坏的管道。trhead

这两种情况对我来说都有道理,但似乎它们是互斥的。我不明白调用之间有什么不同:

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

相关内容