16

考虑以下 C 程序 (test.c):

#include <stdio.h>

int main() {
  printf("string out 1\n");
  fprintf(stderr, "string err 1\n");
  getchar();
  printf("string out 2\n");
  fprintf(stderr, "string err 2\n");
  fclose(stdout);
}

哪个应该打印一行到stdout,一行到stderr,然后等待用户输入,然后另一行到stdout,另一行到stderr。很基础!编译并在命令行上运行完成时程序的输出(getchar() 接收用户输入):

$ ./test 
string out 1
string err 1

string out 2
string err 2

当尝试使用带有以下代码的 nodejs 将该程序作为子进程生成时:

var TEST_EXEC = './test';

var spawn = require('child_process').spawn;
var test = spawn(TEST_EXEC);

test.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

test.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

// Simulate entering data for getchar() after 1 second
setTimeout(function() {
  test.stdin.write('\n');
}, 1000);

输出如下所示:

$ nodejs test.js 
stderr: string err 1

stdout: string out 1
string out 2

stderr: string err 2

与在终端中运行 ./test 时看到的输出非常不同。这是因为 ./test 程序在由 nodejs 生成时没有在交互式 shell 中运行。test.c 标准输出流被缓冲,并且当在终端中运行时,一旦到达 \n,缓冲区就会被刷新,但是当使用节点以这种方式产生时,缓冲区不会被刷新。这可以通过在每次打印后刷新标准输出来解决,或者将标准输出流更改为无缓冲以便立即刷新所有内容。假设 test.c 源不可用或不可修改,则提到的两个刷新选项都不能实现。

然后我开始研究模拟交互式外壳,有 pty.js(伪终端),它做得很好,例如:

var spawn = require('pty.js').spawn;
var test = spawn(TEST_EXEC);

test.on('data', function (data) {
  console.log('data: ' + data);
});

// Simulate entering data for getchar() after 1 second
setTimeout(function() {
  test.write('\n');
}, 1000);

哪个输出:

$ nodejs test.js
data: string out 1
string err 1

data: 

data: string out 2
string err 2

但是,stdout 和 stderr 都合并在一起(正如您在终端中运行程序时所看到的那样),我想不出一种将数据与流分开的方法。

所以问题..

有什么方法可以使用 nodejs 来实现运行 ./test 时看到的输出而不修改 test.c 代码?通过终端仿真或进程生成或任何其他方法?

干杯!

4

3 回答 3

15

我尝试了 user568109 的答案,但这不起作用,这是有道理的,因为管道仅在流之间复制数据。因此,它仅在刷新缓冲区时才到达 process.stdout ......以下似乎有效:

var TEST_EXEC = './test';

var spawn = require('child_process').spawn;
var test = spawn(TEST_EXEC, [], { stdio: 'inherit' });

//the following is unfortunately not working 
//test.stdout.on('data', function (data) {
//  console.log('stdout: ' + data);
//});

请注意,这有效地与节点进程共享标准输入输出。不确定你是否可以忍受。

于 2013-04-29T14:09:05.873 回答
5

我只是重新审视了这一点,因为从 5.7.0 版本开始,节点中的 spawn 命令现在有一个“shell”选项可用。不幸的是,似乎没有生成交互式外壳的选项(我也尝试过shell: '/bin/sh -i'但没有快乐)。但是我刚刚发现建议使用“stdbuf”,允许您更改要运行的程序的缓冲选项。在所有内容上将它们设置为 0 会为所有流产生无缓冲的输出,并且它们仍然保持独立。

这是更新的javascript:

var TEST_EXEC = './test';

var spawn = require('child_process').spawn;
var test = spawn('stdbuf', ['-i0', '-o0', '-e0', TEST_EXEC]);

test.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

test.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

// Simulate entering data for getchar() after 1 second
setTimeout(function() {
  test.stdin.write('\n');
}, 1000);

看起来这没有预装在 OSX 上,当然也不适用于 Windows,但可能是类似的替代方案。

于 2016-03-03T11:15:20.187 回答
2

你可以这样做 :

var TEST_EXEC = 'test';
var spawn = require('child_process').spawn;
var test = spawn(TEST_EXEC);

test.stdin.pipe(process.stdin);
test.stdout.pipe(process.stdout);
test.stderr.pipe(process.stderr);

当您使用事件stdoutstderr在 上打印输出时console.log,由于函数的异步执行,您将得到混乱的输出。输出将为流独立排序,但输出仍然可以在和stdin之间交错。stdoutstderr

于 2013-03-11T18:19:11.683 回答