Jalankan biner baris perintah dengan Node.js

649

Saya sedang dalam proses porting perpustakaan CLI dari Ruby ke Node.js. Dalam kode saya, saya menjalankan beberapa binari pihak ketiga bila perlu. Saya tidak yakin bagaimana cara terbaik untuk melakukannya di Node.

Berikut ini contoh di Ruby tempat saya memanggil PrinceXML untuk mengonversi file ke PDF:

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

Apa kode yang setara di Node?

Dave Thompson
sumber
3
Perpustakaan ini adalah tempat yang baik untuk memulai. Ini memungkinkan Anda untuk menelurkan proses di semua platform os.
Obsidian
2
Paling sederhana adalah dengan menggunakan child_process.exec, berikut adalah beberapa contoh yang bagus
drorw

Jawaban:

1070

Untuk versi Node.js yang lebih baru (v8.1.4), acara dan panggilannya serupa atau identik dengan versi yang lebih lama, tetapi dianjurkan untuk menggunakan fitur bahasa standar yang lebih baru. Contoh:

Untuk buffer, output diformat non-aliran (Anda mendapatkan semuanya sekaligus), gunakan 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}`);
});

Anda juga dapat menggunakannya dengan Janji:

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

Jika Anda ingin menerima data secara bertahap dalam potongan (output sebagai aliran), gunakan 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}`);
});

Kedua fungsi ini memiliki mitra sinkron. Contoh untuk 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');

Dan juga 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);

Catatan: Kode berikut masih berfungsi, tetapi utamanya ditargetkan untuk pengguna ES5 dan sebelumnya.

Modul untuk memunculkan proses anak dengan Node.js didokumentasikan dengan baik dalam dokumentasi (v5.0.0). Untuk menjalankan perintah dan mengambil output lengkapnya sebagai buffer, gunakan 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
});

Jika Anda perlu menggunakan proses pegangan I / O dengan aliran, seperti ketika Anda mengharapkan sejumlah besar output, gunakan 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);

Jika Anda mengeksekusi file daripada perintah, Anda mungkin ingin menggunakan child_process.execFile, yang mana parameter yang hampir identik spawn, tetapi memiliki parameter callback keempat seperti execuntuk mengambil buffer output. Itu mungkin terlihat sedikit seperti ini:

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

Pada v0.11.12 , Node sekarang mendukung sinkron spawndan exec. Semua metode yang dijelaskan di atas tidak sinkron, dan memiliki mitra sinkron. Dokumentasi untuk mereka dapat ditemukan di sini . Meskipun mereka berguna untuk skrip, perlu diketahui bahwa tidak seperti metode yang digunakan untuk menelurkan proses anak secara serempak, metode sinkron tidak mengembalikan turunan dari ChildProcess.

hexacyanide
sumber
19
TERIMA KASIH. Ini membuatku gila. Kadang-kadang membantu untuk menunjukkan solusi yang jelas sehingga kita noobs (ke node) dapat belajar dan menjalankannya.
Dave Thompson
10
Catatan: membutuhkan ('child_process'). ExecFile () akan menarik bagi orang-orang yang perlu menjalankan file daripada perintah yang dikenal seluruh sistem seperti pangeran di sini.
Louis Ameline
2
Alih-alih child.pipe(dest)(yang tidak ada), Anda harus menggunakan child.stdout.pipe(dest)dan child.stderr.pipe(dest), misalnya child.stdout.pipe(process.stdout)dan child.stderr.pipe(process.stderr).
ComFreek
Bagaimana jika saya tidak ingin memasukkan semuanya ke dalam file, tetapi saya ingin menjalankan lebih dari satu perintah? Mungkin suka echo "hello"dan echo "world".
Cameron
apakah ini cara standar untuk melakukan ini? maksud saya bagaimana semua pembungkus ditulis dalam nodejs? maksud saya katakanlah untuk tukang gigi, rabbitmq dll yang memerlukan untuk menjalankan perintah tetapi mereka juga memiliki beberapa pembungkus juga tetapi saya tidak dapat menemukan kode ini dalam kode perpustakaan mereka
ANinJa
261

Node JS v13.9.0, LTS v12.16.1, dan v10.19.0 --- Mar 2020

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


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


Sinkronkan:

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

Dari Dokumentasi Node.js v13.9.0

Hal yang sama berlaku untuk Dokumentasi Node.js v12.16.1 dan Dokumentasi Node.js v10.19.0

iSkore
sumber
8
Terima kasih telah memberikan versi yang tepat dan sederhana. Versi sinkronisasi yang sedikit lebih sederhana benar-benar baik untuk skrip saya "melakukan sesuatu dan membuangnya" yang saya butuhkan.
Brian Jorden
Tidak masalah! Selalu menyenangkan untuk memiliki keduanya meskipun menurut beberapa orang "tidak layak".
iSkore
7
Mungkin perlu menunjukkan bahwa untuk melakukan contoh ini di Windows, kita harus menggunakan 'cmd', ['/c', 'dir']. Setidaknya saya hanya mencari-cari alasan mengapa 'dir'tanpa argumen tidak berhasil sebelum saya mengingat ini ...;)
AndyO
1
Tak satu pun dari output ini APA PUN ke konsol.
Tyguy7
@ Tyguy7 bagaimana Anda menjalankannya? Dan apakah Anda memiliki penggantian pada objek konsol?
iSkore
73

Anda mencari child_process.exec

Berikut ini contohnya:

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
sumber
Ini benar. Namun ketahuilah bahwa pemanggilan semacam ini sebagai proses anak memiliki keterbatasan untuk jangka waktu yang lama.
hgoebl
@ Hgoebl, apa alternatifnya?
Harshdeep
2
@ Harshdeep jika output stdout panjang (misalnya MB beberapa), Anda dapat mendengarkan dataacara di stdout. Lihat di dokumen, tetapi pasti seperti itu childProc.stdout.on("data", fn).
hgoebl
30
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})
Ben Bieler
sumber
14
Harap tambahkan lebih banyak penjelasan tentang cara kerja kode ini dan cara memecahkan jawabannya. Ingat bahwa StackOverflow sedang membangun arsip jawaban untuk orang yang membaca ini di masa depan.
Al Sweigart
4
Apa yang Al katakan itu benar, tetapi saya akan mengatakan bahwa manfaat dari jawaban ini adalah bahwa hal itu jauh lebih sederhana daripada harus membaca jawaban teratas untuk seseorang yang membutuhkan respons cepat.
29

Karena versi 4 alternatif terdekat adalah child_process.execSyncmetode:

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

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

Perhatikan bahwa execSyncpanggilan memblokir loop peristiwa.

Paul Rumkin
sumber
Ini berfungsi baik pada simpul terbaru. Apakah child_processsedang dibuat saat menggunakan execSync? Dan apakah itu dihapus setelah perintah, kan? Jadi tidak ada memori yang bocor?
NiCk Newman
1
Ya, tidak ada kebocoran memori. Saya kira itu hanya menginisialisasi struktur proses libuv anak tanpa membuatnya di node sama sekali.
Paul Rumkin
21

Jika Anda menginginkan sesuatu yang mirip dengan jawaban teratas tetapi juga sinkron maka ini akan berhasil.

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

var options = {
  encoding: 'utf8'
};

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

Saya baru saja menulis pembantu Cli untuk menangani Unix / windows dengan mudah.

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

File sumber naskah asli:

 /**
 * 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
sumber
1
Versi terbaru di sana, dengan contoh penggunaan untuk menggunakan Grunt di CLI: gist.github.com/Vadorequest/f72fa1c152ec55357839
Vadorequest
7

Sekarang Anda dapat menggunakan shelljs (dari node v4) sebagai berikut:

var shell = require('shelljs');

shell.echo('hello world');
shell.exec('node --version')
Telapak tangan
sumber
6

Jika Anda tidak keberatan dengan ketergantungan dan ingin menggunakan janji, child-process-promiseberfungsi:

instalasi

npm install child-process-promise --save

exec Penggunaan

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

penggunaan bibit

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);
    });
Kata-kata Seperti Jared
sumber
4

Gunakan npmpaket ringan ini :system-commands

Lihat di sini .

Impor seperti ini:

const system = require('system-commands')

Jalankan perintah seperti ini:

system('ls').then(output => {
    console.log(output)
}).catch(error => {
    console.error(error)
})
Ken Mueller
sumber
Sempurna! Bekerja sangat baik untuk kebutuhan saya.
roosevelt
3

@ Jawaban hexacyanide hampir lengkap. Pada perintah Windows princebisa prince.exe, prince.cmd, prince.batatau hanya prince(aku tidak menyadari betapa permata dibundel, tapi sampah NPM datang dengan script sh dan batch script - npmdan npm.cmd). Jika Anda ingin menulis skrip portabel yang akan berjalan di Unix dan Windows, Anda harus menelurkan executable yang tepat.

Berikut adalah fungsi spawn yang sederhana namun portabel:

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
sumber