Node.js产生子进程并实时输出终端


113

我有一个脚本,输出“ 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不支持线程,那么有什么解决方案吗?我的想法是可能运行两个实例,第一个实例用于创建任务的特定目的,并考虑到可以实现的目的,将其通过管道将输出传递给第二个实例的进程。


1
如果编写了子进程,python则不要忘记传递其-u标志以不缓冲控制台输出,否则看起来脚本不是实时的 stackoverflow.com/a/49947671/906265
Aivaras

使用npmjs.com/package/cross-spawn而不是其他任何东西。好多了
安德鲁·科斯特

Answers:


92

我仍然对Node.js感兴趣,但是我有一些想法。首先,我相信您需要使用execFile而不是spawn; execFile用于您具有脚本路径的路径,而spawn用于执行Node.js可以针对您的系统路径解析的众所周知的命令。

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.将侦听器添加到子进程的stdout 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文档中有一些例子如下:

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

8
如果您可以解释如何使用exec()执行此操作(我需要执行Shell cmd),则可以加分。
DynamicDan 2014年

2
您可以child.spawn()shell选项设置为使用truenodejs.org/api/…–
CedX

5
您还可以使用child.stdout.pipe(process.stdout);
darkadept '16

@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! } );
瑞克(Rik)

130

现在(6年后)要容易得多!

Spawn返回childObject,然后您可以使用此监听事件。这些事件是:

  • 类:ChildProcess
    • 事件:“错误”
    • 事件:“退出”
    • 事件:“关闭”
    • 事件:“断开连接”
    • 事件:“消息”

还有来自childObject的一堆对象,它们是:

  • 类:ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • 小孩
    • child.pid
    • 与孩子有关
    • child.kill([signal])
    • 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或上收到事件时,使用Socket.io发送每一行stderr)。

同步

如果您希望节点停止正在执行的操作并等待脚本完成,则可以使用同步版本:

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

11
+1,现在应该选择这作为正确答案。请注意,回调中的数据变量作为Buffer对象进入。child.stdout.setEncoding('utf8')如果要输入utf8字符串,则可以使用
。– Ashish

2
如果您需要stdout异步获取信息,即剩余的程序继续运行,而该过程仍在继续运行,则此方法将无效。
Christian Hujer

2
嘿@ChristianHujer!我更新了答案,将异步和同步都包括在内:D
凯蒂(Katie)

如果您有一个脚本是:console.log("Output 1"); console.error("Boom"); console.log("Output 2");并且我正在做spawnAsync('node ./script.js')...如何保存输出的顺序?我的输出似乎总是以错误的顺序输出。
Bryan Ray

仅供参考,如果您使用pipepipeline或将适当的选项传递给,则更加容易spawn
RichS

25

这是我找到的最干净的方法:

require("child_process").spawn('bash', ['./script.sh'], {
  cwd: process.cwd(),
  detached: true,
  stdio: "inherit"
});

15
到底在做什么?为什么行得通?为什么这是一种更清洁的方法?
葡萄收获期

16

在子进程中生成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');
});

3

我发现自己经常需要此功能,因此我将其打包到一个名为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因此在您使用它的任何地方都应该是替换品。


1
@KodieGrantham很高兴为您工作!大家似乎都在做一些很酷的工作,所以我希望它能使您保持运行状态。
Joel B

1

儿童:

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

1

类似于PHP的passthru

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' })

0

添加与相关的答案 child_process.exec因为我也需要实时反馈,直到脚本完成后才得到任何。这也补充了我对已接受答案的评论,但由于它的格式设置,它会更易于理解和阅读。

基本上,我有一个调用Gulp的npm脚本,调用了一个随后使用的任务 child_process.exec根据操作系统执行bash或批处理脚本。两种脚本都可以通过Gulp运行构建过程,然后对与Gulp输出配合使用的某些二进制文件进行一些调用。

它与其他(生成等)完全一样,但是为了完整起见,以下是操作方法:

// 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
    }
);

现在,它与添加事件侦听器一样简单。对于stdout

childProcess.stdout.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live':
        console.log( '[STDOUT]: ' + data );
    }
);

对于stderr

childProcess.stderr.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live' too:
        console.log( '[STDERR]: ' + data );
    }
);

一点也不差-HTH

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.