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、 child_process.spawn
には200KBのバッファー制限があるため、 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);
ここには、修正が必要な3つの問題があります。
最初のは、stdoutを非同期で使用しているときに同期動作を期待しているということです。 run_cmd
関数の呼び出しはすべて非同期であるため、子プロセスが生成され、stdoutから読み取られたデータの一部、すべて、またはまったくに関係なく、すぐに戻ります。そのため、実行すると
console.log(foo.stdout);
現在foo.stdoutに保存されているものは何でも取得できますが、子プロセスがまだ実行されている可能性があるため、それが何であるかは保証されません。
Secondは、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変数に追加します。
Thirdは、「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) }
);
受け入れられた答えの単純化されたバージョン(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) });
私はこれをより簡潔に使用しました:
var sys = require('sys')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
exec("ls -la", puts);
完璧に機能します。 :)
最も簡単な方法は、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関数としてマッピングされた多くの一般的なシェルコマンドをサポートしています。
私は同様の問題を抱えていたため、このためにノード拡張を作成することになりました。 gitリポジトリをチェックアウトできます。それはオープンソースで無料であり、すべてのものが良いものです!
https://github.com/aponxi/npm-execxi
ExecXIは、シェルコマンドを1つずつ実行し、コマンドの出力をリアルタイムでコンソールに出力するために、C++で記述されたノード拡張です。オプションの連鎖および非連鎖の方法があります。つまり、コマンドが失敗した(連鎖)後にスクリプトを停止するか、何も起こらなかったように続行することを選択できます。
使用方法は ReadMeファイル にあります。プルリクエストを行ったり、問題を送信したりしてください。
言及する価値があると思いました。
@ TonyO'Haganは包括的なshelljs
の回答ですが、彼の回答の同期バージョンを強調したいと思います。
var Shell = require('shelljs');
var output = Shell.exec('netstat -rn', {silent:true}).output;
console.log(output);
require('child_process').execSync("echo 'hi'", function puts(error, stdout, stderr) { console.log(stdout) });
最も受賞した回答の約束されたバージョン:
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))
実際には、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...'
うまく動作します。 :)
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別のスタンドアロンプログラムとして提供されるnotであるdir
を実行しようとしているため、コードが失敗します。 cmd
プロセスのコマンドです。ファイルシステムを使用したい場合は、ネイティブrequire( 'fs' )
を使用してください。
別の方法として(推奨しません)、バッチファイルを作成して実行することもできます。 OSはデフォルトでcmd
を介してバッチファイルを起動することに注意してください。