简体   繁体   English

如何按顺序执行shell命令?

[英]How can I execute shell commands in sequence?

I have a list of shell commands I want to execute with nodejs: 我有一个我想用nodejs执行的shell命令列表:

// index.js
var commands = ["npm install", "echo 'hello'"];

var exec = require('child_process').exec;

for (var i = 0; i < commands.length; i++) {
    exec(commands[i], function(err, stdout) {
        console.log(stdout);
    });
}

When I run this, the commands are executed in the reverse order. 当我运行它时,命令以相反的顺序执行。 Why is this happening? 为什么会这样? How do i execute the commands in sequence? 我如何按顺序执行命令?

Better yet, is there a way to execute shell commands without using nodejs ? 更好的是,有没有办法在不使用nodejs的情况下执行shell命令? I find its async handling of the shell a little cumbersome. 我发现它对shell的异步处理有点麻烦。

NOTE: 注意:

I know that libraries like shelljs exist. 我知道像shelljs这样的shelljs在。 I'm trying to do this with base nodejs only. 我正在尝试仅使用基本nodejs。

Your for loop is executing all your asynchronous operation in parallel at once because exec() is non-blocking. 你的for循环正在同时并行执行所有异步操作,因为exec()是非阻塞的。 The order they will complete depends upon their execution time and will not be determinate. 他们将完成的顺序取决于他们的执行时间,并且不会确定。 If you truly want them to be sequenced, then you have to execute one, wait for it to call it's completion callback and then execute the next one. 如果你真的希望它们被排序,那么你必须执行一个,等待它调用它的完成回调,然后执行下一个。

You can't use a traditional for loop to "wait" on an asynchronous operation to complete in Javascript in order to execute them sequentially. 您不能使用传统的for循环来“等待”异步操作以在Javascript中完成以便按顺序执行它们。 Instead, you have to make the iteration manually where you kick off the next iteration in the completion callback of the previous one. 相反,您必须手动进行迭代,然后在前一个完成回调中启动下一次迭代。 My usual way of doing that is with a counter and a local function called next() like this: 我通常的做法是使用一个计数器和一个名为next()的本地函数,如下所示:

Manual Async Iteration 手动异步迭代

var commands = ["npm install", "echo 'hello'"];

var exec = require('child_process').exec;

function runCommands(array, callback) {

    var index = 0;
    var results = [];

    function next() {
       if (index < array.length) {
           exec(array[index++], function(err, stdout) {
               if (err) return callback(err);
               // do the next iteration
               results.push(stdout);
               next();
           });
       } else {
           // all done here
           callback(null, results);
       }
    }
    // start the first iteration
    next();
}

runCommands(commands, function(err, results) {
    // error or results here
});

ES6 Promises ES6承诺

Since promises have been standardized in ES6 and are built into node.js now, I like to use Promises for my async operations: 由于promises已在ES6中标准化并且现在已内置到node.js中,因此我喜欢使用Promises进行异步操作:

var exec = require('child_process').exec;

function execPromise = function(cmd) {
    return new Promise(function(resolve, reject) {
        exec(cmd, function(err, stdout) {
            if (err) return reject(err);
            resolve(stdout);
        });
    });
}

var commands = ["npm install", "echo 'hello'"];

commands.reduce(function(p, cmd) {
    return p.then(function(results) {
        return execPromise(cmd).then(function(stdout) {
            results.push(stdout);
            return results;
        });
    });
}, Promise.resolve([])).then(function(results) {
    // all done here, all results in the results array
}, function(err) {
    // error here
});

Bluebird Promises 蓝鸟承诺

Using the Bluebird promise library, this would be even simpler: 使用Bluebird promise库,这将更简单:

var Promise = require('bluebird');
var execP = Promise.promisify(require('child_process').exec);

var commands = ["npm install", "echo 'hello'"];
Promise.mapSeries(commands, execP).then(function(results) {
    // all results here
}, function(err) {
    // error here
});

Opt.1: Use the '...Sync' version of the function if it exists 选项1:使用函数的“...同步”版本(如果存在)

In this case there is already an execSync function: 在这种情况下,已经有一个execSync函数:

child_process.execSync(command[, options]) child_process.execSync(命令[,options])

Opt.2: Generators magic! 选项2:发电机魔术!

For a more general purpose, nowadays you could use eg this 'generator' pattern to 'deasync' any async function inside them , very useful for any sequential OS script. 为了更通用的目的,现在您可以使用例如这个“生成器”模式来“解除”其中的任何异步函数 ,这对于任何顺序OS脚本都非常有用。

Here an example of how to use readline async function in a sync fashion in node.js v6+ (I think also v4+) 这里有一个如何在node.js v6 +中以同步方式使用readline异步函数的例子(我想也是v4 +)

var main = (function* () {
  var rl = require('readline')
          .createInterface({input: process.stdin, output: process.stdout });
  // the callback uses the iterator '.next()' to resume the 'yield'
  a = yield rl.question('do you want this? ', r=>main.next(r))  
  b = yield rl.question('are you sure? ', r=>main.next(r))      
  rl.close()
  console.log(a,b)
})()          // <- generator executed, iterator 'main' created
main.next()   // <- start iterator, run till the first 'yield'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM