node.js使用单独的stdout和stderrstream交互式产生一个subprocess

考虑下面的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,然后等待用户input,然后另一行stdout和另一行stderr。 非常基本! 在编译并在命令行上运行时,程序输出完成时(用户input为getchar()):

 $ ./test string out 1 string err 1 string out 2 string err 2 

当试图用下面的代码使用nodejs产生这个程序作为subprocess时:

 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 

与在terminal中运行./test时看到的输出非常不同。 这是因为./test程序在由nodejs产生时不在交互式shell中运行。 test.c的stdoutstream被缓冲,当达到\ n时,在terminal中运行时,缓冲区将被刷新,但是当以这种方式产生时,缓冲区不会被刷新。 这可以通过在每次打印之后刷新stdout或者将stdoutstream更改为无缓冲来解决,以便立即刷新所有内容。 假设test.c源文件不可用或者可以修改,那么两个提到的冲洗选项都不能实现。

然后我开始研究模拟一个交互式shell,有一个很好的pty.js(伪terminal),例如:

 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被合并在一起(就像你在terminal上运行程序的时候看到的那样),我想不出从数据stream中分离数据的方法。

所以这个问题

有没有办法使用nodejs来实现运行./test而不修改test.c代码时看到的输出? 是通过terminal仿真还是进程产卵或者其他方法?

干杯!

我尝试了user568109的答案,但这不起作用,这是有道理的,因为pipe道只是在数据stream之间复制数据。 因此,当缓冲区被刷新时,它只会到达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); //}); 

请注意,这有效地与节点进程共享stdio。 不知道你是否可以忍受。

你可以这样做 :

 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上打印输出时,由于函数的asynchronous执行,你会得到混乱的输出。 输出将被独立sorting,但输出仍然可以在stdinstdoutstderr之间交错。

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

这是更新的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,可能是类似的替代品,但。