Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
13
rated 0 times [  17] [ 4]  / answers: 1 / hits: 161548  / 12 Years ago, tue, january 22, 2013, 12:00:00

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

More From » node.js

 Answers
32

There 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:WindowsSystem32netstat.exe -an
var foo = new run_cmd(
'netstat.exe', ['-an'],
function (me, buffer) { me.stdout += buffer.toString() },
function () { console.log(foo.stdout) }
);

[#80712] Sunday, January 20, 2013, 12 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
jazminuniquer

Total Points: 63
Total Questions: 121
Total Answers: 96

Location: Cambodia
Member since Thu, May 21, 2020
4 Years ago
;