Ejecutar un binario de línea de comando con Node.js
Estoy en el proceso de trasladar una biblioteca CLI de Ruby a Node.js. En mi código ejecuto varios binarios de terceros cuando es necesario. No estoy seguro de cuál es la mejor manera de lograr esto en Node.
Aquí hay un ejemplo en Ruby donde llamo a PrinceXML para convertir un archivo a PDF:
cmd = system("prince -v builds/pdf/book.html -o builds/pdf/book.pdf")
¿Cuál es el código equivalente en Node?
Para una versión aún más nueva de Node.js (v8.1.4), los eventos y las llamadas son similares o idénticos a las versiones anteriores, pero se recomienda utilizar las funciones estándar del lenguaje más nuevo. Ejemplos:
Para una salida almacenada en búfer y sin formato de transmisión (la obtiene toda a la vez), use 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}`);
});
También puedes usarlo con Promesas:
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();
Si desea recibir los datos gradualmente en fragmentos (salida como una secuencia), utilice 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}`);
});
Ambas funciones tienen una contraparte síncrona. Un ejemplo para 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');
Así como 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);
Nota: El siguiente código sigue siendo funcional, pero está dirigido principalmente a usuarios de ES5 y anteriores.
El módulo para generar procesos secundarios con Node.js está bien documentado en la documentación (v5.0.0). Para ejecutar un comando y obtener su salida completa como un búfer, use 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
});
Si necesita manejar E/S de proceso con transmisiones, como cuando espera grandes cantidades de salida, use 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);
Si está ejecutando un archivo en lugar de un comando, es posible que desee utilizar child_process.execFile
, cuyos parámetros son casi idénticos a spawn
, pero tiene un cuarto parámetro de devolución de llamada como exec
para recuperar buffers de salida. Podría parecerse un poco a esto:
var execFile = require('child_process').execFile;
execFile(file, args, options, function(error, stdout, stderr) {
// command output is in stdout
});
A partir de v0.11.12 , Node ahora admite sincronización spawn
y exec
. Todos los métodos descritos anteriormente son asíncronos y tienen una contraparte síncrona. La documentación para ellos se puede encontrar aquí . Si bien son útiles para secuencias de comandos, tenga en cuenta que, a diferencia de los métodos utilizados para generar procesos secundarios de forma asincrónica, los métodos sincrónicos no devuelven una instancia de ChildProcess
.
Nodo JS v15.8.0
, LTS v14.15.4
y v12.20.1
--- febrero de 2021
Método asíncrono (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 }` );
} );
Método asíncrono (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}` ) );
Sincronización:
'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() }` );
De la documentación de Node.js v15.8.0
Lo mismo ocurre con la documentación de Node.js v14.15.4 y la documentación de Node.js v12.20.1.
Estás buscando child_process.exec
Aquí está el ejemplo:
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}`);
}
});