子进程-从Node.js运行Shell命令而不缓冲输出

我正在尝试从Node.js启动shell命令,而没有重定向该命令的输入和输出-就像使用shell脚本或使用Ruby的child_process命令将其shell化为命令一样。 如果子进程想要写入STDOUT,则我希望它直接进入控制台(或重定向,如果Node应用程序的输出已重定向)。

Node似乎没有任何直接的方法来执行此操作。 看来运行另一个进程的唯一方法是使用child_process,它始终将子进程的输入和输出重定向到管道。 我可以编写代码以接受来自那些管道的数据,然后将其写入我进程的STDOUT和STDERR,但是如果这样做,API会迫使我牺牲一些灵活性。

我想要两个功能:

  • Shell语法。 我希望能够在命令之间传递输出,或运行Windows批处理文件。
  • 无限的输出。 如果我要使用编译器,并且它想生成兆字节的编译器警告,则希望它们全部在屏幕上滚动(直到用户厌倦了它并按Ctrl + C)。

看来Node要强迫我在这两个功能之间进行选择。

  • 如果我想要无限量的输出,我可以使用child_process.execFile,然后对on('data')使用child_process.execFile和同样的方法,它将愉快地传输数据,直到母牛回家。 不幸的是,exec不支持shell语法。
  • 如果我需要shell语法,则可以使用child_process.execFile。但是child_process.execFile坚持为我缓冲子进程的STDOUT和STDERR并在最后将它们全部提供给我,这限制了这些缓冲区的大小(可配置,默认为200K)。 如果我想查看生成的输出,我仍然可以钩住on('data')事件,但是exec仍然会将数据也添加到其缓冲区中。 当数据量超过预定义的缓冲区大小时,exec将终止子进程。

(还有child_process.execFile,从灵活性的角度来看,这是两全其美的情况:没有shell语法,但是您仍然必须限制期望的输出量。)

我想念什么吗? 有什么方法可以只封装到Node中的子进程,而不重定向其输入和输出吗? 支持shell语法并且在预定义数量的输出后不会消失的东西,就像shell脚本,Ruby等中可用的东西一样吗?

Joe White asked 2020-01-13T12:33:16Z
4个解决方案
42 votes

您可以通过-c参数继承stdin / out / error流,因此无需手动传递它们:

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

使用shell进行shell语法-对于bash来说,它是-c参数来从字符串读取脚本:

var spawn = require('child_process').spawn;
var shellSyntaxCommand = 'ls -l | grep test | wc -c';
spawn('sh', ['-c', shellSyntaxCommand], { stdio: 'inherit' });

总结一下:

var spawn = require('child_process').spawn;
function shspawn(command) {
   spawn('sh', ['-c', command], { stdio: 'inherit' });
} 

shspawn('ls -l | grep test | wc -c');
Andrey Sidorov answered 2020-01-13T12:33:47Z
7 votes

您可以将{shell: true}替换为spawn,并仅通过以下方式使用shell语法:

const {spawn} = require ('child_process');
const cmd = 'ls -l | grep test | wc -c';
const p = spawn (cmd, [], {shell: true});

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

魔术只是{shell: true}

Skywalker13 answered 2020-01-13T12:34:12Z
4 votes

我没有用过,但是我看过这个库:[https://github.com/polotek/procstreams]

你会做到这一点。 .out()自动将管道传输到进程的标准输入/输出。

var $p = require('procstreams');
$p('cat lines.txt').pipe('wc -l').out();

如果不支持shell语法,但是我认为这很简单。

var command_str = "cat lines.txt | wc -l";
var cmds = command_str.split(/\s?\|\s?/);
var cmd = $p(cmds.shift());
while(cmds.length) cmd = cmd.pipe(cmds.shift());
cmd
  .out()
  .on('exit', function() {
    // Do whatever
  });
loganfsmyth answered 2020-01-13T12:34:41Z
0 votes

节点文档中有一个有关child_process模块的示例:

分离长时间运行的进程并将其输出重定向到文件的示例:

 var fs = require('fs'),
     spawn = require('child_process').spawn,
     out = fs.openSync('./out.log', 'a'),
     err = fs.openSync('./out.log', 'a');

 var child = spawn('prg', [], {
   detached: true,
   stdio: [ 'ignore', out, err ]
 });

 child.unref();
darelf answered 2020-01-13T12:35:06Z
translate from https://stackoverflow.com:/questions/9777215/running-a-shell-command-from-node-js-without-buffering-output