Thực hiện nhị phân dòng lệnh với Node.js


648

Tôi đang trong quá trình chuyển thư viện CLI từ Ruby sang Node.js. Trong mã của tôi, tôi thực thi một số nhị phân của bên thứ ba khi cần thiết. Tôi không chắc làm thế nào tốt nhất để thực hiện điều này trong Node.

Đây là một ví dụ trong Ruby nơi tôi gọi PrinceXML để chuyển đổi tệp thành PDF:

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

Mã tương đương trong Node là gì?


3
Đây thư viện là một nơi tốt để bắt đầu. Nó cho phép bạn sinh ra các quy trình trên tất cả các nền tảng os.
Obsidian


2
Đơn giản nhất là sử dụng child_ process.exec, đây là một số ví dụ hay
drorw

Câu trả lời:


1069

Đối với phiên bản mới hơn của Node.js (v8.1.4), các sự kiện và cuộc gọi tương tự hoặc giống hệt với các phiên bản cũ hơn, nhưng chúng tôi khuyến khích sử dụng các tính năng ngôn ngữ mới hơn tiêu chuẩn. Ví dụ:

Đối với đầu ra được định dạng bộ đệm, không được phát trực tuyến (bạn nhận được tất cả cùng một lúc), hãy sử dụng 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}`);
});

Bạn cũng có thể sử dụng nó với Lời hứa:

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

Nếu bạn muốn nhận dữ liệu dần dần theo từng khối (đầu ra dưới dạng luồng), hãy sử dụng 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}`);
});

Cả hai chức năng này có một đối tác đồng bộ. Một ví dụ cho 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');

Cũng như 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);

Lưu ý: Mã sau đây vẫn hoạt động, nhưng chủ yếu nhắm vào người dùng ES5 trở về trước.

Mô-đun để sinh ra các tiến trình con với Node.js cũng được ghi lại trong tài liệu (v5.0.0). Để thực thi một lệnh và tìm nạp đầu ra hoàn chỉnh của nó dưới dạng bộ đệm, hãy sử dụng 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
});

Nếu bạn cần sử dụng xử lý I / O xử lý với các luồng, chẳng hạn như khi bạn đang mong đợi một lượng lớn đầu ra, hãy sử dụng 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);

Nếu bạn đang thực thi một tệp chứ không phải lệnh, bạn có thể muốn sử dụng child_process.execFile, tham số nào gần giống với spawnnhưng có tham số gọi lại thứ tư như execđể truy xuất bộ đệm đầu ra. Điều đó có thể trông hơi giống như thế này:

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

Kể từ v0.11.12 , Node hiện hỗ trợ đồng bộ spawnexec. Tất cả các phương pháp được mô tả ở trên là không đồng bộ và có một đối tác đồng bộ. Tài liệu cho họ có thể được tìm thấy ở đây . Mặc dù chúng rất hữu ích cho việc tạo kịch bản, nhưng lưu ý rằng không giống như các phương thức được sử dụng để sinh ra các tiến trình con không đồng bộ, các phương thức đồng bộ không trả về một thể hiện của ChildProcess.


19
CẢM ƠN BẠN. Điều này đã khiến tôi phát điên. Đôi khi nó giúp chỉ có giải pháp rõ ràng được chỉ ra để chúng ta noobs (đến nút) có thể học và chạy với nó.
Dave Thompson

10
Lưu ý: Yêu cầu ('child_ process'). ExecFile () sẽ được quan tâm cho những người cần chạy tệp chứ không phải là lệnh được biết đến trên toàn hệ thống như hoàng tử ở đây.
Louis Ameline

2
Thay vì child.pipe(dest)(không tồn tại), bạn phải sử dụng child.stdout.pipe(dest)child.stderr.pipe(dest), ví dụ child.stdout.pipe(process.stdout)child.stderr.pipe(process.stderr).
ComFalet

Điều gì xảy ra nếu tôi không muốn đặt mọi thứ vào một tệp, nhưng tôi muốn thực thi nhiều hơn một lệnh? Có lẽ thích echo "hello"echo "world".
Cameron

Đây có phải là cách tiêu chuẩn để làm điều này? tôi có nghĩa là làm thế nào tất cả các trình bao bọc được viết trong nodejs? ý tôi là hãy nói cho gearman, rabbitmq, v.v. yêu cầu chạy lệnh nhưng họ cũng có một số trình bao bọc nhưng tôi không thể tìm thấy bất kỳ mã nào trong mã thư viện của họ
ANinJa

261

Nút Node v13.9.0, LTS v12.16.1v10.19.0 --- Tháng 3 năm 2020

Phương pháp không đồng bộ (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}` );
} );


Phương pháp không đồng bộ (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}` ) );


Đồng bộ hóa:

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

Từ Tài liệu Node.js v13.9.0

Điều tương tự cũng xảy ra với Tài liệu Node.js v12.16.1Tài liệu Node.js v10.19.0


8
Cảm ơn bạn đã đưa ra cả hai phiên bản phù hợp và đơn giản. Phiên bản đồng bộ hóa đơn giản hơn một chút là hoàn toàn tốt đối với kịch bản "làm gì đó và vứt nó đi" mà tôi cần.
Brian Jorden

Không vấn đề gì! Luôn luôn tốt đẹp để có cả hai ngay cả khi nó không "phù hợp" theo một số người.
iSkore

7
Có thể đáng để chỉ ra rằng để làm ví dụ này trong Windows, người ta phải sử dụng 'cmd', ['/c', 'dir']. Ít nhất tôi chỉ tìm kiếm cao và thấp tại sao 'dir'không có đối số không hoạt động trước khi tôi nhớ điều này ...;)
AndyO

1
Không ai trong số này xuất ra BẤT CỨ điều khiển nào.
Tyguy7

@ Tyguy7 bạn đang chạy nó như thế nào? Và bạn có bất kỳ ghi đè nào trên đối tượng console không?
iSkore

73

Bạn đang tìm kiếm child_ process.exec

Dưới đây là ví dụ:

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

Chính xác. Nhưng hãy lưu ý rằng kiểu gọi quá trình con này có những hạn chế đối với độ dài của thiết bị xuất chuẩn.
hgoebl

@hgoebl, thế nào là thay thế?
Harshdeep

2
@Harshdeep trong trường hợp đầu ra thiết bị xuất chuẩn dài (vài MB, ví dụ) bạn có thể nghe datacác sự kiện trên thiết bị xuất chuẩn. Tìm trong các tài liệu, nhưng nó phải là một cái gì đó như childProc.stdout.on("data", fn).
hgoebl

30
const exec = require("child_process").exec
exec("ls", (error, stdout, stderr) => {
 //do whatever here
})

14
Vui lòng thêm giải thích cho cách mã này hoạt động và cách giải quyết câu trả lời. Hãy nhớ rằng StackOverflow đang xây dựng một kho lưu trữ các câu trả lời cho những người đọc nó trong tương lai.
Al Sweigart

4
Những gì Al nói là đúng, nhưng tôi sẽ nói lợi ích của câu trả lời này là nó đơn giản hơn nhiều so với việc phải đọc qua câu trả lời hàng đầu cho người cần phản hồi nhanh.

29

Vì phiên bản 4 thay thế gần nhất là child_process.execSyncphương thức:

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

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

Lưu ý rằng execSyncvòng gọi khối sự kiện.


Điều này hoạt động tuyệt vời trên nút mới nhất. Là một child_processđược tạo ra khi sử dụng execSyncmặc dù? Và nó có được gỡ bỏ ngay sau lệnh không? Vì vậy, không có rò rỉ bộ nhớ?
NiCk Newman

1
Vâng, không có rò rỉ bộ nhớ. Tôi đoán nó chỉ khởi tạo các cấu trúc tiến trình libuv con mà không tạo nó trong nút nào cả.
Paul Rumkin

21

Nếu bạn muốn một cái gì đó gần giống với câu trả lời hàng đầu nhưng cũng đồng bộ thì điều này sẽ hoạt động.

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

var options = {
  encoding: 'utf8'
};

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

14

Tôi vừa viết một trình trợ giúp Cli để đối phó với Unix / windows dễ dàng.

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

Tệp nguồn gốc bản thảo:

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

1
Phiên bản gần đây nhất ở đó, với ví dụ sử dụng để sử dụng Grunt trong CLI: gist.github.com/Vadorequest/f72fa1c152ec55357839
Vadorequest

7

Bây giờ bạn có thể sử dụng shelljs (từ nút v4) như sau:

var shell = require('shelljs');

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

6

Nếu bạn không ngại phụ thuộc và muốn sử dụng lời hứa, child-process-promise làm việc:

cài đặt

npm install child-process-promise --save

thực hiện sử dụng

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

sử dụng sinh sản

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

4

Sử dụng npmgói gọn nhẹ này :system-commands

Nhìn vào nó ở đây .

Nhập nó như thế này:

const system = require('system-commands')

Chạy các lệnh như thế này:

system('ls').then(output => {
    console.log(output)
}).catch(error => {
    console.error(error)
})

Hoàn hảo! Hoạt động tuyệt vời cho nhu cầu của tôi.
roosevelt

3

Câu trả lời của @ hexacyanide gần như hoàn chỉnh. Trên Windows lệnh princecó thể là prince.exe, prince.cmd, prince.bathoặc chỉ prince(Tôi không có ý thức về cách đá quý được đóng gói, nhưng thùng NPM đi kèm với một kịch bản sh và một kịch bản hàng loạt - npmnpm.cmd ). Nếu bạn muốn viết một tập lệnh di động chạy trên Unix và Windows, bạn phải tạo ra tập tin thực thi đúng.

Đây là một chức năng sinh sản đơn giản nhưng di động:

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.