Node.js 生成子进程并实时获取终端输出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14332721/
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
Node.js spawn child process and get terminal output live
提问by foklepoint
I have a script that outputs 'hi', sleeps for a second, outputs 'hi', sleeps for 1 second, and so on and so forth. Now I thought I would be able to tackle this problem with this model.
我有一个脚本,输出“hi”,休眠一秒,输出“hi”,休眠 1 秒,依此类推。现在我想我可以用这个模型解决这个问题。
var spawn = require('child_process').spawn,
temp = spawn('PATH TO SCRIPT WITH THE ABOVE BEHAVIOUR');
temp.stdout.pipe(process.stdout);
Now the problem is that the task needs to be finished in order for the output to be displayed. As I am understanding it, this is due to the fact that the newly spawned process takes execution control. Obviously node.js does not support threads so any solutions? My idea was to possibly run two instances, first one for the specific purpose of creating the task and have it pipe the output to process of the second instance, considering this can be achieved.
现在的问题是需要完成任务才能显示输出。据我了解,这是因为新生成的进程获得了执行控制。显然 node.js 不支持线程,所以有什么解决方案吗?我的想法是可能运行两个实例,第一个用于创建任务的特定目的,并将输出通过管道传输到第二个实例的进程,考虑到可以实现这一点。
采纳答案by RubyTuesdayDONO
I'm still getting my feet wet with Node.js, but I have a few ideas. first, I believe you need to use execFileinstead of spawn; execFileis for when you have the path to a script, whereas spawnis for executing a well-known command that Node.js can resolve against your system path.
我仍然对 Node.js 感兴趣,但我有一些想法。首先,我相信你需要使用execFile而不是spawn; execFile用于当您拥有脚本路径时,而spawn用于执行 Node.js 可以根据您的系统路径解析的众所周知的命令。
1. Provide a callbackto process the buffered output:
1.提供一个回调来处理缓冲的输出:
var child = require('child_process').execFile('path/to/script', [
'arg1', 'arg2', 'arg3',
], function(err, stdout, stderr) {
// Node.js will invoke this callback when process terminates.
console.log(stdout);
});
2. Add a listener to the child process' stdout stream(9thport.net)
2. 为子进程的标准输出流(9thport.net)添加一个监听器
var child = require('child_process').execFile('path/to/script', [
'arg1', 'arg2', 'arg3' ]);
// use event hooks to provide a callback to execute when data are available:
child.stdout.on('data', function(data) {
console.log(data.toString());
});
Further, there appear to be options whereby you can detach the spawned process from Node's controlling terminal, which would allow it to run asynchronously. I haven't tested this yet, but there are examples in the API docsthat go something like this:
此外,似乎有一些选项可以让您将生成的进程与 Node 的控制终端分离,这将允许它异步运行。我还没有测试过这个,但是API 文档中有一些例子是这样的:
child = require('child_process').execFile('path/to/script', [
'arg1', 'arg2', 'arg3',
], {
// detachment and ignored stdin are the key here:
detached: true,
stdio: [ 'ignore', 1, 2 ]
});
// and unref() somehow disentangles the child's event loop from the parent's:
child.unref();
child.stdout.on('data', function(data) {
console.log(data.toString());
});
回答by Katie
It's much easier now (6 years later)!
现在容易多了(6 年后)!
Spawn returns a childObject, which you can then listen for eventswith. The events are:
Spawn 返回一个childObject,然后您可以用它来监听事件。事件是:
- Class: ChildProcess
- Event: 'error'
- Event: 'exit'
- Event: 'close'
- Event: 'disconnect'
- Event: 'message'
- 类:ChildProcess
- 事件:'错误'
- 事件:'退出'
- 事件:'关闭'
- 事件:'断开'
- 事件:'消息'
There are also a bunch of objects from childObject, they are:
还有一堆来自 childObject的对象,它们是:
- Class: ChildProcess
- child.stdin
- child.stdout
- child.stderr
- child.stdio
- child.pid
- child.connected
- child.kill([signal])
- child.send(message[, sendHandle][, callback])
- child.disconnect()
- 类:ChildProcess
- 儿童标准输入
- 儿童标准输出
- child.stderr
- child.stdio
- 子进程
- 儿童连接
- child.kill([信号])
- child.send(message[, sendHandle][, callback])
- child.disconnect()
See more information here about childObject: https://nodejs.org/api/child_process.html
在此处查看有关 childObject 的更多信息:https://nodejs.org/api/child_process.html
Asynchronous
异步
If you want to run your process in the background while node is still able to continue to execute, use the asynchronous method. You can still choose to perform actions after your process completes, and when the process has any output (for example if you want to send a script's output to the client).
如果你想在后台运行你的进程而 node 仍然能够继续执行,请使用异步方法。您仍然可以选择在流程完成后以及流程有任何输出时执行操作(例如,如果您想将脚本的输出发送到客户端)。
child_process.spawn(...);(Node v0.1.90)
child_process.spawn(...); (节点 v0.1.90)
var spawn = require('child_process').spawn;
var child = spawn('node ./commands/server.js');
// You can also use a variable to save the output
// for when the script closes later
var scriptOutput = "";
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
//Here is where the output goes
console.log('stdout: ' + data);
data=data.toString();
scriptOutput+=data;
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
//Here is where the error output goes
console.log('stderr: ' + data);
data=data.toString();
scriptOutput+=data;
});
child.on('close', function(code) {
//Here you can get the exit code of the script
console.log('closing code: ' + code);
console.log('Full output of script: ',scriptOutput);
});
Here's how you would use a callback + asynchronous method:
以下是您将如何使用回调 + 异步方法:
var child_process = require('child_process');
console.log("Node Version: ", process.version);
run_script("ls", ["-l", "/home"], function(output, exit_code) {
console.log("Process Finished.");
console.log('closing code: ' + exit_code);
console.log('Full output of script: ',output);
});
console.log ("Continuing to do node things while the process runs at the same time...");
// This function will output the lines from the script
// AS is runs, AND will return the full combined output
// as well as exit code when it's done (using the callback).
function run_script(command, args, callback) {
console.log("Starting Process.");
var child = child_process.spawn(command, args);
var scriptOutput = "";
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
console.log('stdout: ' + data);
data=data.toString();
scriptOutput+=data;
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
console.log('stderr: ' + data);
data=data.toString();
scriptOutput+=data;
});
child.on('close', function(code) {
callback(scriptOutput,code);
});
}
Using the method above, you can send every line of output from the script to the client (for example using Socket.io to send each line when you receive events on stdoutor stderr).
使用上面的方法,您可以将脚本的每一行输出发送到客户端(例如,当您在stdout或上接收事件时,使用 Socket.io 发送每一行stderr)。
Synchronous
同步
If you want node to stop what it's doing and wait until the script completes, you can use the synchronous version:
如果你想让 node 停止它正在做的事情并等到脚本完成,你可以使用同步版本:
child_process.spawnSync(...);(Node v0.11.12+)
child_process.spawnSync(...); (节点 v0.11.12+)
Issues with this method:
这种方法的问题:
- If the script takes a while to complete, your server will hang for that amount of time!
- The stdout will only be returned once the script has finished running. Because it's synchronous, it cannot continue until the current line has finished. Therefore it's unable to capture the 'stdout' event until the spawn line has finished.
- 如果脚本需要一段时间才能完成,您的服务器将挂起这段时间!
- 只有在脚本完成运行后才会返回标准输出。因为它是同步的,所以在当前行完成之前它不能继续。因此,在 spawn line 完成之前,它无法捕获 'stdout' 事件。
How to use it:
如何使用它:
var child_process = require('child_process');
var child = child_process.spawnSync("ls", ["-l", "/home"], { encoding : 'utf8' });
console.log("Process finished.");
if(child.error) {
console.log("ERROR: ",child.error);
}
console.log("stdout: ",child.stdout);
console.log("stderr: ",child.stderr);
console.log("exist code: ",child.status);
回答by Harel Ashwal
Here is the cleanest approach I've found:
这是我发现的最干净的方法:
require("child_process").spawn('bash', ['./script.sh'], {
cwd: process.cwd(),
detached: true,
stdio: "inherit"
});
回答by agenaille
I had a little trouble getting logging output from the "npm install" command when I spawned npm in a child process. The realtime logging of dependencies did not show in the parent console.
当我在子进程中生成 npm 时,从“npm install”命令获取日志输出时遇到了一些麻烦。依赖项的实时日志记录未显示在父控制台中。
The simplest way to do what the original poster wants seems to be this (spawn npm on windows and log everything to parent console):
做原始海报想要什么的最简单方法似乎是这样的(在 Windows 上生成 npm 并将所有内容记录到父控制台):
var args = ['install'];
var options = {
stdio: 'inherit' //feed all child process logging into parent process
};
var childProcess = spawn('npm.cmd', args, options);
childProcess.on('close', function(code) {
process.stdout.write('"npm install" finished with code ' + code + '\n');
});
回答by Joel B
I found myself requiring this functionality often enough that I packaged it into a library called std-pour. It should let you execute a command and view the output in real time. To install simply:
我发现自己经常需要这个功能,所以我将它打包到一个名为std-pour的库中。它应该让您执行命令并实时查看输出。简单安装:
npm install std-pour
Then it's simple enough to execute a command and see the output in realtime:
然后就可以很简单地执行一个命令并实时查看输出:
const { pour } = require('std-pour');
pour('ping', ['8.8.8.8', '-c', '4']).then(code => console.log(`Error Code: ${code}`));
It's promised based so you can chain multiple commands. It's even function signature-compatible with child_process.spawnso it should be a drop in replacement anywhere you're using it.
它是基于承诺的,因此您可以链接多个命令。它甚至与函数签名兼容,child_process.spawn因此在您使用它的任何地方都应该是替代品。
回答by Sandro Pasquali
child:
孩子:
setInterval(function() {
process.stdout.write("hi");
}, 1000); // or however else you want to run a timer
parent:
家长:
require('child_process').fork('./childfile.js');
// fork'd children use the parent's stdio
回答by Rik
Adding an answer related to child_process.execas I too had needed live feedback and wasn't getting any until after the script finished. This also supplements my comment to the accepted answer, but as it's formatted it will a bit more understandable and easier to read.
添加一个与child_process.exec我也需要实时反馈相关的答案,直到脚本完成后才得到任何反馈。这也补充了我对已接受答案的评论,但由于它已格式化,因此更易于理解和阅读。
Basically, I have a npm script that calls Gulp, invoking a task which subsequently uses child_process.execto execute a bash or batch script depending on the OS. Either script runs a build process via Gulp and then makes some calls to some binaries that work with the Gulp output.
基本上,我有一个 npm 脚本,它调用 Gulp,调用一个任务,该任务随后用于child_process.exec根据操作系统执行 bash 或批处理脚本。这两个脚本都通过 Gulp 运行构建过程,然后调用一些与 Gulp 输出一起工作的二进制文件。
It's exactly like the others (spawn, etc.), but for the sake of completion, here's exactly how to do it:
它和其他的(生成等)完全一样,但为了完成,这里是如何做到的:
// INCLUDES
import * as childProcess from 'child_process'; // ES6 Syntax
// GLOBALS
let exec = childProcess.exec; // Or use 'var' for more proper
// semantics, though 'let' is
// true-to-scope
// Assign exec to a variable, or chain stdout at the end of the call
// to exec - the choice, yours (i.e. exec( ... ).stdout.on( ... ); )
let childProcess = exec
(
'./binary command -- --argument argumentValue',
( error, stdout, stderr ) =>
{
if( error )
{
// This won't show up until the process completes:
console.log( '[ERROR]: "' + error.name + '" - ' + error.message );
console.log( '[STACK]: ' + error.stack );
console.log( stdout );
console.log( stderr );
callback(); // Gulp stuff
return;
}
// Neither will this:
console.log( stdout );
console.log( stderr );
callback(); // Gulp stuff
}
);
Now its as simple as adding an event listener. For stdout:
现在就像添加一个事件监听器一样简单。对于stdout:
childProcess.stdout.on
(
'data',
( data ) =>
{
// This will render 'live':
console.log( '[STDOUT]: ' + data );
}
);
And for stderr:
而对于stderr:
childProcess.stderr.on
(
'data',
( data ) =>
{
// This will render 'live' too:
console.log( '[STDERR]: ' + data );
}
);
Not too bad at all - HTH
一点也不差 - HTH

