Führen Sie eine Befehlszeilen-Binärdatei mit Node.js aus

644

Ich bin gerade dabei, eine CLI-Bibliothek von Ruby auf Node.js zu portieren. In meinem Code führe ich bei Bedarf mehrere Binärdateien von Drittanbietern aus. Ich bin mir nicht sicher, wie ich dies in Node am besten erreichen kann.

Hier ist ein Beispiel in Ruby, in dem ich PrinceXML aufrufe, um eine Datei in eine PDF-Datei zu konvertieren:

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

Was ist der entsprechende Code in Node?

Dave Thompson
quelle
3
Diese Bibliothek ist ein guter Anfang. Es ermöglicht Ihnen, Prozesse auf allen Betriebssystemplattformen zu erzeugen.
Obsidian
Mögliches Duplikat von Ausführen und die Ausgabe eines Shell-Befehls in node.js
Damjan Pavlica
2
Am einfachsten ist es, child_process.exec zu verwenden. Hier sind einige gute Beispiele
drorw

Antworten:

1067

Für eine noch neuere Version von Node.js (v8.1.4) sind die Ereignisse und Aufrufe ähnlich oder identisch mit älteren Versionen, es wird jedoch empfohlen, die neueren Standard-Sprachfunktionen zu verwenden. Beispiele:

Verwenden Sie für gepufferte, nicht Stream-formatierte Ausgaben (Sie erhalten alles auf einmal) 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}`);
});

Sie können es auch mit Versprechen verwenden:

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();

Wenn Sie die Daten schrittweise in Blöcken empfangen möchten (Ausgabe als Stream), verwenden Sie 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}`);
});

Beide Funktionen haben ein synchrones Gegenstück. Ein Beispiel für 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');

Sowie 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);

Hinweis: Der folgende Code ist weiterhin funktionsfähig, richtet sich jedoch hauptsächlich an Benutzer von ES5 und früheren Versionen.

Das Modul zum Laichen untergeordneter Prozesse mit Node.js ist in der Dokumentation (v5.0.0) gut dokumentiert . Verwenden Sie Folgendes, um einen Befehl auszuführen und seine vollständige Ausgabe als Puffer abzurufen 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
});

Wenn Sie Handle-Prozess-E / A für Streams verwenden müssen, z. B. wenn Sie große Ausgabemengen erwarten, verwenden Sie 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);

Wenn Sie eine Datei anstelle eines Befehls ausführen, möchten Sie möglicherweise verwenden child_process.execFile, welche Parameter fast identisch sind spawn, aber einen vierten Rückrufparameter haben, wie execzum Abrufen von Ausgabepuffern. Das könnte ein bisschen so aussehen:

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

Ab Version 0.11.12 unterstützt Node jetzt synchrones spawnund exec. Alle oben beschriebenen Methoden sind asynchron und haben ein synchrones Gegenstück. Die Dokumentation dazu finden Sie hier . Beachten Sie, dass die synchronen Methoden im Gegensatz zu den Methoden, mit denen untergeordnete Prozesse asynchron erzeugt werden, keine Instanz von zurückgeben, obwohl sie für die Skripterstellung nützlich sind ChildProcess.

Hexacyanid
quelle
19
VIELEN DANK. Das hat mich verrückt gemacht. Manchmal hilft es, nur auf die offensichtliche Lösung hinzuweisen, damit wir Noobs (zum Knoten) lernen und damit laufen können.
Dave Thompson
10
Hinweis: require ('child_process'). ExecFile () ist von Interesse für Personen, die eine Datei anstelle eines systemweit bekannten Befehls wie prince ausführen müssen.
Louis Ameline
2
Anstelle von child.pipe(dest)(was nicht existiert) müssen Sie child.stdout.pipe(dest)und verwenden child.stderr.pipe(dest), z . B. child.stdout.pipe(process.stdout)und child.stderr.pipe(process.stderr).
ComFreek
Was ist, wenn ich nicht alles in eine Datei einfügen möchte, aber mehr als einen Befehl ausführen möchte? Vielleicht wie echo "hello"und echo "world".
Cameron
Ist dies der Standardweg, um dies zu tun? Ich meine, wie alle Wrapper in NodeJS geschrieben sind? Ich meine, sagen wir mal für Gearman, Rabbitmq usw., die den Befehl ausführen müssen, aber auch einen Wrapper haben, aber ich kann keinen dieser Codes in ihrem Bibliothekscode finden
ANinJa
261

Knoten JS v13.9.0, LTS v12.16.1und v10.19.0 --- März 2020

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


Asynchrone Methode (Windows):

'use strict';

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


Synchronisieren:

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

Aus der Node.js v13.9.0-Dokumentation

Gleiches gilt für die Dokumentation zu Node.js v12.16.1 und Node.js v10.19.0

iSkore
quelle
8
Vielen Dank, dass Sie sowohl die richtige als auch die einfache Version angegeben haben. Die etwas einfachere Synchronisierungsversion war völlig in Ordnung für mein einmaliges Skript "Mach etwas und wirf es weg", das ich brauchte.
Brian Jorden
Kein Problem! Es ist immer schön, beides zu haben, auch wenn es laut einigen nicht "richtig" ist.
iSkore
7
Es könnte erwähnenswert sein, dass man dieses Beispiel unter Windows verwenden muss 'cmd', ['/c', 'dir']. Zumindest habe ich nur hoch und niedrig gesucht, warum 'dir'ohne Argumente nicht funktioniert, bevor ich mich daran erinnerte ...;)
AndyO
1
Keine dieser Ausgaben gibt etwas an die Konsole aus.
Tyguy7
@ Tyguy7 wie läuft es? Und haben Sie Überschreibungen für das Konsolenobjekt?
iSkore
73

Sie suchen nach child_process.exec

Hier ist das Beispiel:

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}`);
        }
});
Andrei Karpushonak
quelle
Das ist richtig. Beachten Sie jedoch, dass diese Art des Aufrufs eines untergeordneten Prozesses Einschränkungen hinsichtlich der Länge von stdout aufweist.
Hgoebl
@hgoebl, was ist dann die Alternative?
Harshdeep
2
@Harshdeep Bei langen Standardausgaben (z. B. mehrere MB) können Sie dataEreignisse auf Standardausgabe abhören . Schauen Sie in die Dokumente, aber es muss so etwas wie sein childProc.stdout.on("data", fn).
Hgoebl
30
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})
Ben Bieler
quelle
14
Bitte fügen Sie weitere Erklärungen hinzu, wie dieser Code funktioniert und wie er die Antwort löst. Denken Sie daran, dass StackOverflow in Zukunft ein Archiv mit Antworten für Leser erstellt.
Al Sweigart
4
Was Al gesagt hat, ist wahr, aber ich werde sagen, der Vorteil dieser Antwort ist, dass es so viel einfacher ist, als die oberste Antwort für jemanden durchlesen zu müssen, der eine schnelle Antwort benötigt.
28

Seit Version 4 ist die nächste Alternative die child_process.execSyncMethode:

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

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

Beachten Sie, dass der execSyncAufruf die Ereignisschleife blockiert.

Paul Rumkin
quelle
Dies funktioniert hervorragend auf dem neuesten Knoten. Wird child_processbei der Verwendung ein Wesen geschaffen execSync? Und wird es direkt nach dem Befehl entfernt, richtig? Also keine Speicherlecks?
NiCk Newman
1
Ja, es gibt keine Speicherlecks. Ich denke, es initialisiert nur untergeordnete libuv-Prozessstrukturen, ohne sie überhaupt im Knoten zu erstellen.
Paul Rumkin
21

Wenn Sie etwas wollen, das der Top-Antwort sehr ähnlich ist , aber auch synchron ist, funktioniert dies.

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

var options = {
  encoding: 'utf8'
};

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

Ich habe gerade einen Cli-Helfer geschrieben, um einfach mit Unix / Windows umzugehen.

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

Typoskript-Originalquelldatei:

 /**
 * 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. ---------------');
    });
Vadorequest
quelle
1
Neueste Version dort, mit Verwendungsbeispiel zur Verwendung von Grunt in der CLI: gist.github.com/Vadorequest/f72fa1c152ec55357839
Vadorequest
7

Jetzt können Sie shelljs (von Knoten v4) wie folgt verwenden:

var shell = require('shelljs');

shell.echo('hello world');
shell.exec('node --version')
FacePalm
quelle
6

Wenn Ihnen eine Abhängigkeit nichts ausmacht und Sie Versprechen verwenden möchten, child-process-promisefunktioniert dies:

Installation

npm install child-process-promise --save

exec Verwendung

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

Spawn-Nutzung

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);
    });
Worte wie Jared
quelle
4

Verwenden Sie dieses leichte npmPaket:system-commands

Schau es dir hier an .

Importiere es so:

const system = require('system-commands')

Führen Sie folgende Befehle aus:

system('ls').then(output => {
    console.log(output)
}).catch(error => {
    console.error(error)
})
Ken Mueller
quelle
Perfekt! Funktioniert hervorragend für meine Bedürfnisse.
Roosevelt
3

Die Antwort von @ hexacyanide ist fast vollständig. Auf Windows - Befehl princesein könnte prince.exe, prince.cmd, prince.batoder einfach nur prince(ich bin nicht bewusst, wie Edelsteine gebündelt sind, aber npm Behälter werden mit einer sh Skript und einem Batch - Skript - npmund npm.cmd). Wenn Sie ein portables Skript schreiben möchten, das unter Unix und Windows ausgeführt werden kann, müssen Sie die richtige ausführbare Datei erstellen.

Hier ist eine einfache, aber tragbare Spawn-Funktion:

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