[英]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
});
In this case there is already an execSync
function: 在这种情况下,已经有一个
execSync
函数:
child_process.execSync(command[, options]) child_process.execSync(命令[,options])
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.