web-dev-qa-db-fra.com

Exécuter un binaire en ligne de commande avec Node.js

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

Voici un exemple en 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?

454
Dave Thompson

Pour les versions encore plus récentes de Node.js (v8.1.4), les événements et les appels sont similaires ou identiques aux versions antérieures, mais il est conseillé d'utiliser les nouvelles fonctionnalités standard. Exemples:

Pour une sortie mise en mémoire tampon et non formatée en flux (vous obtenez tout en une 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 des promesses:

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 sous forme de 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 stderr of parent process
// you can set options.stdio if you want it to go elsewhere
let stdout = execSync('ls');

Ainsi que 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);

Remarque: Le code suivant est toujours fonctionnel, mais s’adresse principalement aux utilisateurs d’ES5 et antérieurs.

Le module de génération de processus enfants avec Node.js est bien décrit dans la documentation (v5.0.0). Pour exécuter une commande et extraire sa sortie complète sous forme de 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 des entrées/sorties de processus de traitement avec des flux, par exemple 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 voudrez peut-être utiliser child_process.execFile , paramètres presque identiques à spawn, mais disposant d'un quatrième paramètre de rappel, comme exec, pour extraire les tampons de sortie. Cela pourrait ressembler un peu à ça:

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

À compter de v0.11.12 , Node prend désormais en charge les variables spawn et 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'elles soient utiles pour les scripts, notez que, contrairement aux méthodes utilisées pour générer des processus enfants de manière asynchrone, les méthodes synchrones ne renvoient pas d'instance de ChildProcess .

814
hexacyanide

Nœud JS v11.5.0, LTS v10.14.2 et v8.14.1 --- décembre 2018

Async et méthode appropriée (Unix):

'use strict';

const
    { spawn } = require( 'child_process' ),
    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éthode Async (Windows):

'use strict';

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


Sync:

'use strict';

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

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

De Node.js v11.5.0 Documentation

Il en va de même pour Node.js v10.14.2 Documentation et Node.js v8.14.1 Documentation

200
iSkore

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}`);
        }
});
61
Andrei Karpushonak
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})
22
Ben Bieler

Si vous voulez quelque chose qui ressemble de près à la réponse - top mais qui est également synchrone, cela fonctionnera.

var execSync = require('child_process').execSync;
var cmd = "echo 'hello world'";

var options = {
  encoding: 'utf8'
};

console.log(execSync(cmd, options));
14
Cameron

Je viens d'écrire un assistant Cli pour gérer facilement 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 d'origine TypeScript:

 /**
 * 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. ---------------');
    });
13
Vadorequest

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

const execSync = require('child_process').execSync;

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

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

12
Pasha Rumkin

Si une dépendance ne vous dérange pas et que vous souhaitez utiliser des promesses, child-process-promise fonctionne:

installation

npm install child-process-promise --save

exec Utilisation

var exec = require('child-process-promise').exec;

exec('echo hello')
    .then(function (result) {
        var stdout = result.stdout;
        var stderr = result.stderr;
        console.log('stdout: ', stdout);
        console.log('stderr: ', stderr);
    })
    .catch(function (err) {
        console.error('ERROR: ', err);
    });

utilisation spawn

var spawn = require('child-process-promise').spawn;

var promise = spawn('echo', ['hello']);

var childProcess = promise.childProcess;

console.log('[spawn] childProcess.pid: ', childProcess.pid);
childProcess.stdout.on('data', function (data) {
    console.log('[spawn] stdout: ', data.toString());
});
childProcess.stderr.on('data', function (data) {
    console.log('[spawn] stderr: ', data.toString());
});

promise.then(function () {
        console.log('[spawn] done!');
    })
    .catch(function (err) {
        console.error('[spawn] ERROR: ', err);
    });
5
Words Like Jared

La réponse de @ hexacyanide est presque complète. Sous Windows, la commande prince pourrait être prince.exe, prince.cmd, prince.bat ou tout simplement prince (je ne suis pas au courant de la façon dont les gems sont fournis, mais les bacs npm sont livrés avec un script sh et un script de traitement par lots - npm et npm.cmd) . Si vous voulez écrire un script portable s'exécutant sous Unix et Windows, vous devez générer le bon fichier exécutable.

Voici une fonction de spawn simple mais portable:

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;
1
DUzun

Vous pouvez maintenant utiliser shelljs (à partir du noeud v4) comme suit:

var Shell = require('shelljs');

Shell.echo('hello world');
Shell.exec('node --version')
0
FacePalm