Node.js 生成子进程并实时获取终端输出
Posted
技术标签:
【中文标题】Node.js 生成子进程并实时获取终端输出【英文标题】:Node.js spawn child process and get terminal output live 【发布时间】:2012-12-29 06:08:05 【问题描述】:我有一个脚本,它输出“hi”,休眠一秒钟,输出“hi”,休眠 1 秒,依此类推。现在我想我可以用这个模型解决这个问题。
var spawn = require('child_process').spawn,
temp = spawn('PATH TO SCRIPT WITH THE ABOVE BEHAVIOUR');
temp.stdout.pipe(process.stdout);
现在的问题是需要完成任务才能显示输出。据我了解,这是因为新生成的进程具有执行控制权。显然 node.js 不支持线程所以有什么解决方案吗?我的想法是可能运行两个实例,第一个用于创建任务的特定目的,并让它通过管道将输出传递给第二个实例的进程,考虑到这可以实现。
【问题讨论】:
如果子进程写成python
,那么不要忘记传递-u
标志以使其不缓冲控制台输出,否则它看起来像脚本不活动***.com/a/49947671/906265
使用npmjs.com/package/cross-spawn 代替其他任何东西。它只是更好。
【参考方案1】:
Asynchronous
我喜欢的方法是:
const spawn = require('child_process').spawnSync;
try
const child = spawn(cmd)
console.log(child.stdout.toString());
console.log(child.stderr.toString())
catch (error)
console.log(error);
return error
【讨论】:
【参考方案2】:我在生成 Python 3 脚本时遇到了上述情况均不起作用的情况。我会从标准输出中获取数据,但只有在孩子终止后。
事实证明,Python 默认缓冲标准输出。可以通过将-u
作为python3 的命令行参数来禁用stdout 缓冲。
【讨论】:
【参考方案3】:为exec
添加一个示例,因为我也需要实时反馈,直到脚本完成后才得到任何反馈。 exec
确实返回一个 EventEmitter,这与许多声称只有 spawn
以这种方式工作的说法相反。
这更彻底地补充了我对已接受答案的评论。
exec的接口类似于spawn:
// INCLUDES
import * as childProcess from 'child_process'; // ES6 Syntax
// DEFINES
let exec = childProcess.exec; // Use 'var' for more proper
// semantics, or 'const' it all
// if that's your thing; though 'let' is
// true-to-scope;
// Return an EventEmitter to work with, though
// you can also chain stdout too:
// (i.e. exec( ... ).stdout.on( ... ); )
let childProcess = exec
(
'./binary command -- --argument argumentValue',
( error, stdout, stderr ) =>
// When the process completes:
if( error )
console.log( `$error.name: $error.message` );
console.log( `[STACK] $error.stack` );
console.log( stdout );
console.log( stderr );
callback(); // Gulp stuff
);
现在就像为stdout
注册一个事件处理程序一样简单:
childProcess.stdout.on( 'data', data => console.log( data ) );
对于stderr
:
childProcess.stderr.on( 'data', data => console.log( `[ERROR]: $data` ) );
你也可以pipe
stdout 到主进程的stdout:
childProcess.stdout.pipe( process.stdout );
一点也不差——HTH
【讨论】:
【参考方案4】:我仍然对 Node.js 感兴趣,但我有一些想法。首先,我相信你需要使用execFile
而不是spawn
; execFile
用于当您拥有脚本路径时,而spawn
用于执行 Node.js 可以针对您的系统路径解析的众所周知的命令。
1。 Provide a callback 处理缓冲输出:
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。为子进程的标准输出添加监听器stream (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());
);
此外,似乎有一些选项可以让您将生成的进程与 Node 的控制终端分离,这将允许它异步运行。我还没有对此进行测试,但API docs 中有一些示例如下:
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());
);
【讨论】:
如果您可以解释如何使用 exec() 执行此操作,则可以加分,因为我需要执行 shell cmd。 您可以将child.spawn()
与shell
选项设置为true
一起使用。 nodejs.org/api/…
您也可以使用child.stdout.pipe(process.stdout);
将 child.stdout 直接通过管道传递给 process.stdout
@DynamicDan javascript let childProcess = exec ( './script-to-run --arg1 arg1value', ( error, stdout, stderror ) => console.log( '[CALLBACK]: ' + error ); // or stdout or stderror ); // Same as with spawn: childProcess.stdout.on ( 'data', ( data ) => console.log( '[LIVE]: ' + data ); // Here's your live data! );
【参考方案5】:
类似 PHP 的通路
import spawn from 'child_process';
export default async function passthru(exe, args, options)
return new Promise((resolve, reject) =>
const env = Object.create(process.env);
const child = spawn(exe, args,
...options,
env:
...env,
...options.env,
,
);
child.stdout.setEncoding('utf8');
child.stderr.setEncoding('utf8');
child.stdout.on('data', data => console.log(data));
child.stderr.on('data', data => console.log(data));
child.on('error', error => reject(error));
child.on('close', exitCode =>
console.log('Exit code:', exitCode);
resolve(exitCode);
);
);
用法
const exitCode = await passthru('ls', ['-al'], cwd: '/var/www/html' )
【讨论】:
【参考方案6】:这是我发现的最干净的方法:
require("child_process").spawn('bash', ['./script.sh'],
cwd: process.cwd(),
detached: true,
stdio: "inherit"
);
【讨论】:
它到底在做什么?为什么它有效?为什么这是更清洁的方法?【参考方案7】:现在(6 年后)容易多了!
Spawn 返回一个 childObject,然后您可以使用它监听事件。这些事件是:
类:子进程 事件:“错误” 事件:“退出” 事件:“关闭” 事件:“断开连接” 事件:“消息”还有一堆来自childObject的对象,它们是:
类:子进程 child.stdin child.stdout child.stderr child.stdio child.pid child.connected child.kill([信号]) child.send(message[, sendHandle][, callback]) child.disconnect()在此处查看有关 childObject 的更多信息:https://nodejs.org/api/child_process.html
异步
如果您想在节点仍然能够继续执行的情况下在后台运行您的进程,请使用异步方法。您仍然可以选择在流程完成后以及流程有任何输出时执行操作(例如,如果您想将脚本的输出发送到客户端)。
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);
);
下面是如何使用回调 + 异步方法:
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);
);
使用上述方法,您可以将脚本的每一行输出发送到客户端(例如,当您在stdout
或stderr
上接收事件时,使用 Socket.io 发送每一行)。
同步
如果您希望节点停止正在执行的操作并等待脚本完成,您可以使用同步版本:
child_process.spawnSync(...);(节点 v0.11.12+)
这种方法的问题:
如果脚本需要一段时间才能完成,您的服务器将挂起 那段时间! stdout 只会在脚本执行一次后返回 已完成运行。因为是同步的,所以不能继续 直到当前行结束。因此无法捕获 'stdout' 事件,直到生成线完成。使用方法:
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);
【讨论】:
+1,现在应该选择这个作为正确答案。请注意,回调中的数据变量以 Buffer 对象的形式出现。如果你想输入 utf8 字符串,你可以使用child.stdout.setEncoding('utf8')
。
如果你需要来自stdout
的信息是异步的,那么这不起作用,也就是说,如果进程继续,则剩余的程序继续。
嘿@ChristianHujer !我更新了我的答案以包括异步和同步:D
如果你有一个脚本是:console.log("Output 1"); console.error("Boom"); console.log("Output 2");
而我正在做spawnAsync('node ./script.js')
...你如何保持输出的顺序?我的输出似乎总是以错误的顺序输出。
如果您使用 spawnSync 并希望值作为字符串而不是缓冲区返回,您需要输入 encoding: 'utf-8' 作为选项的一部分 (第三个参数)。【参考方案8】:
我发现自己经常需要这个功能,所以我将它打包到一个名为std-pour 的库中。它应该让您执行命令并实时查看输出。简单安装:
npm install std-pour
那么执行命令并实时查看输出就足够简单了:
const pour = require('std-pour');
pour('ping', ['8.8.8.8', '-c', '4']).then(code => console.log(`Error Code: $code`));
它是基于承诺的,因此您可以链接多个命令。它甚至与 child_process.spawn
的功能签名兼容,因此它应该是您使用它的任何地方的替代品。
【讨论】:
@KodieGrantham 很高兴它对你有用!你们似乎都在做一些很酷的工作,所以我希望它能让你们继续前进。【参考方案9】:当我在子进程中生成 npm 时,我在从“npm install”命令获取日志输出时遇到了一点麻烦。依赖项的实时日志记录未显示在父控制台中。
执行原始发布者想要的最简单的方法似乎是这样(在 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');
);
【讨论】:
【参考方案10】:孩子:
setInterval(function()
process.stdout.write("hi");
, 1000); // or however else you want to run a timer
父母:
require('child_process').fork('./childfile.js');
// fork'd children use the parent's stdio
【讨论】:
以上是关于Node.js 生成子进程并实时获取终端输出的主要内容,如果未能解决你的问题,请参考以下文章