读取未缓冲的子进程stdout输出

8

我正在尝试实时读取由Node.js启动的Python脚本的输出结果。然而,我只能在进程完成后才可以访问数据。

var proc, args;

args = [
    './bin/build_map.py',
    '--min_lon',
    opts.sw.lng,
    '--max_lon',
    opts.ne.lng,
    '--min_lat',
    opts.sw.lat,
    '--max_lat',
    opts.ne.lat,
    '--city',
    opts.city
];

proc = spawn('python', args);

proc.stdout.on('data', function (buf) {
    console.log(buf.toString());
    socket.emit('map-creation-response', buf.toString());
});

如果我使用 { stdio : 'inherit' } 启动进程,我可以直接在控制台上看到输出。但是像 process.stdout.on('data', ...) 这样的操作将不起作用。

我该如何确保能够及时读取子进程的输出并将其定向到其他地方?


1
这种缓冲发生在你启动的进程中。在Node中无法做任何事情,你必须在Python程序中解决它。规范文档在此处:(https://dev59.com/inVD5IYBdhLWcg3wDHDm)。 - Hans Passant
2个回答

3
缓冲数据的进程是 Python,因为它知道终端已重定向并不会直接传输到终端。你可以很容易地告诉 Python 不要缓冲:只需运行 "python -u" 而非 "python" 即可。应该很简单。

0
当使用child_process.spawn()生成一个进程时,与子进程的标准输出和标准错误流连接的流实际上在Nodejs端是未缓冲的。为了说明这一点,请考虑以下程序:
const spawn = require('child_process').spawn;

var proc = spawn('bash', [
  '-c',
  'for i in $(seq 1 80); do echo -n .; sleep 1; done'
]);

proc.stdout
.on('data', function (b) {
  process.stdout.write(b);
})
.on('close', function () {
  process.stdout.write("\n");
});

此程序运行bash,并每秒发出字符80秒,同时通过data事件消耗该子进程的标准输出。您应该注意到Node程序每秒发出的点,以确认在Nodejs方面不会发生缓冲。

此外,正如child_process中Nodejs文档所述:

默认情况下,父Node.js进程和生成的子进程之间建立了stdin、stdout和stderr管道。可以通过这些管道以非阻塞方式流式传输数据。然而,请注意,某些程序在内部使用行缓冲I/O。虽然这不影响Node.js,但可能意味着发送到子进程的数据可能不会立即被消耗。

您可能想要确认Python程序不会缓冲其输出。如果您感觉从Python程序发出的数据是单独的不同写入标准输出,则可以考虑在每次写入后运行sys.stdout.flush(),以建议Python实际写入数据而不是尝试缓冲它。

更新: 在这个提交中,Nodejs文档中的那段话被删除了,原因如下:

文档:删除关于子进程stdio的混淆说明

这段话的意思不明确。特别是,一个进程使用什么样的缓冲机制来处理其stdio流并不影响它何时消耗发送给它的数据,通常不能保证有任何保证。

这表明在Nodejs进程接收数据之前可能会有缓冲。尽管如此,应该注意确保在Nodejs上游受控制的进程不会缓冲其输出。


去掉sleep语句,看看会发生什么。它被缓冲了,但缓冲时间不到一秒。 - temporary_user_name
这里涉及到的缓冲区大小以字节为单位进行测量。我不知道是否有任何缓冲区确保延迟(例如少于一秒的缓冲区)。该示例演示了stdout流上的缓冲区小于一个字节(因为数据事件在数据生成时被发出)。如果您觉得存在延迟,可能值得对Nodejs应用程序进行分析以确定时间花费在哪里。逐个字节通过流读取数据将带来相当大的开销,这可能解释了延迟。这就是为什么缓冲通常是有益的原因。 - ctt
也许我说错了,但无论如何,如果你去掉 sleep 语句,它都会缓冲。我的意思是,如果你是对的,那么去掉 sleep 语句不应该有影响。 - temporary_user_name
你到底观察到了什么迹象表明正在进行缓冲?你如何衡量缓冲的定义?你是否认为输出被缓冲,因为似乎所有的输出在按下enter调用node后的一瞬间同时出现在屏幕上?还是你使用了更客观的衡量标准? - ctt
@temporary_user_name 为了让事情更清楚,我在child_process文档中包含了一段内容。尽管如此,使用Streams API未缓冲处理数据会导致与处理相关的开销引起的延迟。 - ctt
@temporary_user_name 我又更新了一些内容来解释更多的东西。答案更准确,但是确实表明除Python引入的任何缓冲/延迟之外,缓冲或延迟也可能存在。因此仍然值得确保Python尽可能经常地刷新其输出。 - ctt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接