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ます。

最終決議

ジェームズホワイトの答えを受け入れますが、これは私のために働いた正確なコードです...

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 2015

ねえ、その最終的な解決コードは完全にひどいです、もしあなたのnetstatを実行するのに0.25秒より長くかかるとしたらどうでしょう?
Steven Lu

うーん...多分私がボーナスを授与した答えの1つを使用しますか?????
マイクペニントン

回答:


89

ここには、修正が必要な3つの問題があります。

1つ目は、stdoutを非同期で使用しながら同期動作を期待していることです。run_cmd関数内のすべての呼び出しは非同期であるため、stdoutから読み取られたデータの一部、すべて、またはなしに関係なく、子プロセスを生成してすぐに戻ります。そのため、実行すると

console.log(foo.stdout);

現時点ではfoo.stdoutに保存されているものが何でも得られますが、子プロセスがまだ実行されている可能性があるため、何が起こるかは保証されません。

2つ目は、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変数に追加します。

3つ目は、「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 2014年

1
関数this.stdout = "";内で実行する必要がありrun()ます。そうしないconsole.log(foo.sdtout);と、接頭辞がになりますundefined
f1lt3r 2015

78

受け入れられた回答の簡略版(3番目のポイント)は、私にとってはうまくいきました。

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
戻り値はどうですか、プロセスがゼロ以外を返す場合、どうすれば取得できますか?
Vitim.us

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

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

9
sys.puts()2011年に廃止されました(Node.js v0.2.3を使用)。console.log()代わりに使用する必要があります。
tfmontague 2016年

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
  • エコー
  • エグゼクティブ
  • 出口
  • 見つける
  • グレップ
  • ln
  • ls
  • mkdir
  • mv
  • ポップ
  • プッシュ
  • pwd
  • 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

4

私は同様の問題を抱えていて、結局このためのノード拡張を作成しました。gitリポジトリをチェックアウトできます。それはオープンソースであり、無料であり、すべてが良いものです!

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

ExecXIはC ++で記述されたノード拡張であり、シェルコマンドを1つずつ実行し、コマンドの出力をコンソールにリアルタイムで出力します。オプションの連鎖方法と非連鎖方法が存在します。つまり、コマンドが失敗した(連鎖した)後にスクリプトを停止するか、何も起こらなかったかのように続行することができます。

使用方法は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 );
  });

EDITあなたが実行しようとしているので、あなたがしているコードが失敗dirしているではない別のスタンドアロンのプログラムとして提供します。cmd処理中のコマンドです。ファイルシステムで遊びたい場合はネイティブを使用してくださいrequire( 'fs' )

または(お勧めしません)、バッチファイルを作成して実行することもできます。OSはデフォルトでを介してバッチファイルを起動することに注意してくださいcmd


助けてくれてありがとう...しかし、実行C:\Windows\System32\netstat.exeしても結果はまだ出ません...私の正確な構文はfoo = new run_cmd('netstat.exe', ['-an'], function (me, data){me.stdout=data;});...これまで成功しなかったフルパスも試しました
Mike Pennington

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オブジェクトの属性を適切に設定している限りは必要ないと思います
Mike Pennington

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.