使用 Node.js 执行命令行二进制文件

IT技术 javascript ruby node.js command-line-interface
2021-01-20 10:28:41

我正在将 CLI 库从 Ruby 移植到 Node.js。在我的代码中,我会在必要时执行几个第三方二进制文件。我不确定如何最好地在 Node.js 中实现这一点。

这是我在 Ruby 中调用 PrinceXML 将文件转换为 PDF 的示例:

cmd = system("prince -v builds/pdf/book.html -o builds/pdf/book.pdf")

Node 中的等效代码是什么?

6个回答

对于更新版本的 Node.js (v8.1.4),事件和调用与旧版本相似或相同,但鼓励使用标准的更新语言功能。例子:

对于缓冲的、非流格式的输出(您一次获得所有内容),请使用child_process.exec

const { exec } = require('child_process');
exec('cat *.js bad_file | wc -l', (err, stdout, stderr) => {
  if (err) {
    // node couldn't execute the command
    return;
  }

  // the *entire* stdout and stderr (buffered)
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

您还可以将其与 Promise 一起使用:

const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function ls() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.log('stderr:', stderr);
}
ls();

如果您希望以块的形式逐渐接收数据(作为流输出),请使用child_process.spawn

const { spawn } = require('child_process');
const child = spawn('ls', ['-lh', '/usr']);

// use child.stdout.setEncoding('utf8'); if you want text chunks
child.stdout.on('data', (chunk) => {
  // data from standard output is here as buffers
});

// since these are streams, you can pipe them elsewhere
child.stderr.pipe(dest);

child.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

这两个函数都有一个同步对应物。一个例子child_process.execSync

const { execSync } = require('child_process');
// stderr is sent to stderr of parent process
// you can set options.stdio if you want it to go elsewhere
let stdout = execSync('ls');

以及child_process.spawnSync

const { spawnSync} = require('child_process');
const child = spawnSync('ls', ['-lh', '/usr']);

console.log('error', child.error);
console.log('stdout ', child.stdout);
console.log('stderr ', child.stderr);

注意:以下代码仍然有效,但主要针对 ES5 及之前版本的用户。

使用 Node.js 生成子进程的module在文档(v5.0.0)中有详细记录要执行命令并将其完整输出作为缓冲区获取,请使用child_process.exec

var exec = require('child_process').exec;
var cmd = 'prince -v builds/pdf/book.html -o builds/pdf/book.pdf';

exec(cmd, function(error, stdout, stderr) {
  // command output is in stdout
});

如果您需要对流使用处理进程 I/O,例如当您需要大量输出时,请使用child_process.spawn

var spawn = require('child_process').spawn;
var child = spawn('prince', [
  '-v', 'builds/pdf/book.html',
  '-o', 'builds/pdf/book.pdf'
]);

child.stdout.on('data', function(chunk) {
  // output will be here in chunks
});

// or if you want to send output elsewhere
child.stdout.pipe(dest);

如果您正在执行文件而不是命令,您可能想要使用child_process.execFile,这些参数几乎与 相同spawn,但有第四个回调参数,例如exec用于检索输出缓冲区。这可能看起来有点像这样:

var execFile = require('child_process').execFile;
execFile(file, args, options, function(error, stdout, stderr) {
  // command output is in stdout
});

v0.11.12 开始,Node 现在支持同步spawnexec. 上面描述的所有方法都是异步的,并且有一个同步对应的方法。可以在此处找到它们的文档虽然它们对脚本很有用,但请注意,与用于异步生成子进程的方法不同,同步方法不返回ChildProcess.

如果我不想将所有内容都放入一个文件中,但又想执行多个命令怎么办?也许喜欢echo "hello"echo "world"
2021-03-14 10:28:41
而不是child.pipe(dest)(不存在),您必须使用child.stdout.pipe(dest)and child.stderr.pipe(dest),例如child.stdout.pipe(process.stdout)and child.stderr.pipe(process.stderr)
2021-03-19 10:28:41
这是执行此操作的标准方法吗?我的意思是所有的包装器都是如何用 nodejs 编写的?我的意思是让我们说对于需要运行命令的 gearman、rabbitmq 等,但他们也有一些包装器,但我在他们的库代码中找不到任何这些代码
2021-03-19 10:28:41
谢谢你。这让我发疯。有时,仅指出明显的解决方案会有所帮助,因此我们的菜鸟(对节点)可以学习并运行它。
2021-04-06 10:28:41
注意: require('child_process').execFile() 对需要运行文件而不是像 Prince 这样的系统范围内已知命令的人会感兴趣。
2021-04-07 10:28:41

Node JS v15.8.0、LTSv14.15.4v12.20.1 --- 2021 年 2 月

异步方法(Unix):

'use strict';

const { spawn } = require( 'child_process' );
const ls = spawn( 'ls', [ '-lh', '/usr' ] );

ls.stdout.on( 'data', ( data ) => {
    console.log( `stdout: ${ data }` );
} );

ls.stderr.on( 'data', ( data ) => {
    console.log( `stderr: ${ data }` );
} );

ls.on( 'close', ( code ) => {
    console.log( `child process exited with code ${ code }` );
} );

异步方法(Windows):

'use strict';

const { spawn } = require( 'child_process' );
// NOTE: Windows Users, this command appears to be differ for a few users.
// You can think of this as using Node to execute things in your Command Prompt.
// If `cmd` works there, it should work here.
// If you have an issue, try `dir`:
// const dir = spawn( 'dir', [ '.' ] );
const dir = spawn( 'cmd', [ '/c', 'dir' ] );

dir.stdout.on( 'data', ( data ) => console.log( `stdout: ${ data }` ) );
dir.stderr.on( 'data', ( data ) => console.log( `stderr: ${ data }` ) );
dir.on( 'close', ( code ) => console.log( `child process exited with code ${code}` ) );

同步:

'use strict';

const { spawnSync } = require( 'child_process' );
const ls = spawnSync( 'ls', [ '-lh', '/usr' ] );

console.log( `stderr: ${ ls.stderr.toString() }` );
console.log( `stdout: ${ ls.stdout.toString() }` );

来自Node.js v15.8.0 文档

这同样适用于Node.js的v14.15.4文档Node.js的v12.20.1文档

感谢您提供正确和简单的版本。稍微简单的同步版本非常适合我需要的“做某事然后扔掉”脚本。
2021-03-14 10:28:41
没问题!即使根据某些人的说法“不合适”,也总是很高兴同时拥有两者。
2021-03-25 10:28:41
可能值得指出的是,为了在 Windows 中执行此示例,必须使用'cmd', ['/c', 'dir']. 至少'dir'在我想起这个之前,我只是在高低搜索为什么没有参数不起作用......;)
2021-04-04 10:28:41
@Tyguy7 你如何运行它?你对控制台对象有任何覆盖吗?
2021-04-04 10:28:41
这些都没有向控制台输出任何内容。
2021-04-06 10:28:41

您正在寻找child_process.exec

这是示例:

const exec = require('child_process').exec;
const child = exec('cat *.js bad_file | wc -l',
    (error, stdout, stderr) => {
        console.log(`stdout: ${stdout}`);
        console.log(`stderr: ${stderr}`);
        if (error !== null) {
            console.log(`exec error: ${error}`);
        }
});
@hgoebl,那有什么选择呢?
2021-03-11 10:28:41
@Harshdeep 如果标准输出很长(例如几个 MB),您可以data在标准输出上收听事件。查看文档,但它必须类似于childProc.stdout.on("data", fn).
2021-03-20 10:28:41
这是对的。但请注意,这种调用子进程对 stdout 的长度有限制。
2021-04-09 10:28:41

从第 4 版开始,最接近的替代child_process.execSync 方法方法

const {execSync} = require('child_process');

let output = execSync('prince -v builds/pdf/book.html -o builds/pdf/book.pdf');

⚠️ 注意execSynccall 会阻塞事件循环。

这在最新的节点上效果很好。child_process使用时是否正在创建execSync它会在命令后立即删除,对吗?所以没有内存泄漏?
2021-03-11 10:28:41
是的,没有内存泄漏。我猜它只初始化 libuv 子进程结构,根本没有在节点中创建它。
2021-03-19 10:28:41
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})
Al 说的是对的,但我会说这个答案的好处是,对于需要快速响应的人来说,它比通读最佳答案要简单得多。
2021-03-24 10:28:41
请添加有关此代码如何工作以及如何解决答案的更多解释。请记住,StackOverflow 正在为将来阅读本文的人们建立一个答案档案。
2021-04-07 10:28:41