Node.js로 명령 행 바이너리 실행


648

Ruby에서 Node.js로 CLI 라이브러리를 이식하는 중입니다. 내 코드에서 필요한 경우 여러 타사 바이너리를 실행합니다. 노드 에서이 작업을 수행하는 가장 좋은 방법을 모르겠습니다.

다음은 Ruby에서 파일을 PDF로 변환하기 위해 PrinceXML을 호출하는 예입니다.

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

노드에서 동등한 코드는 무엇입니까?


3
도서관은 시작하기에 좋은 곳입니다. 모든 OS 플랫폼에서 프로세스를 생성 할 수 있습니다.
흑요석


2
가장 간단한 방법은 child_process.exec를 사용하는 것입니다. 다음은 좋은 예입니다
drorw

답변:


1069

최신 버전의 Node.js (v8.1.4)의 경우에도 이벤트 및 호출은 이전 버전과 유사하거나 동일하지만 표준 최신 언어 기능을 사용하는 것이 좋습니다. 예 :

버퍼링되고 비 스트림 형식의 출력 (한 번에 가져옴)의 경우 다음을 사용하십시오 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}`);
});

약속과 함께 사용할 수도 있습니다.

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

점차 청크 단위로 데이터를 수신하려면 (스트림으로 출력) 다음을 사용하십시오 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}`);
});

이 두 기능에는 동기 기능이 있습니다. 예 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');

뿐만 아니라 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);

참고 : 다음 코드는 여전히 작동하지만 주로 ES5 및 이전 사용자를 대상으로합니다.

Node.js로 자식 프로세스를 생성하는 모듈은 문서 (v5.0.0) 에 잘 설명되어 있습니다. 명령을 실행하고 완전한 출력을 버퍼로 가져 오려면 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
});

많은 양의 출력을 예상 할 때와 같이 스트림에 핸들 프로세스 I / O를 사용해야하는 경우 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);

명령이 아닌 파일을 실행하는 경우 child_process.execFile거의 동일한 매개 변수 를 사용 하고 출력 버퍼 검색 spawn과 같은 네 번째 콜백 매개 변수를 사용할 수 exec있습니다. 다음과 같이 보일 수 있습니다.

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

현재 v0.11.12 , 노드는 이제 동기를 지원 spawn하고 exec. 위에서 설명한 모든 방법은 비동기식이며 동기식으로 대응됩니다. 이에 대한 설명서는 여기 에서 찾을 수 있습니다 . 그것들은 스크립팅에 유용하지만 자식 프로세스를 비동기 적으로 생성하는 데 사용되는 메소드와 달리 동기 메소드는의 인스턴스를 반환하지 않습니다 ChildProcess.


19
감사합니다. 이것은 나를 미치게했다. 때때로 우리는 멍청한 놈들이 그것을 배우고 실행할 수 있도록 명확한 해결책을 지적하는 데 도움이됩니다.
Dave Thompson

10
참고 : require ( 'child_process'). execFile ()은 여기에서 prince과 같이 시스템 전체에 알려진 명령이 아닌 파일을 실행해야하는 사람들에게 유용합니다.
Louis Ameline

2
대신에 child.pipe(dest)(존재하지 않는), 당신은 사용해야 child.stdout.pipe(dest)하고 child.stderr.pipe(dest), 예를 들어 child.stdout.pipe(process.stdout)child.stderr.pipe(process.stderr).
ComFreek

모든 것을 파일에 넣고 싶지 않지만 둘 이상의 명령을 실행하려면 어떻게해야합니까? 아마 echo "hello"and echo "world".
카메론

이것이 표준 방법입니까? 모든 래퍼가 nodejs로 작성된 방법을 의미합니까? 명령을 실행해야하는 gearman, rabbitmq 등을 말하지만 래퍼도 있지만 라이브러리 코드에서이 코드를 찾을 수 없습니다
ANinJa

261

노드 JS v13.9.0, LTS v12.16.1v10.19.0 --- 2020 년 3 월

비동기 방법 (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}` );
} );


비동기 방식 (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}` ) );


동조:

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

에서 Node.js를의 v13.9.0 문서

동일은 간다 Node.js를의 v12.16.1 문서Node.js를의 v10.19.0 문서


8
올바른 버전과 간단한 버전을 제공해 주셔서 감사합니다. 약간 더 간단한 동기화 버전은 필자가 필요로하는 "무언가를 버리십시오"스크립트에서 완전히 괜찮 았습니다.
Brian Jorden

문제 없어요! 일부에 따르면 "적절하지 않은"경우에도 항상 두 가지를 모두 사용하는 것이 좋습니다.
iSkore

7
Windows 에서이 예제를 수행하려면을 사용해야한다는 것을 지적 할 가치가 있습니다 'cmd', ['/c', 'dir']. 적어도 나는 'dir'이것을 기억하기 전에 왜 논증없이 작동하지 않는지 높고 낮게 검색하고
있었지만

1
이 출력 중 아무것도 콘솔에 출력되지 않습니다.
Tyguy7

@ Tyguy7 어떻게 운영하고 있습니까? 그리고 콘솔 객체에 대한 재정의가 있습니까?
iSkore

73

당신이 찾고있는 child_process.exec

예를 들면 다음과 같습니다.

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

맞습니다. 그러나 이런 종류의 자식 프로세스 호출에는 stdout 길이에 제한이 있습니다.
hgoebl

@ hgoebl, 그렇다면 대안은 무엇입니까?
Harshdeep

2
긴 stdout 출력 (예 : 몇 MB)의 경우 @Harshdeep stdout에서 data이벤트를 들을 수 있습니다 . 문서를 살펴보십시오. 그러나와 같아야 childProc.stdout.on("data", fn)합니다.
hgoebl

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

14
이 코드의 작동 방식과 응답 해결 방법에 대한 추가 설명을 추가하십시오. StackOverflow는 앞으로 이것을 읽는 사람들을위한 답변의 아카이브를 구축하고 있음을 기억하십시오.
Al Sweigart

4
Al이 말한 것은 사실이지만,이 답변의 이점은 빠른 답변이 필요한 사람을 위해 최고 답변을 읽는 것보다 훨씬 간단하다는 것입니다.

29

버전 4부터 가장 가까운 대안은 child_process.execSync방법입니다.

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

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

참고 execSync통화 차단 이벤트 루프를.


이것은 최신 노드에서 훌륭하게 작동합니다. (A)는되어 child_process사용할 때 생성되는 execSync하지만? 그리고 명령 직후에 제거됩니까? 메모리 누수가 없습니까?
NiCk Newman 2016

1
예, 메모리 누수가 없습니다. 노드에서 전혀 만들지 않고 libuv 자식 프로세스 구조 만 초기화한다고 생각합니다.
Paul Rumkin

21

최고 답변 과 매우 비슷 하지만 동기가 있는 것을 원한다면 이것이 효과가 있습니다.

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

var options = {
  encoding: 'utf8'
};

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

14

방금 유닉스 / 윈도우를 쉽게 다루기 위해 Cli 도우미를 작성했습니다.

자바 스크립트 :

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

타입 스크립트 원본 소스 파일 :

 /**
 * 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
사용 예와가 가장 최근 버전은 CLI에서 그런트를 사용하는 : gist.github.com/Vadorequest/f72fa1c152ec55357839
Vadorequest

7

이제 다음과 같이 shelljs (노드 v4)를 사용할 수 있습니다.

var shell = require('shelljs');

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

6

의존성을 신경 쓰지 않고 약속을 사용하려면 child-process-promise다음을 수행하십시오.

설치

npm install child-process-promise --save

exec 사용법

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

스폰 사용법

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

이 경량 npm패키지를 사용하십시오 .system-commands

그것을 봐 여기 .

다음과 같이 가져 오십시오.

const system = require('system-commands')

다음과 같은 명령을 실행하십시오.

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

완전한! 내 필요에 잘 작동합니다.
루즈 벨트

3

@hexacyanide의 답변은 거의 완전한 답변입니다. Windows의 명령이 prince될 수있다 prince.exe, prince.cmd, prince.bat또는 단지 prince(나는 보석이 번들로 제공하는 방법을 전혀 인식은 아니지만, NPM 쓰레기통은 쉬 스크립트와 배치 스크립트와 함께 - npmnpm.cmd ). Unix와 Windows에서 실행되는 이식 가능한 스크립트를 작성하려면 올바른 실행 파일을 생성해야합니다.

간단하지만 이식 가능한 스폰 기능은 다음과 같습니다.

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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.