Exécuter une ligne de commande binaire avec Node.js

Je suis en train de transférer une bibliothèque CLI de Ruby sur Node.js. Dans mon code, j'exécute plusieurs binaires tiers si nécessaire. Je ne suis pas sûr de la meilleure façon d'accomplir cela dans Node.

Voici un exemple dans Ruby où j'appelle PrinceXML pour convertir un fichier en PDF:

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

Quel est le code équivalent dans Node?

Pour une version encore plus récente de Node.js (v8.1.4), les événements et les appels sont similaires ou identiques aux anciennes versions, mais il est encouragé à utiliser les fonctionnalités de langue standard plus récentes. Exemples:

Pour la sortie formatée non-mise en forme (vous l'obtenez tout à la fois), utilisez 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}`); }); 

Vous pouvez également l'utiliser avec Promises:

 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 vous souhaitez recevoir les données progressivement en morceaux (sortie en flux), utilisez 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}`); }); 

Ces deux fonctions ont une contrepartie synchrone. Un exemple pour child_process.execSync :

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

De même que child_process.spawnSync :

 const { spawnSync} = require('child_process'); const child = spawnSync('ls', ['-lh', '/usr']); console.log('error', child.error); console.log('stdout ', child.stderr); console.log('stderr ', child.stderr); 

Remarque: Le code suivant est encore fonctionnel, mais s'adresse principalement aux utilisateurs d'ES5 et avant.

Le module pour engendrer des processus enfants avec Node.js est bien documenté dans la documentation (v5.0.0). Pour exécuter une commande et récupérer sa sortie complète en tant que tampon, utilisez 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 vous devez utiliser handle E / S processus avec des flux, comme lorsque vous attendez de grandes quantités de sortie, utilisez 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 vous exécutez un fichier plutôt qu'une commande, vous pouvez utiliser child_process.execFile , quels paramètres sont presque identiques à spawn , mais a un quatrième paramètre de rappel comme exec pour récupérer les tampons de sortie. Cela pourrait être un peu comme ceci:

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

À partir de v0.11.12 , Node prend désormais en charge le génome et l' exec synchrones. Toutes les méthodes décrites ci-dessus sont asynchrones et ont une contrepartie synchrone. La documentation pour eux peut être trouvée ici . Bien qu'ils soient utiles pour les scripts, notez que contrairement aux méthodes utilisées pour générer des processus enfants de façon asynchrone, les méthodes synchrones ne renvoient pas une instance de ChildProcess .

Node JS v8.1.4 , LTS v6.11.1 et v4.8.4 — Juillet 2017

Méthode asynchrone et correcte:

 'use strict'; const spawn = require( 'child_process' ).spawn, 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}` ); }); 

Synchronisation:

 'use strict'; const spawn = require( 'child_process' ).spawnSync, ls = spawn( 'ls', [ '-lh', '/usr' ] ); console.log( `stderr: ${ls.stderr.toString()}` ); console.log( `stdout: ${ls.stdout.toString()}` ); 

From Node.js v8.1.4 Documentation

Il en va de même pour Node.js v6.11.1 Documentation et Node.js v4.8.4 Documentation

Vous recherchez child_process.exec

Voici l'exemple:

 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}`); } }); 

Je viens d'écrire un assistant Cli pour traiter facilement avec Unix / Windows.

Javascript:

 define(["require", "exports"], function (require, exports) { /** * Helper to use the Command Line Interface (CLI) easily with both Windows and Unix environments. * Requires underscore or lodash as global through "_". */ var Cli = (function () { function Cli() {} /** * Execute a CLI command. * Manage Windows and Unix environment and try to execute the command on both env if fails. * Order: Windows -> Unix. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success. * @param callbackErrorWindows Failure on Windows env. * @param callbackErrorUnix Failure on Unix env. */ Cli.execute = function (command, args, callback, callbackErrorWindows, callbackErrorUnix) { if (typeof args === "undefined") { args = []; } Cli.windows(command, args, callback, function () { callbackErrorWindows(); try { Cli.unix(command, args, callback, callbackErrorUnix); } catch (e) { console.log('------------- Failed to perform the command: "' + command + '" on all environments. -------------'); } }); }; /** * Execute a command on Windows environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success callback. * @param callbackError Failure callback. */ Cli.windows = function (command, args, callback, callbackError) { if (typeof args === "undefined") { args = []; } try { Cli._execute(process.env.comspec, _.union(['/c', command], args)); callback(command, args, 'Windows'); } catch (e) { callbackError(command, args, 'Windows'); } }; /** * Execute a command on Unix environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success callback. * @param callbackError Failure callback. */ Cli.unix = function (command, args, callback, callbackError) { if (typeof args === "undefined") { args = []; } try { Cli._execute(command, args); callback(command, args, 'Unix'); } catch (e) { callbackError(command, args, 'Unix'); } }; /** * Execute a command no matters what's the environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @private */ Cli._execute = function (command, args) { var spawn = require('child_process').spawn; var childProcess = spawn(command, args); childProcess.stdout.on("data", function (data) { console.log(data.toString()); }); childProcess.stderr.on("data", function (data) { console.error(data.toString()); }); }; return Cli; })(); exports.Cli = Cli; }); 

Fichier source original typographique:

  /** * Helper to use the Command Line Interface (CLI) easily with both Windows and Unix environments. * Requires underscore or lodash as global through "_". */ export class Cli { /** * Execute a CLI command. * Manage Windows and Unix environment and try to execute the command on both env if fails. * Order: Windows -> Unix. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success. * @param callbackErrorWindows Failure on Windows env. * @param callbackErrorUnix Failure on Unix env. */ public static execute(command: string, args: string[] = [], callback ? : any, callbackErrorWindows ? : any, callbackErrorUnix ? : any) { Cli.windows(command, args, callback, function () { callbackErrorWindows(); try { Cli.unix(command, args, callback, callbackErrorUnix); } catch (e) { console.log('------------- Failed to perform the command: "' + command + '" on all environments. -------------'); } }); } /** * Execute a command on Windows environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success callback. * @param callbackError Failure callback. */ public static windows(command: string, args: string[] = [], callback ? : any, callbackError ? : any) { try { Cli._execute(process.env.comspec, _.union(['/c', command], args)); callback(command, args, 'Windows'); } catch (e) { callbackError(command, args, 'Windows'); } } /** * Execute a command on Unix environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @param callback Success callback. * @param callbackError Failure callback. */ public static unix(command: string, args: string[] = [], callback ? : any, callbackError ? : any) { try { Cli._execute(command, args); callback(command, args, 'Unix'); } catch (e) { callbackError(command, args, 'Unix'); } } /** * Execute a command no matters what's the environment. * * @param command Command to execute. ('grunt') * @param args Args of the command. ('watch') * @private */ private static _execute(command, args) { var spawn = require('child_process').spawn; var childProcess = spawn(command, args); childProcess.stdout.on("data", function (data) { console.log(data.toString()); }); childProcess.stderr.on("data", function (data) { console.error(data.toString()); }); } } Example of use: Cli.execute(Grunt._command, args, function (command, args, env) { console.log('Grunt has been automatically executed. (' + env + ')'); }, function (command, args, env) { console.error('------------- Windows "' + command + '" command failed, trying Unix... ---------------'); }, function (command, args, env) { console.error('------------- Unix "' + command + '" command failed too. ---------------'); }); 
 const exec = require("child_process").exec exec("ls", (error, stdout, stderr) => { //do whatever here }) 

Depuis la version 4, l'alternative la plus proche est la méthode child_process.execSync :

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

Notez que cette méthode bloque la boucle d'événements.

Si vous voulez quelque chose qui ressemble beaucoup à la meilleure réponse, mais qui est également synchrone, cela fonctionnera.

 var exec = require('child_process').execSync; var cmd = "echo 'hello world'"; var options = { encoding: 'utf8' }; console.log(exec(cmd, options)); 

La réponse de l'hexacyanide est presque complète. Sur le prince commande Windows, prince.exe , prince.cmd , prince.bat ou tout simplement prince (je ne suis pas au courant de la façon dont les gemmes sont regroupées, mais les bacs npm viennent avec un script sh et un script batch – npm et npm.cmd ). Si vous souhaitez écrire un script portable qui fonctionnerait sur Unix et Windows, vous devez générer le bon exécutable.

Voici une fonction de multiplication simple mais portative:

 function spawn(cmd, args, opt) { var isWindows = /win/.test(process.platform); if ( isWindows ) { if ( !args ) args = []; args.unshift(cmd); args.unshift('/c'); cmd = process.env.comspec; } return child_process.spawn(cmd, args, opt); } var cmd = spawn("prince", ["-v", "builds/pdf/book.html", "-o", "builds/pdf/book.pdf"]) // Use these props to get execution results: // cmd.stdin; // cmd.stdout; // cmd.stderr; 

J'ai eu le même problème et j'ai trouvé pour toujours. C'est une CLI que vous pouvez installer avec npm et ça a bien fonctionné avec mes framboises pi! Et si vous voulez l'exécuter sur le démarrage, vous pouvez installer "service pour toujours". Ils installeront tout ce dont vous avez besoin pour que votre application soit opérationnelle en tout temps!

Regarde!

https://github.com/zapty/forever-service

https://www.npmjs.com/package/forever