Node.js 用于异步 Linux 脚本
Node.js for Asynchronous Linux Scripting
在我编写的其他程序中,我喜欢 node.js 使用 promises 的异步方面。
我想将相同的编程风格(使用 node.js)用于 Linux 脚本。换句话说,我希望能够同时执行多个 Linux 命令,然后在这些命令完成后,我希望 node.js 脚本异步执行另一组命令,依此类推(无阻塞)。
我遇到了 an aritlce,它展示了如何使用 node.js 执行同步 Linux 命令,但我还没有找到涵盖多个异步 [=] 管理的类似教程=27=] 命令使用 node.js.
目前可以吗?如果是这样,你能指导我一些特定的资源来帮助我开始实现这个目标吗?
在核心节点库中,同步函数在函数名称后附加了 Sync
。他们使用 child_process.execFileSync, so you should be looking for child_process.execFile 作为函数的异步版本。
我不确定我是否正确,但我认为您正在寻找 exec
和 spawn
。请参阅related API documentation。文档中有这两个命令的示例。
exec
和 spawn
exec
是 spawn
的 "simple" 版本。前者使用单个回调在命令完成时向用户报告,并且仅在完成时 finished/failed.
child = exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
所以基本上只调用提供的回调,当 写入 stdout/stderr 的所有内容 可用时,完全。在流程终止(成功或失败)之后,只有回调被调用,用户(您)才能对其采取行动。如果失败 error
是 truthy.
spawn
不同,因为你可以在 stdout/stderr events 上监听。首先你"spawn"一个进程。 spawn
函数 return 是对子进程的引用。
var spawn = require('child_process').spawn;
var ls = spawn('ls', ['-lh', '/usr']);
ls
这是您生成的子进程。它有两个属性(现在很重要),stdout
和 stderr
是事件发射器。他们正在发出 data
事件。当它被写在任何一个流上时,在 data
事件上注册的回调被调用。
ls.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
ls.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
当然还有其他重要事件(当然要查看文档以获得最新的相关信息)。
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
当你想在 stdout 上捕获内容时,你可以使用 spawn
,例如当进程是 运行 时。一个很好的例子是,如果您正在生成一个需要几分钟才能完成的 ffmpeg 编码任务。您可以监听 stderr(因为 ffmpeg 将进度信息写入 stderr 而不是 stdout)以解析 "progress" 信息。
逐行使用 carrier
有一个很好的附加库可以与 spawn
一起使用。 It's called carrier。它有助于从派生进程的 stdout/stderr 中读取 "lines"。这很有用,因为传递给回调的 data
参数不一定包含由 \n
分隔的 "complete" 行。 carrier
对此有所帮助。 (但是,它不会帮助你捕获ffmpeg在stderr上的进度,因为在这种情况下ffmpeg没有写换行符,它只是一个回车符return,该行基本上总是重写。)
你会像这样使用它
var carry = require("carrier").carry;
var child = spawn("command");
carry(child.stdout, function(line) {
console.log("stdout", line);
});
Promises 和 Deferreds
如果你想使用 promise/deferred 风格的方法,那么你可以做类似下面 using Q
的事情,它被 AngularJS 使用 - 或者至少是 非常 相似(有关承诺的完整教程,请参阅 link)。
spawn
return 是一个 Emitter
对象,它不是一个承诺。所以你必须包装对 spawn (see Using Deferreds).
的调用
var q = require("q");
var spawn = require("child_process").spawn;
var ls = function() {
var deferred = q.defer();
var ls = spawn("ls", ["-lh", "/usr"]);
ls.stdout.on("data", function(data) {
deferred.notify({stdout: true, data: data});
});
ls.stderr.on("data", function(data) {
deferred.notify({stderr: true, data: data});
});
ls.on("close", function(code) {
if (code === 0) {
deferred.resolve();
} else {
deferred.reject(code);
}
});
return deferred.promise;
};
现在通过执行 ls()
一个 promise 是 returned,您可以像使用任何其他 promise 一样使用它。当它完全解决时,将调用第一个回调。如果发生错误(进程存在非零退出代码),则调用错误处理程序。当命令进行时,将调用第三个回调(通知回调)。
ls().then(function() {
console.log("child process exited successfully");
}, function(err) {
console.log("child process exited with code " + err);
}, function(args) {
if (args.stdout) {
console.log("stdout: " + args.data);
} else {
console.log("stderr: " + args.data);
}
});
当某些内容写入 stderr 时,您可以立即调用 reject,但这是设计决定。回到 ffmpeg 示例,这对您没有任何好处,因为 ffmpeg 将一般信息吐出到 stderr。但是它可以与其他命令一起使用。
我想你会明白的:)
例子取自nodejs的文档,因为很好理解
我推荐一种结合生成器和承诺的方法。先决条件如下:
- 带有
--harmony
标志的节点 0.11 或更高版本,或 io.js 的任何版本(不需要标志)。
- 一个生成器处理程序。这是我最喜欢的:https://www.npmjs.com/package/co
- 一种将 Node 的基于回调的方法转换为承诺返回函数的方法。我用这个:https://www.npmjs.com/package/ugly-adapter
然后做你想做的事:
var co = require('co');
var adapt = require('ugly-adapter');
var childProcessExec = require('child_process').exec;
var exec = adapt.part(childProcessExec);
co(function*() {
// run commands in parallel by yielding
// a single object or array
var result = yield {
listing: exec('ls -l'),
mkdirResult: exec('mkdir foo'),
blah: exec('echo "blah blah"'),
};
console.log(result.blah); // "blah blah"
// run commands in series by yielding
// one thing at a time
var listing = yield exec('ls -l');
var mkdirResult = yield exec('mkdir foo2');
var blah = yield exec('echo "blah blah"');
console.log(blah); // "blah blah"
}).catch(function(err){
// this handles any possible errors
// thrown by the above
console.error(err.stack);
process.exit(1);
});
yield
关键字导致函数暂停,而 co
解开 promise 并将结果发送回您的函数。
注意: 库 co
实际上只是 async/await 的临时替代品,它在 es7 中出现并且工作方式基本相同。
在我编写的其他程序中,我喜欢 node.js 使用 promises 的异步方面。
我想将相同的编程风格(使用 node.js)用于 Linux 脚本。换句话说,我希望能够同时执行多个 Linux 命令,然后在这些命令完成后,我希望 node.js 脚本异步执行另一组命令,依此类推(无阻塞)。
我遇到了 an aritlce,它展示了如何使用 node.js 执行同步 Linux 命令,但我还没有找到涵盖多个异步 [=] 管理的类似教程=27=] 命令使用 node.js.
目前可以吗?如果是这样,你能指导我一些特定的资源来帮助我开始实现这个目标吗?
在核心节点库中,同步函数在函数名称后附加了 Sync
。他们使用 child_process.execFileSync, so you should be looking for child_process.execFile 作为函数的异步版本。
我不确定我是否正确,但我认为您正在寻找 exec
和 spawn
。请参阅related API documentation。文档中有这两个命令的示例。
exec
和 spawn
exec
是 spawn
的 "simple" 版本。前者使用单个回调在命令完成时向用户报告,并且仅在完成时 finished/failed.
child = exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
所以基本上只调用提供的回调,当 写入 stdout/stderr 的所有内容 可用时,完全。在流程终止(成功或失败)之后,只有回调被调用,用户(您)才能对其采取行动。如果失败 error
是 truthy.
spawn
不同,因为你可以在 stdout/stderr events 上监听。首先你"spawn"一个进程。 spawn
函数 return 是对子进程的引用。
var spawn = require('child_process').spawn;
var ls = spawn('ls', ['-lh', '/usr']);
ls
这是您生成的子进程。它有两个属性(现在很重要),stdout
和 stderr
是事件发射器。他们正在发出 data
事件。当它被写在任何一个流上时,在 data
事件上注册的回调被调用。
ls.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
ls.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
当然还有其他重要事件(当然要查看文档以获得最新的相关信息)。
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
当你想在 stdout 上捕获内容时,你可以使用 spawn
,例如当进程是 运行 时。一个很好的例子是,如果您正在生成一个需要几分钟才能完成的 ffmpeg 编码任务。您可以监听 stderr(因为 ffmpeg 将进度信息写入 stderr 而不是 stdout)以解析 "progress" 信息。
逐行使用 carrier
有一个很好的附加库可以与 spawn
一起使用。 It's called carrier。它有助于从派生进程的 stdout/stderr 中读取 "lines"。这很有用,因为传递给回调的 data
参数不一定包含由 \n
分隔的 "complete" 行。 carrier
对此有所帮助。 (但是,它不会帮助你捕获ffmpeg在stderr上的进度,因为在这种情况下ffmpeg没有写换行符,它只是一个回车符return,该行基本上总是重写。)
你会像这样使用它
var carry = require("carrier").carry;
var child = spawn("command");
carry(child.stdout, function(line) {
console.log("stdout", line);
});
Promises 和 Deferreds
如果你想使用 promise/deferred 风格的方法,那么你可以做类似下面 using Q
的事情,它被 AngularJS 使用 - 或者至少是 非常 相似(有关承诺的完整教程,请参阅 link)。
spawn
return 是一个 Emitter
对象,它不是一个承诺。所以你必须包装对 spawn (see Using Deferreds).
var q = require("q");
var spawn = require("child_process").spawn;
var ls = function() {
var deferred = q.defer();
var ls = spawn("ls", ["-lh", "/usr"]);
ls.stdout.on("data", function(data) {
deferred.notify({stdout: true, data: data});
});
ls.stderr.on("data", function(data) {
deferred.notify({stderr: true, data: data});
});
ls.on("close", function(code) {
if (code === 0) {
deferred.resolve();
} else {
deferred.reject(code);
}
});
return deferred.promise;
};
现在通过执行 ls()
一个 promise 是 returned,您可以像使用任何其他 promise 一样使用它。当它完全解决时,将调用第一个回调。如果发生错误(进程存在非零退出代码),则调用错误处理程序。当命令进行时,将调用第三个回调(通知回调)。
ls().then(function() {
console.log("child process exited successfully");
}, function(err) {
console.log("child process exited with code " + err);
}, function(args) {
if (args.stdout) {
console.log("stdout: " + args.data);
} else {
console.log("stderr: " + args.data);
}
});
当某些内容写入 stderr 时,您可以立即调用 reject,但这是设计决定。回到 ffmpeg 示例,这对您没有任何好处,因为 ffmpeg 将一般信息吐出到 stderr。但是它可以与其他命令一起使用。
我想你会明白的:)
例子取自nodejs的文档,因为很好理解
我推荐一种结合生成器和承诺的方法。先决条件如下:
- 带有
--harmony
标志的节点 0.11 或更高版本,或 io.js 的任何版本(不需要标志)。 - 一个生成器处理程序。这是我最喜欢的:https://www.npmjs.com/package/co
- 一种将 Node 的基于回调的方法转换为承诺返回函数的方法。我用这个:https://www.npmjs.com/package/ugly-adapter
然后做你想做的事:
var co = require('co');
var adapt = require('ugly-adapter');
var childProcessExec = require('child_process').exec;
var exec = adapt.part(childProcessExec);
co(function*() {
// run commands in parallel by yielding
// a single object or array
var result = yield {
listing: exec('ls -l'),
mkdirResult: exec('mkdir foo'),
blah: exec('echo "blah blah"'),
};
console.log(result.blah); // "blah blah"
// run commands in series by yielding
// one thing at a time
var listing = yield exec('ls -l');
var mkdirResult = yield exec('mkdir foo2');
var blah = yield exec('echo "blah blah"');
console.log(blah); // "blah blah"
}).catch(function(err){
// this handles any possible errors
// thrown by the above
console.error(err.stack);
process.exit(1);
});
yield
关键字导致函数暂停,而 co
解开 promise 并将结果发送回您的函数。
注意: 库 co
实际上只是 async/await 的临时替代品,它在 es7 中出现并且工作方式基本相同。