admin管理员组文章数量:1134081
I am still trying to grasp the finer points of how I can run a linux or windows shell command and capture output within node.js; ultimately, I want to do something like this...
//pseudocode
output = run_command(cmd, args)
The important piece is that output
must be available to a globally scoped variable (or object). I tried the following function, but for some reason, I get undefined
printed to the console...
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" <------
I'm having trouble understanding where the code breaks above... a very simple prototype of that model works...
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" <----
Can someone help me understand why try_this()
works, and run_cmd()
does not? FWIW, I need to use child_process.spawn
, because child_process.exec
has a 200KB buffer limit.
Final Resolution
I'm accepting James White's answer, but this is the exact code that worked for me...
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);
I am still trying to grasp the finer points of how I can run a linux or windows shell command and capture output within node.js; ultimately, I want to do something like this...
//pseudocode
output = run_command(cmd, args)
The important piece is that output
must be available to a globally scoped variable (or object). I tried the following function, but for some reason, I get undefined
printed to the console...
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" <------
I'm having trouble understanding where the code breaks above... a very simple prototype of that model works...
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" <----
Can someone help me understand why try_this()
works, and run_cmd()
does not? FWIW, I need to use child_process.spawn
, because child_process.exec
has a 200KB buffer limit.
Final Resolution
I'm accepting James White's answer, but this is the exact code that worked for me...
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);
Share
Improve this question
edited Feb 3, 2018 at 14:34
Mike Pennington
asked Jan 22, 2013 at 12:25
Mike PenningtonMike Pennington
43k21 gold badges139 silver badges188 bronze badges
4
|
10 Answers
Reset to default 89 +50There are three issues here that need to be fixed:
First is that you are expecting synchronous behavior while using stdout asynchronously. All of the calls in your run_cmd
function are asynchronous, so it will spawn the child process and return immediately regardless of whether some, all, or none of the data has been read off of stdout. As such, when you run
console.log(foo.stdout);
you get whatever happens to be stored in foo.stdout at the moment, and there's no guarantee what that will be because your child process might still be running.
Second is that stdout is a readable stream, so 1) the data event can be called multiple times, and 2) the callback is given a buffer, not a string. Easy to remedy; just change
foo = new run_cmd(
'netstat.exe', ['-an'], function (me, data){me.stdout=data;}
);
into
foo = new run_cmd(
'netstat.exe', ['-an'], function (me, buffer){me.stdout+=buffer.toString();}
);
so that we convert our buffer into a string and append that string to our stdout variable.
Third is that you can only know you've received all output when you get the 'end' event, which means we need another listener and callback:
function run_cmd(cmd, args, cb, end) {
// ...
child.stdout.on('end', end);
}
So, your final result is this:
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) }
);
A simplified version of the accepted answer (third point), just worked for me.
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) });
} // ()
Usage:
run_cmd( "ls", ["-l"], function(text) { console.log (text) });
run_cmd( "hostname", [], function(text) { console.log (text) });
I used this more concisely :
var sys = require('sys')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
exec("ls -la", puts);
it works perfectly. :)
Simplest way is to just use the ShellJS lib ...
$ npm install [-g] shelljs
EXEC Example:
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 supports many common shell commands mapped as NodeJS functions including:
- cat
- cd
- chmod
- cp
- dirs
- echo
- exec
- exit
- find
- grep
- ln
- ls
- mkdir
- mv
- popd
- pushd
- pwd
- rm
- sed
- test
- which
I had a similar problem and I ended up writing a node extension for this. You can check out the git repository. It's open source and free and all that good stuff !
https://github.com/aponxi/npm-execxi
ExecXI is a node extension written in C++ to execute shell commands one by one, outputting the command's output to the console in real-time. Optional chained, and unchained ways are present; meaning that you can choose to stop the script after a command fails (chained), or you can continue as if nothing has happened !
Usage instructions are in the ReadMe file. Feel free to make pull requests or submit issues!
I thought it was worth to mention it.
@TonyO'Hagan is comprehrensive shelljs
answer, but, I would like to highlight the synchronous version of his answer:
var shell = require('shelljs');
var output = shell.exec('netstat -rn', {silent:true}).output;
console.log(output);
Synchronous one-liner:
require('child_process').execSync("echo 'hi'", function puts(error, stdout, stderr) {
console.log(stdout)
});
There's a variable conflict in your run_cmd
function:
var me = this;
child.stdout.on('data', function(me, data) {
// me is overriden by function argument
cb(me, data);
});
Simply change it to this:
var me = this;
child.stdout.on('data', function(data) {
// One argument only!
cb(me, data);
});
In order to see errors always add this:
child.stderr.on('data', function(data) {
console.log( data );
});
EDIT You're code fails because you are trying to run dir
which is not provided as a separate standalone program. It is a command in cmd
process. If you want to play with filesystem use native require( 'fs' )
.
Alternatively ( which I do not recommend ) you can create a batch file which you can then run. Note that OS by default fires batch files via cmd
.
You're not actually returning anything from your run_cmd function.
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...'
Works just fine. :)
A promisified version of the most-awarded answer:
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) })
})
}
To use:
runCmd('ls').then(ret => console.log(ret))
本文标签: javascriptnodejs shell command executionStack Overflow
版权声明:本文标题:javascript - node.js shell command execution - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736790641a1953072.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
me.stdout = "";
incmd_exec()
to prevent concatenatingundefined
to the beginning of the result. – aorcsik Commented Jul 14, 2015 at 14:35