【问题标题】:How can I execute shell commands in sequence?如何按顺序执行 shell 命令?
【发布时间】:2016-04-30 19:27:42
【问题描述】:

我有一个我想用 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);
    });
}

当我运行它时,命令以相反的顺序执行。为什么会这样?如何按顺序执行命令?

更好的是,有没有办法在不使用 nodejs 的情况下执行 shell 命令?我发现它对 shell 的异步处理有点麻烦。

注意:

我知道存在像 shelljs 这样的库。我正在尝试仅使用基本 nodejs 来执行此操作。

【问题讨论】:

  • 由于exec 是异步方法,因此 echo 命令执行速度可能更快。这是一个提示:您需要重新考虑如何设置执行。
  • 它们不是以相反的顺序执行的,它们是异步的,一个不等待另一个。 Shell 命令由 shell 执行,而exec 只是一个访问它的接口。您可以在终端中运行任何此命令

标签: node.js shell


【解决方案1】:

您的for 循环正在同时并行执行所有异步操作,因为exec() 是非阻塞的。他们将完成的顺序取决于他们的执行时间,并且不是确定的。如果你真的希望它们被排序,那么你必须执行一个,等待它调用它的完成回调,然后执行下一个。

您不能使用传统的for 循环“等待”异步操作以在 Javascript 中完成,以便按顺序执行它们。相反,您必须手动进行迭代,在前一个迭代的完成回调中启动下一个迭代。我通常的做法是使用一个计数器和一个名为 next() 的本地函数,如下所示:

手动异步迭代

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 中已经标准化并且现在内置到 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 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
});

【讨论】:

    【解决方案2】:

    选项 1:使用函数的“...同步”版本(如果存在)

    在这种情况下,已经有一个execSync 函数:

    child_process.execSync(command[, options])

    选项 2:发电机魔法!

    为了更通用的目的,现在你可以使用例如这个“生成器”模式可以“去同步”其中的任何异步函数,对于任何顺序操作系统脚本都非常有用。

    这里有一个示例,说明如何在 node.js v6+(我认为也是 v4+)中以同步方式使用 readline async 函数

    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'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-23
      • 2010-09-26
      • 1970-01-01
      • 2015-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多