NodeJS child_process stdout,如果进程正在等待 stdin

NodeJS child_process stdout, if process is waiting for stdin

我正在开发一个应用程序,它允许编译和执行通过 api 给出的代码。

我要执行的二进制文件保存为 input_c 并且应该打印一个文本,询问用户的姓名并在收到输入后打印出另一个文本。

使用以下代码可以正常工作:第一个文本 - 输入(在终端上) - 第二个文本。

const {spawn} = require('child_process');
let cmd = spawn('input_c', [], {stdio: [process.stdin, process.stdout, process.stderr]});

输出:

$ node test.js
Hello, what is your name? Heinz
Hi Heinz, nice to meet you!

我喜欢单独处理 stdout、stderr 和 stdin,而不是将其写入终端。以下代码是我尝试实现与上述相同的行为:

const {spawn} = require('child_process');

let cmd = spawn('input_c');

cmd.stdout.on('data', data => {
    console.log(data.toString());
});

cmd.stderr.on('data', data => {
    console.log(data.toString());
});

cmd.on('error', data => {
    console.log(data.toString());
});

// simulating user input
setTimeout(function() {
    console.log('Heinz');
    cmd.stdin.write('Heinz\n');
}, 3000);

输出:

$ node test.js
Heinz
Hello, what is your name? Hi Heinz, nice to meet you!

为了模拟用户输入,我在 3000 毫秒后写入标准输入。但是这里我没有直接在 运行 上接收到 stdout 中的第一个数据,它似乎在等待 stdin 并立即输出所有内容。

我怎样才能为我的第二个案例实现相同的行为?

以下 C 代码用于编译二进制文件,但任何等待用户输入的应用程序都可用于此目的:

#include <stdio.h>

int main() {
    char name[32];
    printf("Hello, what is your name? ");
    scanf("%s", name);
    printf("Hi %s, nice to meet you!", name);
    return 0;
}

node-pty可以在这里使用来防止子进程的缓冲输出。

const pty = require('node-pty');

let cmd = pty.spawn('./input_c');

cmd.on('data', data => {
    console.log(data.toString());
});

// simulating user input
setTimeout(function() {
    console.log('Heinz');
    cmd.write('Heinz\n');
}, 3000);

输出:

Hello, what is your name?
Heinz
Heinz
Hi Heinz, nice to meet you!

您遇到的 stdout.on() 事件在 spawn() 之后未被触发的问题出现是因为 node.js 生成子进程的方式。本质上,它创建 stream.pipe()(默认情况下),它允许子进程在将输出发送到 stdout 之前缓冲输出,它是由节点提供的,这通常有利于性能。

但是,由于您需要实时输出并且您负责二进制文件,因此您可以简单地禁用内部缓冲。在 C 中,您可以通过将 setbuf(stdout, NULL); 添加到程序的开头来实现:

#include <stdio.h>

int main() {
    setbuf(stdout, NULL);
    char name[32];
    printf("Hello, what is your name? ");
    scanf("%31s", name);
    printf("Hi %s, nice to meet you!", name);
    return 0;
}

或者,您可以在每个 printf()puts() 等之后调用 fflush(stdout);

#include <stdio.h>

int main() {
    char name[32];
    printf("Hello, what is your name? "); fflush(stdout);
    scanf("%31s", name);
    printf("Hi %s, nice to meet you!", name); fflush(stdout);
    return 0;
}

在子进程中禁用内部缓冲或触发显式刷新后,您将立即获得预期的行为,而无需任何外部依赖。

更新:

许多应用程序有意抑制或至少允许抑制 stdio 缓冲,因此您可能会找到相关的启动参数。例如,您可以使用 -u 选项启动 python 解释器二进制文件,这将 强制 stdin、stdout 和 stderr 完全无缓冲。还有几个与 nodejsstdio buffering problems 相关的旧问题,您可能会发现有用,例如: