Executing shell command using child process - node.js

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.

Related

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

Pass : (Colon) into child_process SPAWN arg

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.

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

How to launch Chrome from node.js as a separate process and return immediately?

The objective is to launch Chrome or Chromium from nodejs using child_process, and return immediately, similar to how the windows START command launches a completely separate process and the calling process can exit immediately.
The { shell: true } option for child_process.execFile() almost does the job, in that it separates the node process from the Chrome process; I can exit the main nodejs process with Ctrl+C, and the launched browser remains open. Without that option, they remain married and ^C in node closes Chrome.exe.
What I need, however, is for node to exit completely after launching Chrome. There is apparently no adverse effects of pressing ^C. So if ^C is possible to exit node, why won't it exit immediately? I suspect until the chrome process object is destroyed, node can't exit in good conscience.
What is interesting: If the same Chrome.exe happens to be running already, the "new" Chrome I am launching starts a new tab or Window in that existing chrome and exits. In that case the nodejs script exits immediately.
const child_process = require('child_process');
let ex = "C:\\PROGRA~2\\Google\\Chrome\\APPLIC~1\\chrome.exe";
let chrome = child_process.execFile(ex, [
// tried various Chromium switches here but nothing helped
], {
shell: true, // this spawns a separate process but node won't exit
} , function(err, data) {
console.log(err)
console.log(data.toString());
});
chrome.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
chrome.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
chrome.on('exit', function (code) {
console.log('child process exited with code ' + code);
// chrome.kill();
});
Expected: Since the nodejs can be killed with ^C, why does it even continue running / blocking? I would expect it to exit after it launched Chrome.exe.
However, in actuality, nodejs blocks until I exit Chrome, or press ^C.
I also tried without callback function and .stdout, .stderr and .on hooks -- they don't seem to help or hurt. Node always blocks till I ^C or the child process, albeit separate, exits.
Posting r3wt's comment as answer here: use process.exit(0) to exit your script with non-error. it doesn't exit immediately because there are running EventEmitter(s)
const path = require('path');
const spawn = require('child_process').spawn;
chrome = spawn(
path.join(__dirname, '/path_to_chrome'),
[ ... your chrome switches here],
{
shell: true, // use to run in a shell if you need
detached: true, // important
stdio: 'ignore' // important
}
);
chrome.unref(); // this one is important too
To achieve both: possibility to exit immediately and keep subprocess running, - follow the instructions below.
Use subProcess.unref() which is explained here
It prevents parent from waiting for a given subprocess to exit.
One more thing is detached option together with stdio:'ignored' which allow you to keep your subprocess alive even after you stop nodejs process.

Is it possible to stream sdtout/stderr output from node-ssh exec()

Using node-ssh JS library, one can execute remote commands. Is it possible to get streamed output from a long-running command, as it is generated? (Without waiting the command to end).
The example in the project page suggests you can only get stdout and stderr once the command is done:
ssh.execCommand('hh_client --json', { cwd:'/var/www'})
.then(function(result) {
console.log('STDOUT: ' + result.stdout)
console.log('STDERR: ' + result.stderr)
})

Resources