144

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

4

9 回答 9

173

现在(6 年后)容易多了!

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

  • 类:子进程
    • 事件:'错误'
    • 事件:“退出”
    • 事件:'关闭'
    • 事件:“断开连接”
    • 事件:'消息'

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

  • 类:子进程
    • 子标准输入
    • child.stdout
    • child.stderr
    • 孩子.stdio
    • 孩子.pid
    • 子连接
    • 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);
    });
}

使用上述方法,您可以将脚本的每一行输出发送到客户端(例如,当您在stdoutor上接收事件时使用 Socket.io 发送每一行stderr)。

同步

如果您希望节点停止它正在做的事情并等待脚本完成,您可以使用同步版本:

child_process.spawnSync(...); (节点 v0.11.12+)

这种方法的问题:

  • 如果脚本需要一段时间才能完成,您的服务器将在这段时间内挂起!
  • 只有脚本完成运行后才会返回标准输出。因为它是同步的,所以在当前行完成之前无法继续。因此,在生成线完成之前,它无法捕获“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);
于 2015-09-30T17:57:44.057 回答
113

我仍然对 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. 为子进程的标准输出添加一个监听器(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()); 
});
于 2013-03-08T03:45:13.510 回答
45

这是我发现的最干净的方法:

require("child_process").spawn('bash', ['./script.sh'], {
  cwd: process.cwd(),
  detached: true,
  stdio: "inherit"
});
于 2017-11-16T20:26:26.237 回答
18

当我在子进程中生成 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');
});
于 2016-02-05T02:05:50.677 回答
6

类似 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' })
于 2020-07-27T23:12:09.687 回答
4

孩子:

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
于 2013-08-13T02:14:38.190 回答
4

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

于 2017-06-17T18:20:38.640 回答
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标准输出到主进程的标准输出:

childProcess.stdout.pipe( process.stdout );

一点也不差 - HTH

于 2018-06-08T14:45:00.930 回答
0

当我生成 Python 3 脚本时,我遇到了上述情况都不起作用的情况。我会从标准输出获取数据,但只有在孩子终止后。

事实证明,Python 默认缓冲标准输出。可以通过包含-u作为 python3 的命令行参数来禁用标准输出缓冲。

于 2021-09-22T20:45:42.203 回答