1. ホーム
  2. javascript

[解決済み] node.js シェルコマンドの実行

2022-05-15 16:58:49

質問

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() は動作しないのですか? 参考までに、私は child_process.spawn というのは child_process.exec には200KBのバッファ制限があるためです。

最終的な解決

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

どのように解決するのですか?

ここでは、修正しなければならない3つの問題があります。

まず は、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変数に追加しています。

第三の は、'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) }
);