从 Node.js 运行 shell 命令而不缓冲输出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9777215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:22:08  来源:igfitidea点击:

Running a shell command from Node.js without buffering output

node.jschild-process

提问by Joe White

I'm trying to launch a shell command from Node.js, withoutredirecting that command's input and output -- just like shelling out to a command using a shell script, or using Ruby's systemcommand. If the child process wants to write to STDOUT, I want that to go straight to the console (or get redirected, if my Node app's output was redirected).

我正在尝试从 Node.js 启动一个 shell 命令,而不重定向该命令的输入和输出——就像使用 shell 脚本或使用 Ruby 的system命令对命令进行炮击一样。如果子进程想要写入 STDOUT,我希望它直接进入控制台(或者被重定向,如果我的 Node 应用程序的输出被重定向)。

Node doesn't seem to have any straightforward way to do this. It looks like the only way to run another process is with child_process, which alwaysredirects the child process's input and output to pipes. I can write code to accept data from those pipes and write it to my process's STDOUT and STDERR, but if I do that, the APIs force me to sacrifice some flexibility.

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

I want two features:

我想要两个功能:

  • Shell syntax. I want to be able to pipe output between commands, or run Windows batch files.
  • Unlimited output. If I'm shelling out to a compiler and it wants to generate megabytes of compiler warnings, I want them all to scroll across the screen (until the user gets sick of it and hits Ctrl+C).
  • 外壳语法。我希望能够在命令之间进行管道输出,或者运行 Windows 批处理文件。
  • 无限输出。如果我对编译器进行炮击并且它想要生成数兆字节的编译器警告,我希望它们都在屏幕上滚动(直到用户厌倦它并按 Ctrl+C)。

It looks like Node wants to force me choose between those two features.

看起来 Node 想要强迫我在这两个功能之间做出选择。

  • If I want an unlimited amount of output, I can use child_process.spawnand then do child.stdout.on('data', function(data) { process.stdout.write(data); });and the same thing for stderr, and it'll happily pipe data until the cows come home. Unfortunately, spawndoesn't support shell syntax.
  • If I want shell syntax, I can use child_process.exec. But execinsists on buffering the child process's STDOUT and STDERR for me and giving them to me all at the end, and it limits the size of those buffers (configurable, 200K by default). I can still hook the on('data')events, if I want to see the output as it's generated, but execwill still add the data to its buffers too. When the amount of data exceeds the predefined buffer size, execwill terminate the child process.
  • 如果我想要无限量的输出,我可以使用child_process.spawn然后对 做child.stdout.on('data', function(data) { process.stdout.write(data); });同样的事情stderr,它会很高兴地传输数据,直到奶牛回家。不幸的是,spawn不支持 shell 语法。
  • 如果我想要 shell 语法,我可以使用child_process.exec. 但是exec坚持为我缓冲子进程的 STDOUT 和 STDERR 并在最后将它们全部交给我,并且它限制了这些缓冲区的大小(可配置,默认为 200K)。on('data')如果我想查看生成的输出,我仍然可以挂钩事件,但exec仍会将数据添加到其缓冲区。当数据量超过预定义的缓冲区大小时,exec将终止子进程。

(There's also child_process.execFile, which is the worstof both worlds from a flexibility standpoint: no shell syntax, but you still have to cap the amount of output you expect.)

(还有child_process.execFile,从灵活性的角度来看,这是两个世界中最糟糕的:没有 shell 语法,但您仍然必须限制您期望的输出量。)

Am I missing something? Is there any way to just shell out to a child process in Node, and notredirect its input and output?Something that supports shell syntax and doesn't crap out after a predefined amount of output, just like is available in shell scripts, Ruby, etc.?

我错过了什么吗?有什么办法可以只对 Node 中的子进程进行 shell,而不重定向其输入和输出?支持 shell 语法并且在预定义的输出量后不会出错的东西,就像在 shell 脚本、Ruby 等中可用一样?

回答by Andrey Sidorov

You can inherit stdin/out/error streams via spawnargument so you don't need to pipe them manually:

您可以通过spawn参数继承 stdin/out/error 流,因此您无需手动管道它们:

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

Use shell for shell syntax - for bash it's -cparameter to read script from string:

将 shell 用于 shell 语法 - 对于 bash,它是-c从字符串读取脚本的参数:

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

To summarise:

总结一下:

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

shspawn('ls -l | grep test | wc -c');

回答by Skywalker13

You can replace execby spawnand use the shell syntax simply with:

您可以替换exec通过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 ());
});

The magic is just {shell: true}.

魔术只是{shell: true}

回答by loganfsmyth

I haven't used it, but I've seen this library: https://github.com/polotek/procstreams

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

It you'd do this. The .out()automatically pipes to the process's stdin/out.

如果你会这样做。该.out()自动管道进程的标准输入/输出。

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

If doesn't support shell syntax, but that's pretty trivial I think.

如果不支持 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
  });

回答by darelf

There's an example in the node docsfor the child_process module:

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

Example of detaching a long-running process and redirecting its output to a file:

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

 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();