Pass : (Colon) into child_process SPAWN arg - node.js

I have a command
omxplayer /home/pi/videos/9886a3n2545r7i505rzz.mp4 -o alsa:sysdefault
It runs fine from the command line, but if I translate that command to a spawn command:
let omxProcess = spawn('omxplayer', ['/home/pi/videos/9886a3n2545r7i505rzz.mp4', '-o', 'alsa:sysdefault'])
The command fails (without any error).
But if I run the following removing the :sysdefault it runs (But without the :sysdefault, the command is not the same and I need to run it with :sysdefault
let omxProcess = spawn('omxplayer', ['/home/pi/videos/9886a3n2545r7i505rzz.mp4', '-o', 'alsa'])
I'm thinking it has to do with having an ":" in the arg.
Any thoughts?

Since you're not using the shell: true flag, it's almost certainly not caused by the : in the command. You can always verify this, just to be on the safe side.
An easy way to check if the environment is messing with your arguments is calling another binary, for example echo, instead of omxplayer. Does it echo back your arguments? Is the colon still there?
The binary is probably exitting with some error code (and possibly an error message). To capture them, be sure to register handlers on the output streams, as well as an exit handler that should tell you the exit code. This is outlined in the child_process docs, right below spawn(). Adapted for your case:
omxProcess.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
omxProcess.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
omxProcess.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
Based on the output and the exit code, you should be able to debug the issue.

Related

Executing shell command using child process

I am trying to execute a command to fork a chain using ganache. I have struggled to find a way to do the fork programmatically so I want to try execute it through a child process.
The code I am using is:
const { exec } = require("child_process");
const ganache = require("ganache");
const ls = exec('ganache-cli --fork https://speedy-nodes-nyc.moralis.io/KEY_HERE/bsc/mainnet/archive', function (error, stdout, stderr) {
if (error) {
console.log(error.stack);
console.log('Error code: ' + error.code);
console.log('Signal received: ' + error.signal);
}
console.log('Child Process STDOUT: ' + stdout);
console.log('Child Process STDERR: ' + stderr);
});
ls.on('exit', function (code) {
console.log('Child process exited with exit code ' + code);
});
But when I run this, No output is given at all. No errors or anything it just carries on with the program asif this code did not exist. When I replace the command with 'dir', it works perfectly and lists the files in the directory. Even if I remove the node address and just use "ganache-cli --fork" as the command, nothing happens.
Why does nothing happen when I add the ganache commands?
If you take a look at the child process docs, they say exec:
spawns a shell and runs a command within that shell, passing the stdout and stderr to a callback function when complete.
However, Ganache is a process that continues running and doesn't "complete" until you kill it. This allows you to send multiple requests to Ganache without it shutting down on you.

Can I “listen” for a specific output with child_process?

So far I have gotten my script to execute a windows .bat file with child_process, my issue is that it opens it in the background with no way to “connect” to it to see what happens and debug, is there a way to “listen” for a certain output to happen? For example, if the .bat outputs a “Done!” in the shell at one point, is there a way to make my node.js script detect that certain keyword and run further commands if it does?
Thanks!
Some clarification: The .bat outputs "Done!" and stays running, it doesn't stop, all I want to do is detect that "Done!" so that I can send a message to the user that the server has successfully started
My current code:
exec('D:\\servers\\game_server_1\\start.bat', {shell: true, cwd: 'D:\\servers\\game_server_1'});
Well, if you're trying to do a one and done type of NodeJS script, you can just spawn a process that launches with the given command and exits when all commands completed. This creates a one and done streaming interface that you can monitor. The stdout returns a data buffer that returns the command you ran, unless it's something like START to launch a program-- it returns null. You could just issue a KILL command after the START -- your_program.exe:
const spawn = require('child_process').spawn;
const child = spawn('cmd.exe', ['/c', 'commands.bat']);
let DONE = 0;
const done = () => {
console.log("log it");
DONE++;
};
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
//it's important to add some type of counter to
//prevent any logic from running twice, since
//this will run twice for any given command
if ( data.toString().includes("DONE") && DONE === 0 ) {
done();
}
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
child.on('exit', function (code) {
console.log('child process exited with code ' + code);
});
Keep in mind, when you run a command to launch a program and the program launches, the data buffer will be null in stdout event listener. The error event will only fire if there was an issue with launching the program.
YOUR .BAT:
ECHO starting batch script
//example launching of program
START "" https://localhost:3000
//issue a command after your program launch
ECHO DONE
EXIT
You could also issue an ECHO DONE command right after the command where you launched the program and listen for that, and try and parse out that command from stdout.
You could use a Regular expression.
const { spawn } = require('child_process');
const child = spawn(...);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
// Now use a regular expression to detect a done event
// For example
data.toString().match(/Done!/);
});
// Error handling etc. here

Run tar command as child_process in Node.js

When trying to use child_process.spawn in node.js to run a tar command, runtime fails with ENOENT error.
If I run it as child_process.exec, the command runs and returns one line of output through exec.stdout.on('data') event handler. When running the command in a terminal it has more output than what exec is returning.
The key problem is that with the flags -cvMf tar requires the user to press enter to continue its process, which I was hoping to do via exec.send
Here's an excerpt of the code I have:
try {
let subprocess = exec('tar -cvMf /dev/nst0 /mnt/Data/2GBdata1 /mnt/Data/1GBdata');
subprocess.stdout.on('data', function (data) {
console.log('stdout on data: \n', data.toString());
});
subprocess.on('data', function(data) {
console.log('on data: \n', data);
})
subprocess.stdout.on('message', function (message) {
console.log('sub messgae: ', message);
});
subprocess.stderr.on('error', function(err) {
console.log('sub error thrown', err);
throw err;
})
subprocess.on('exit', function (data) {
subprocess = null;
console.log('sub exited: \n', data);
});
}
catch (err) {
console.error(`exec error: ${err}`);
output = `${err}`;
}
The output is simply:
stdout on data:
/mnt/Data/2GBdata1
stdout on data:
/mnt/Data/1GBdata
Then the process simply hangs.
The expected output (when running the command in terminal) is the following:
tar -cvMf /dev/nst0 /mnt/Data/2GBdata /mnt/Data/1GBdata
tar: Removing leading `/' from member names
/mnt/Data/2GBdata
/mnt/Data/1GBdata
Prepare volume #2 for ‘/dev/nst0’ and hit return:
My last option will be to write a shell script and use execFile, but I'd like to avoid over-complicating things if possible. Any and all help is appreciated, thanks!
I figured it out. For whatever reason the line Prepare volume #2 for ‘/dev/nst0’ and hit return: doesn't register as a stout event. However upon further research I found that tar has a -F flag, which allows you to define a script to be ran when it reaches the end of the tape.
I wrote a simple shell script that echos when the end of a tape is reached, which then registers as an stdout event.
With this, I was able to continue my project without any hassle.
The command now looks like this:
tar -F './tartest.sh' -cvMf /dev/nst0 '(data files here)'

Node: Read spawn stderr/stdout after quick exit event?

I'm using Node's spawn to create a new process. Sometimes this process will exit very quickly with an error code and a message on stderr. It appears that stderr is getting lost in this quick turnaround. I've tried this:
reader.stderr.on('data', function (buf) {
console.log('stderr message: ' + buf);
});
reader.on('exit', function (code, signal) {
console.log('Exit');
});
Output:
Exit
stderr message: ERROR: Missing required option for command.
I also tried reading it in the exit listener, but no luck:
reader.on('exit', function (code, signal) {
console.log('Exit');
console.log('stderr: ' + reader.stderr.read());
});
Output:
Exit
stderr: null
So, it appears the problem is that the stderr output is too slow, and is late after the exit event where I need that information. How can I fix this?
Taken from the child_process docs for exit:
Note that the child process stdio streams might still be open.
They then describe the close event:
This event is emitted when the stdio streams of a child process have all terminated. This is distinct from 'exit', since multiple processes might share the same stdio streams.
So it looks like you should be using close, not exit.

Node.js intercepting process.exit

So I wanted to run a shell command at the end of my node.js program and wait for the output/print it before exiting. I tried process.on('exit',function(){}) and running the child exec command in there but the program exited before the callback. So instead I used a closure on process.exit but I am getting some strange results. The basics of the code is:
process.exit = (function(old_exit){
return function(code){
var exec = require('child_process').exec;
var child;
child = exec("my shell command", function (error, stdout, stderr) {
if (error !== null) {
console.log('exec error: ' + error);
}
console.log(stdout);
//I first had this as the concluding line:
old_exit.apply(process,arguments);
//and I also tried
old_exit.apply(process,[]);
//and even (which I know is not in the right scope)
old_exit(code);
//and also
process.exit = old_exit;
process.exit(code);
});
}
}(process.exit));
Every one of the above results executed my shell command exactly twice and then exited. I also tried not calling anything at the end and while that kept it so that my command executed only once, the process hung instead of exiting at the end. Unless there is something simply I'm missing I feel like the first attempt I had old_exit.apply(process,arguments); would be the correct way and should not be calling my own code again, it does. I also tried used promises which didn't work (it didn't even throw an error for being resolved multiple times) and I tried using a boolean for if it had been set but that didn't work either. I finally even tried throwing an error after the callback finished but this forced process.exit to be called again after the error. Any ideas?
By process.exit time, you are too late to do anything async. I do not think you can get around the async part, as you apparently need to execute a shell command. You can listen for various exit signals, do your async stuff, then call process.exit yourself when you are ready. Note you will get a all signals until the process exists, so you will want to track the state to ensure your shell command is only run once. The following will work for the ctrl-c signal:
process.on('SIGINT', function() {
console.log('Received Signal');
setTimeout(function() {
console.log('Exit');
process.exit(1);
}, 10000);
});

Resources