【问题标题】:Nodejs: write to stdin of bash process crashes with EPIPENodejs:使用 EPIPE 写入 bash 进程的标准输入崩溃
【发布时间】:2023-12-18 08:43:01
【问题描述】:

我的节点进程通过 HTTP 请求获取一些 PDF 文件,然后使用请求的 onData 事件将传入数据传递到通过 child_process.exec 生成的正确配置的 lpr。我使用process.stdin.write(...) 写入标准输入,然后在完成后使用process.stdin.end()。这让我可以立即打印这些文件。

现在我有一种情况,我不希望将数据通过管道传输到 lpr,而是传输到一些 bash 脚本。该脚本使用cat 处理其stdin

myscript.sh < somefile.pdf 按预期工作,cat somefile.pdf | myscript.sh 也是如此。

但是,当我从节点生成 /path/to/script.sh 时(只需将 lpr 替换为源中的脚本路径),进程退出

events.js:183 throw er; // Unhandled 'error' event ^ Error: write EPIPE at WriteWrap.afterWrite [as oncomplete] (net.js:868:14)

随后,整个节点进程崩溃,错误潜入所有try...catch 块。 bash 脚本开头的日志显示,它甚至没有启动。

当我针对任何不是 shell 脚本而是一些已编译的可执行文件(如 catecho、...)时,一切正常。 添加epipebomb module 不会改变任何东西。

我也尝试通过管道连接到process.exec("bash", ["-c cat | myscript.sh"]),但出现了同样的错误。

一个示例 bash 脚本,仅用于测试执行:

#!/usr/bin/env bash
date > logfile.txt
cat > /dev/null

编辑: 我想我可能需要发出信号以保持标准输入流以某种方式打开。 脚本的进程生成部分,不考虑承诺和输出处理:

const process = require("child_process")    

// inputObservable being an rxjs Observable
execstuff(inputObervable) {
  const task = process.spawn("/path/to/script.sh");
  inputObservable.subscribe(
    chunk => task.stdin.write(chunk),
    error => console.error(error),
    finished => task.stdin.end()
  );
}

【问题讨论】:

  • 你能把你的脚本代码贴出来吗,我直觉你没有正确使用流...
  • 我添加了一些代码@AlexandruOlaru
  • 对此是否有任何可接受的解决方案? @waechtertroll

标签: node.js linux bash stdin


【解决方案1】:

child_process.spawn 有一个示例,您可以如何编写以下行 ps ax | grep ssh 作为 node.js 脚本,也许它会对您有所帮助:

const { spawn } = require('child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);

ps.stdout.on('data', (data) => {
  grep.stdin.write(data);
});

ps.stderr.on('data', (data) => {
  console.log(`ps stderr: ${data}`);
});

第一印象是你在做同样的事情,问题可能出在块数据上,可能其中一个块为空,它正在关闭流,你想通过运行 task.stdin 来关闭它.end()。

您可以尝试的另一件事是使用 NODE_DEBUG=stream node script.js 运行 node.js 脚本 将记录 node.js 内部流的行为方式,也可能对您有所帮助。

【讨论】:

  • 我知道那本手册,我认为它的作用基本相同。我可以修复崩溃问题 - 在调试期间,我在 shell 脚本中插入了一些日志记录,该脚本关闭了标准输入,因此 Node 在报告关闭的流时是正确的。
  • 仍然,数据似乎没有到达脚本文件中。数据是原始缓冲区,只是简单地传递过来,其中没有过早的nulls。我现在就试试流调试环境变量。
最近更新 更多