node.js - Accessing the exit code and stderr of a system command - javascript

The code snippet shown below works great for obtaining access to the stdout of a system command. Is there some way that I can modify this code so as to also get access to the exit code of the system command and any output that the system command sent to stderr?
#!/usr/bin/node
var child_process = require('child_process');
function systemSync(cmd) {
return child_process.execSync(cmd).toString();
};
console.log(systemSync('pwd'));

You do not need to do it Async. You can keep your execSync function.
Wrap it in a try, and the Error passed to the catch(e) block will contain all the information you're looking for.
var child_process = require('child_process');
function systemSync(cmd) {
try {
return child_process.execSync(cmd).toString();
}
catch (error) {
error.status; // Might be 127 in your example.
error.message; // Holds the message you typically want.
error.stderr; // Holds the stderr output. Use `.toString()`.
error.stdout; // Holds the stdout output. Use `.toString()`.
}
};
console.log(systemSync('pwd'));
If an error is NOT thrown, then:
status is guaranteed to be 0
stdout is what's returned by the function
stderr is almost definitely empty because it was successful.
In the rare event the command line executable returns a stderr and yet exits with status 0 (success), and you want to read it, you will need the async function.

You will want the async/callback version of exec. There are 3 values returned. The last two are stdout and stderr. Also, child_process is an event emitter. Listen for the exit event. The first element of the callback is the exit code. (Obvious from the syntax, you'll want to use node 4.1.1 to get the code below to work as written)
const child_process = require("child_process")
function systemSync(cmd){
child_process.exec(cmd, (err, stdout, stderr) => {
console.log('stdout is:' + stdout)
console.log('stderr is:' + stderr)
console.log('error is:' + err)
}).on('exit', code => console.log('final exit code is', code))
}
Try the following:
`systemSync('pwd')`
`systemSync('notacommand')`
And you will get:
final exit code is 0
stdout is:/
stderr is:
Followed by:
final exit code is 127
stdout is:
stderr is:/bin/sh: 1: notacommand: not found

You may also use child_process.spawnSync(), as it returns much more:
return:
pid <Number> Pid of the child process
output <Array> Array of results from stdio output
stdout <Buffer> | <String> The contents of output[1]
stderr <Buffer> | <String> The contents of output[2]
status <Number> The exit code of the child process
signal <String> The signal used to kill the child process
error <Error> The error object if the child process failed or timed out
So the exit code you're looking for would be ret.status.

Related

node js node-exec-promise.exec command to call retire.js through command line works but ends with an error

Trying to run the command mentioned in sections below, it is intended to run retire.js on some javascript files/libraries and pipe the output in a json file. The command works and produces output in a json file but ends with an error.
When I try the same command directly by copy pasting the command in command line it works without an error.
I have tried the exexSync version and shelljs as well though get error in Shelljs as well. However, I am not able to understand what is causing the problem.
using node-exec-promise.exec :
exec('node C:/Users/walee/AppData/Roaming/npm/node_modules/retire/bin/retire --outputformat json --outputpath D:/Internship/local/testing/1.json').
then(function(retire_out){
console.log('Retire Command Success ');
console.log(' Retire_out Result ',retire_out);
return retire_out;
}, function(err){
console.error(err)
}
Using Shelljs:
if (shell.exec('node
C:/Users/walee/AppData/Roaming/npm/node_modules/retire/bin/retire --outputformat json --outputpath D:/Internship/local/testing/1.json').code !== 0) {
shell.echo('Error: Git commit failed');
shell.exit(1);
}
Expected Result is a Json file with known vulnerabilities found by retire.js, the file gets populated and it has valid json.
However, I get the following error in command line:
{ Error: Command failed: node C:/Users/walee/AppData/Roaming/npm/node_modules/retire/bin/retire --outputformat json --outputpath D:/Internship/local/testing/1.json
at ChildProcess.exithandler (child_process.js:294:12)
at ChildProcess.emit (events.js:189:13)
at maybeClose (internal/child_process.js:970:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)
killed: false,
code: 13,
signal: null,
cmd:
'node C:/Users/walee/AppData/Roaming/npm/node_modules/retire/bin/retire --outputformat json --outputpath D:/Internship/local/testing/1.json' }
Because retire exits with an exitcode of 13 if there are vulnerabilities found, you'll want a .catch before any .then to "allow" exitcode 13.
Also, since you've specified --outputpath for retire, the output will NOT go to stdout, so you will need to read the outputfile in the .then code - also, since retire would return an "error" (13) - node-exec-promise doesn't even give you access to stdout/stderr in the err that is passed to the .catch code anyway!
The following is pseudo-code, since I'm not fully familiar with node-exec-promise:
exec('node C:/Users/walee/AppData/Roaming/npm/node_modules/retire/bin/retire --outputformat json --outputpath D:/Internship/local/testing/1.json')
.catch(err => {
if (err.code !== 13) {
throw err;
}
})
.then(() => {
// read the output file if you want to display it here
})
.catch(err => {
//handle other errors here
});

Send IPC message with sh/bash to parent process (Node.js)

I have a Node.js process and this process forks an sh child process to run a bash script. Something like this:
const cp = require('child_process');
const n = cp.spawn('sh',['foo.sh'], {
stdio: ['ignore','ignore','ignore','ipc']
});
in my bash script (foo.sh), how can I send an IPC message back to the Node.js parent process? Cannot find out how to do that.
Doing some more research, looks like I will be getting closer to the IPC internals. One thing that might help is if I pass the parent PID to the bash script, then maybe I can do something with that.
When you add 'ipc' to your stdio options, the parent process and child process will establish a communication channel, and provide a file descriptor for the child process to use. This descriptor will be defined in your environment as $NODE_CHANNEL_FD. You can redirect output to this descriptor and it will be sent to the parent process to be parsed and handled.
As a simple example, I sent my name from the bash script to the parent process to be logged.
index.js
const cp = require('child_process');
const n = cp.spawn('sh', ['foo.sh'], {
stdio: ['ignore', 'ignore', 'ignore', 'ipc']
});
n.on('message', (data) => {
console.log('name: ' + data.name);
});
foo.sh
printf "{\"name\": \"Craig\"}\n" 1>&$NODE_CHANNEL_FD
Essentially what is happening in the bash file is:
I'm using the printf command to send the JSON to stdout, file descriptor 1.
And then redirecting it to a reference (&) of the $NODE_CHANNEL_FD
Note that the JSON you send must be properly formatted and terminated with a \n character
If you wanted to send data from the parent process to the bash process you could add
n.send({"message": "hello world"});
to your JavaScript, and in the bash file you could use something along the lines of
MESSAGE=read -u $NODE_CHANNEL_FD
echo " => message from parent process => $MESSAGE"
Note that you will have to change your stdio options so that you are not ignoring the standard output of the child process. You could set them to ['ignore', 1, 'ignore', 'ipc'] so that the child process' standard output goes straight to the parent's.

How to exec .exe file from Node.js

im using NWJS to create a simple desktop app. I need to connect console c++ app when I do a click event in a button on html5. I heard it's possible using 'child_process' internal module from Nodejs. I didn't get to exec the exe file when I click in the button.
I have next code:
const exec = require('child_process').execFile;
var cmd = 'Test.exe 1';
exec(cmd, function(error, stdout, stderr) {
// command output is in stdout
console.log('stdout:', stdout);
console.log('stderr:', stderr);
if (error !== null) {
console.log('exec error:', error);
}
});
The .exe file has a input parameter (a number) and it returns a simple text with the introduced number.
Anyone can help me?
Thanks!
I think you should give this a try
child_process.execFile(file[, args][, options][, callback])
file <String> The name or path of the executable file to run
args <Array> List of string arguments
options <Object>
cwd <String> Current working directory of the child process
env <Object> Environment key-value pairs
encoding <String> (Default: 'utf8')
timeout <Number> (Default: 0)
maxBuffer <Number> largest amount of data (in bytes) allowed on stdout or stderr - if exceeded child process is killed (Default: 200\*1024)
killSignal <String> (Default: 'SIGTERM')
uid <Number> Sets the user identity of the process. (See setuid(2).)
gid <Number> Sets the group identity of the process. (See setgid(2).)
callback <Function> called with the output when process terminates
error <Error>
stdout <String> | <Buffer>
stderr <String> | <Buffer>

Read output of shell command in meteor

I'm trying to ping a remote server to check if it's online or not. The flow is like this:
1) User insert target Hostname
2) Meteor execute command 'nmap -p 22 hostname'
3) Meteor read and parse the output, to check the status of the target.
I've been able to execute a command aynchronously, for example mkdir, that allow me to verify later that it worked.
Unfortunately it seems I'm not able to wait for the reply. My code, inside /server/poller.coffee is:
Meteor.methods
check_if_open: (target_server) ->
result = ''
exec = Npm.require('child_process').exec
result = exec 'nmap -p ' + target_server.port + ' ' + target_server.host
return result
This should execute exec synchronously, shouldn't it? Any other approach using Futures, ShellJS, AsyncWrap, failed with meteor refusing to start as soon as the node package was installed. It seems I can install only via meteor add (mrt).
My client side code, located at /client/views/home/home.coffee , is:
Template.home.events
'submit form': (e) ->
e.preventDefault()
console.log "Calling the connect button"
server_target =
host: $(e.target).find("[name=hostname]").val()
port: $(e.target).find("[name=port]").val()
password: $(e.target).find("[name=password]").val()
result = ''
result = Meteor.call('check_if_open', server_target)
console.log "You pressed the connect button"
console.log ' ' + result
Result is always null. Result should be a child process object, and has a stdout attribute, but such attribute is null.
What am I doing wrong? How do I read the output? I'm forced to do it asynchronously?
You'll need to use some kind of async wrapping, child_process.exec is strictly asynchronous. Here's how you could use Futures:
# In top-level code:
# I didn't need to install anything with npm,
# this just worked.
Future = Npm.require("fibers/future")
# in the method:
fut = new Future()
# Warning: if you do this, you're probably vulnerable to "shell injection"
# e.g. the user might set target_server.host to something like "blah.com; rm -rf /"
exec "nmap -p #{target_server.port} #{target_server.host}", (err, stdout, stderr) ->
fut.return [err, stdout, stderr]
[err, stdout, stderr] = fut.wait()
if err?
throw new Meteor.Error(...)
# do stuff with stdout and stderr
# note that these are Buffer objects, you might need to manually
# convert them to strings if you want to send them to the client
When you call the method on the client, you have to use an async callback. There's no fibers on the client.
console.log "You pressed the connect button"
Meteor.call "check_if_open", server_target, (err, result) ->
if err?
# handle the error
else
console.log result

Node.js exec with wget

I'm trying to download a lot of files using nodejs and the exec command, simplified like this:
var cmd = 'wget -O output.csv URL';
var child = exec(cmd, function(err) {
console.log('DONE');
});
However, the callback is triggered before the file was actually downloaded through wget, leading to a file that contains garbage like '��0O�6D�1n�]v�����#�'. Shouldn't the callback be triggered once wget is done? When running the same command on the command line it takes rougly 5 seconds, since the file has several MB.
Btw: I'm not using the request module since it's slower and I ran into emitter listener issues (EventEmitter memory leak detected. 11 listeners added).
Thanks!
This will involve some debugging.
Can you please try running your script as:
var cmd = 'wget -O output.csv URL';
var child = exec(
cmd,
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
}
);
It would be interesting to see what stdout and stderr say.
Right, you provided me your stderr which said:
http://productdata.zanox.com/exportservice/v1/rest/22791753C32335607.csv?ticket=BC4B91472561713FD43BA766542E9240AFDD01B95B123E40B2C0375E3A68C142
This URL the command line gets is missing everything after the ampersand (& character). This indicates a problem with escaping.
To get around this try replacing \& with \\\&.

Categories

Resources