node.js 셸 명령 실행


113

Linux 또는 Windows 셸 명령을 실행하고 node.js 내에서 출력을 캡처 할 수있는 방법에 대한 세부 사항을 여전히 파악하려고합니다. 궁극적으로 이렇게하고 싶습니다 ...

//pseudocode
output = run_command(cmd, args)

중요한 부분은 output전역 범위 변수 (또는 개체)에서 사용할 수 있어야한다는 것입니다. 다음 기능을 시도했지만 어떤 이유로 undefined콘솔에 인쇄됩니다 ...

function run_cmd(cmd, args, cb) {
  var spawn = require('child_process').spawn
  var child = spawn(cmd, args);
  var me = this;
  child.stdout.on('data', function(me, data) {
    cb(me, data);
  });
}
foo = new run_cmd('dir', ['/B'], function (me, data){me.stdout=data;});
console.log(foo.stdout);  // yields "undefined" <------

위의 코드가 어디에서 깨지는 지 이해하는 데 어려움이 있습니다 ... 그 모델의 매우 간단한 프로토 타입이 작동합니다 ...

function try_this(cmd, cb) {
  var me = this;
  cb(me, cmd)
}
bar = new try_this('guacamole', function (me, cmd){me.output=cmd;})
console.log(bar.output); // yields "guacamole" <----

누군가가 왜 try_this()작동하고 run_cmd()작동하지 않는지 이해하도록 도울 수 있습니까 ? FWIW, 200KB 버퍼 제한이 child_process.spawn있기 child_process.exec때문에을 사용해야 합니다.

최종 해결책

나는 James White의 대답을 받아들이고 있지만 이것은 나를 위해 일한 정확한 코드입니다 ...

function cmd_exec(cmd, args, cb_stdout, cb_end) {
  var spawn = require('child_process').spawn,
    child = spawn(cmd, args),
    me = this;
  me.exit = 0;  // Send a cb to set 1 when cmd exits
  me.stdout = "";
  child.stdout.on('data', function (data) { cb_stdout(me, data) });
  child.stdout.on('end', function () { cb_end(me) });
}
foo = new cmd_exec('netstat', ['-rn'], 
  function (me, data) {me.stdout += data.toString();},
  function (me) {me.exit = 1;}
);
function log_console() {
  console.log(foo.stdout);
}
setTimeout(
  // wait 0.25 seconds and print the output
  log_console,
250);

2
최종 해상도에서 당신은 설정해야합니다 me.stdout = "";cmd_exec()합치 방지하기 위해 undefined결과의 시작.
aorcsik

최종 해결 코드는 완전히 끔찍합니다. netstat를 실행하는 데 0.25 초 이상 걸리면 어떨까요?
Steven Lu

음 ... 내가 보너스를받은 답변 중 하나를 사용할 수 있을까요 ?????
Mike Pennington

답변:


89

여기에서 수정해야 할 세 가지 문제가 있습니다.

첫 번째 는 stdout을 비동기 적으로 사용하는 동안 동기 동작을 기대한다는 것입니다. run_cmd함수 의 모든 호출 은 비동기식이므로 자식 프로세스를 생성하고 데이터의 일부, 전체 또는 데이터가 stdout에서 읽혀지지 않았는지 여부에 관계없이 즉시 반환됩니다. 따라서 실행하면

console.log(foo.stdout);

현재 foo.stdout에 저장되는 것은 무엇이든 얻을 수 있으며 자식 프로세스가 여전히 실행 중일 수 있기 때문에 그것이 무엇인지 보장 할 수 없습니다.

두 번째 는 stdout이 읽을 수있는 스트림 이므로 1) 데이터 이벤트를 여러 번 호출 할 수 있고 2) 콜백에 문자열이 아닌 버퍼가 제공된다는 것입니다. 치료하기 쉽습니다. 그냥 변경

foo = new run_cmd(
    'netstat.exe', ['-an'], function (me, data){me.stdout=data;}
);

으로

foo = new run_cmd(
    'netstat.exe', ['-an'], function (me, buffer){me.stdout+=buffer.toString();}
);

버퍼를 문자열로 변환하고 해당 문자열을 stdout 변수에 추가합니다.

세 번째 는 'end'이벤트를 받았을 때 모든 출력을 받았음을 알 수 있다는 것입니다. 즉, 다른 리스너와 콜백이 필요합니다.

function run_cmd(cmd, args, cb, end) {
    // ...
    child.stdout.on('end', end);
}

따라서 최종 결과는 다음과 같습니다.

function run_cmd(cmd, args, cb, end) {
    var spawn = require('child_process').spawn,
        child = spawn(cmd, args),
        me = this;
    child.stdout.on('data', function (buffer) { cb(me, buffer) });
    child.stdout.on('end', end);
}

// Run C:\Windows\System32\netstat.exe -an
var foo = new run_cmd(
    'netstat.exe', ['-an'],
    function (me, buffer) { me.stdout += buffer.toString() },
    function () { console.log(foo.stdout) }
);

"자식 프로세스가 여전히 실행 중일 수 있기 때문에 이것이 무엇인지 보장 할 수 없습니다."close ...하지만 해당 시점에 설정 되지 않고 콜백이 마지막으로 호출 될 때만 설정 된다는 보장이 있습니다. 당신은 다른 곳에 표시된

4
이것은 매우 중요한 JS 개념에 대한 훌륭한 설명과 함께 훌륭한 답변입니다. 좋은!
L0j1k

1
함수 this.stdout = "";내에서 수행하고 싶을 것입니다 . run()그렇지 않으면 console.log(foo.sdtout);접두사가 붙습니다 undefined.
f1lt3r

78

받아 들여지는 대답의 단순화 된 버전 (세 번째 점)은 저에게 효과적이었습니다.

function run_cmd(cmd, args, callBack ) {
    var spawn = require('child_process').spawn;
    var child = spawn(cmd, args);
    var resp = "";

    child.stdout.on('data', function (buffer) { resp += buffer.toString() });
    child.stdout.on('end', function() { callBack (resp) });
} // ()

용법:

run_cmd( "ls", ["-l"], function(text) { console.log (text) });

run_cmd( "hostname", [], function(text) { console.log (text) });

1
프로세스가 0이 아닌 값을 반환 할 때 반환 값은 어떻게됩니까?
Vitim.us

2
child.stdout.on('close', (errCode) => { console.log(errCode) } )
Tushar Gautam

52

나는 이것을 더 간결하게 사용했습니다.

var sys = require('sys')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
exec("ls -la", puts);

완벽하게 작동합니다. :)


1
이것은 잘 작동하며 추가 노드 모듈이 필요하지 않습니다. 나는 그것을 좋아한다!
bearvarine 2015 년

9
sys.puts()2011 년에 더 이상 사용되지 않습니다 (Node.js v0.2.3 포함). console.log()대신 사용해야 합니다.
tfmontague

1
실제로 작동합니다 ... 그래서 궁금합니다. 왜 이것이 답이 아닌가? 간단한 있도록이다
ekkis

와!! 이 대답은 완벽하고 우아합니다. 감사합니다.
Em Ji Madhu

43

가장 간단한 방법은 ShellJS lib를 사용하는 것입니다.

$ npm install [-g] shelljs

EXEC 예 :

require('shelljs/global');

// Sync call to exec()
var version = exec('node --version', {silent:true}).output;

// Async call to exec()
exec('netstat.exe -an', function(status, output) {
  console.log('Exit status:', status);
  console.log('Program output:', output);
});

ShellJs.org 는 다음을 포함하여 NodeJS 함수로 매핑 된 많은 공통 셸 명령을 지원합니다.

  • 고양이
  • CD
  • chmod
  • cp
  • Dirs
  • 에코
  • 임원
  • 출구
  • 찾기
  • grep
  • ln
  • ls
  • mkdir
  • mv
  • 푸시
  • 암호
  • rm
  • sed
  • 테스트
  • 어느

shell.exec ( "foo.sh")에 의해 호출되는 쉘 스크립트에 매개 변수를 추가하는 방법은 무엇입니까?
pseudozach

1
문자열 맨 위에 인수를 추가 할 수 있습니다 shell.exec("foo.sh arg1 arg2 ... ").. 귀하의 foo.sh스크립트는 이러한 사용을 참조 할 수 있습니다 $1, $2... 등
토니 O'Hagan

실행하려는 명령에 사용자의 입력이 필요한 경우 ShellJS exec ()를 사용하지 마십시오. 이 함수는 본질적으로 대화 형이 아닙니다. 명령을 받고 출력을 출력하기 때문에 그 사이에 입력을받을 수 없습니다. 대신 내장 된 child_process를 사용하십시오. 예 : https://stackoverflow.com/a/31104898/9749509
MPatel1

4

비슷한 문제가 있었고 결국 이에 대한 노드 확장을 작성했습니다. git 저장소를 확인할 수 있습니다. 그것은 오픈 소스이며 무료이며 모든 좋은 것들입니다!

https://github.com/aponxi/npm-execxi

ExecXI는 C ++로 작성된 노드 확장으로, 쉘 명령을 하나씩 실행하여 명령의 출력을 실시간으로 콘솔에 출력합니다. 선택적 연결 및 연결 해제 방법이 있습니다. 즉, 명령이 실패 (체인) 된 후 스크립트를 중지하도록 선택하거나 아무 일도 일어나지 않은 것처럼 계속할 수 있습니다!

사용 지침은 ReadMe 파일에 있습니다. 풀 요청을하거나 문제를 제출하십시오!

나는 그것을 언급 할 가치가 있다고 생각했다.


4

@ TonyO'Hagan은 포괄적 인 shelljs대답이지만 그의 대답의 동기 버전을 강조하고 싶습니다.

var shell = require('shelljs');
var output = shell.exec('netstat -rn', {silent:true}).output;
console.log(output);

1

동기식 한 줄 :

require('child_process').execSync("echo 'hi'", function puts(error, stdout, stderr) { console.log(stdout) });


0

run_cmd함수에 변수 충돌이 있습니다 .

  var me = this;
  child.stdout.on('data', function(me, data) {
    // me is overriden by function argument
    cb(me, data);
  });

간단히 다음과 같이 변경하십시오.

  var me = this;
  child.stdout.on('data', function(data) {
    // One argument only!
    cb(me, data);
  });

오류를 보려면 항상 다음을 추가하십시오.

  child.stderr.on('data', function(data) {
      console.log( data );
  });

편집 별도의 독립 실행 형 프로그램으로 제공 되지 않는 실행 dir을 시도하기 때문에 코드가 실패했습니다 . 처리 중인 명령입니다 . 파일 시스템으로 플레이하려면 native .cmdrequire( 'fs' )

또는 (권장하지 않는) 배치 파일을 생성하여 실행할 수 있습니다. OS는 기본적으로 cmd.


... 당신의 도움을하지만, 내가 실행하는 경우에도 감사합니다 C:\Windows\System32\netstat.exe,이 여전히 양보하지 않는 결과를 ... 내 정확한 구문은했다 foo = new run_cmd('netstat.exe', ['-an'], function (me, data){me.stdout=data;});... 나는 또한 지금까지 어떤 성공의 전체 경로를 시도
마이크 페닝 턴

0

실제로 run_cmd 함수에서 아무것도 반환하지 않습니다.

function run_cmd(cmd, args, done) {
    var spawn = require("child_process").spawn;
    var child = spawn(cmd, args);
    var result = { stdout: "" };
    child.stdout.on("data", function (data) {
            result.stdout += data;
    });
    child.stdout.on("end", function () {
            done();
    });
    return result;
}

> foo = run_cmd("ls", ["-al"], function () { console.log("done!"); });
{ stdout: '' }
done!
> foo.stdout
'total 28520...'

잘 작동합니다. :)


나는 생각하지 않는다 return당신이 적절하게 개체의 속성을 설정 한 필요
마이크 페닝 턴

0

가장 많은 상을받은 답변의 약속 된 버전 :

  runCmd: (cmd, args) => {
    return new Promise((resolve, reject) => {
      var spawn = require('child_process').spawn
      var child = spawn(cmd, args)
      var resp = ''
      child.stdout.on('data', function (buffer) { resp += buffer.toString() })
      child.stdout.on('end', function () { resolve(resp) })
    })
  }

쓰다:

 runCmd('ls').then(ret => console.log(ret))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.