Node and talking to child processes - node.js

Context
Writing a serial monitor to listen to an Arduino isn't hard. Starting it with command line params for port name and baudrate is straightforward, and thus it's not complicated to configure and launch the monitor from Node:
child_process.exec("./monitor COM6 115200");
This question concerns exec, fork and spawn which are similar but with subtleties I do not yet grasp. As well as launch with parameters, I need to
Capture the output so I can display it in a window
Kill the child process
to re-launch with different parameters
to flash the Arduino and then re-launch after it reboots
I used netcore to write a console app that takes two command line arguments, then connects and listens, echoing to its stdout. I chose netcore because it can run on all three platforms.
Questions
Which should I use of exec, fork and spawn?
How do I kill the child process?
How do I capture the stdout of the child process?
Node documentation talks about a kill method on a subprocess object. The sample code on that page implies this object is returned by spawn but when I use spawn it seems to fail silently. That or it's working but I don't know what I'm doing, which is why I'm posing this question.
All of this Node code will be part of a VSCode extension, so if you also know about those I'd like to pipe stdout to a VSCode OutputChannel if that's possible.

With spawn your are able to listen to stdout.
and then kill process with kill()
From official NodeJS docs:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
// some logic
ls.kill()
EDIT: a more specific example:
// server.js --------------
const http = require('http');
const server = http.createServer(
(function() {
console.log('Initializing server');
return (req, res) => {
res.end('Hello World');
};
})()
);
server.listen(8080, () => console.log('Server is up on port ' + 8080));
// spawn.js --------------
const { spawn } = require('child_process');
const child = spawn('node', ['./server.js']);
child.stdout.on('data', data => console.log(data.toString()));
child.stderr.on('data', data => console.log('Error: ' + data));
child.on('close', code => console.log(`Process ended with code ${code}`));
child.on('error', err => console.log(err));
setTimeout(() => child.kill(), 2000);

Related

Child process function not being triggered with no errors

I'm working on a Node.js app utilizing Electron. I need to access a executable in a certain directory and determine its output. The executable is a simple console application. I read the docs on Child Process and tried to use execFile. However, the callback function doesn't seem to execute.
Here's my code at the moment:
var exec = require('child_process').execFile
exec('E:/SteamLibrary/steamapps/common/GarrysMod/bin/gmad.exe', [], function(err, data) {
console.log(err);
console.log(data);
});
How could I go about fixing this?
Youre using windows so execFile() wouldnt work. It stated on docs.. for convenient I use docs example here with litle change.
const { spawn } = require('child_process');
const bat = spawn('C/steam/steam.exe');
bat.stdout.on('data', (data) => {
console.log(data.toString());
});
bat.stderr.on('data', (data) => {
console.log(data.toString());
});
bat.on('exit', (code) => {
console.log(`Child exited with code ${code}`);
});

NodeJS Child Process stdout is not live but works fine when the command is executed in terminal

I am executing a command and listening to its stdout using spawn like below
const { spawn } = require('child_process');
const childProcess = spawn('RV', ["...jpg"]);
child.stdout.on('data', function(data) {
console.log(data.toString());
})
child.stderr.on('data', function(data) {
console.log("error", data.toString());
})
child.on('error', function(err) {
console.log("Error", err);
})
child.on('exit', function(code, signal) {
console.log("Child process exited")
});
Child process stdout is not live. If i close the child process, it prints the remaining stdout. I understand this is because the stdout is buffered till it receives a new line.
Nodejs always cann't capture child process's stdout data completely, unless child process fllush(stdout)
But if i execute the same command in terminal, it just shows all the stdout logs without the need to close the subprocess. If this is an issue with the application i am calling then it shouldn't work with terminal right?
Is there an option in nodejs's childprocess to execute the command just like executing in terminal?

Can't get websocket client to run as child process

I'm looking to build a project based on node that will have a few different websocket connections, I'm fine with the code itself for what I want to do but can't seem to get my head around how to start the websocket modules (code already written and moved to a file named ws.js) to run from the main server.js file.
I have tried spawning as a child process but just get
Error: spawn ws.js ENOENT
I have removed all other code from each file to prevent hidden errors, content of the files are below.
Server.js
var child = require('child_process').spawn('node', ['ws.js']);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
child.on('exit', function (code) {
console.log('child process exited with code ' + code);
});
setTimeout(function() {
child.stdin.write('echo %PATH%');
}, 2000);
ws.js
const Gdax = require('gdax');
const publicClient = new Gdax.PublicClient();
const websocket = new Gdax.WebsocketClient(['BTC-USD', 'ETH-USD']);
websocket.on('message', data => {
/* work with data */
console.log("data received");
});
websocket.on('error', err => {
/* handle error */
});
websocket.on('close', () => {
/* ... */
});
EDIT --------------------------
Thanks for the response below from Elliot server.js now runs without error however the ws.js child never writes to the console so is either not running or failing silently. Any help is appreciated on getting this working.
Cheers
For the console logs, you have to keep in mind that a child process is a new process and does not share a console. You can make it work with spawn(), however, since you are using a child process to execute a nodeJs file, I recommend using child_process.fork() instead of spawn. It is similar to spawn but is specifically used for other nodeJs processes. The benefit is that it makes it much easier for the two processes to communicate.
With that in mind, you can make the following updates:
Server.js:
var child = require('child_process').fork('./ws.js');
child.on('message', function (data) {
console.log('stdout: ' + data);
});
child.on('error', function (data) {
console.log('stderr: ' + data);
});
child.on('exit', function (code) {
console.log('child process exited with code ' + code);
});
ws.js
const Gdax = require('gdax');
const publicClient = new Gdax.PublicClient();
const websocket = new Gdax.WebsocketClient(['BTC-USD', 'ETH-USD']);
websocket.on('message', data => {
/* work with data */
process.send("data received");
});
websocket.on('error', err => {
/* handle error */
});
websocket.on('close', () => {
/* ... */
});
process.send() sends data to its parent process, where a listener (i.e.
child.on('message', callback)) is waiting to receive it.

Executing shell scripts that take long time using child process module in nodejs

Below is my code written to execute a script called mqValidation.sh in UNIX by sending an AJAX request. This spawns a new child process and when the child process exits, it sends a text file response which obviously contains the output of the script. But, the issue I'm facing here is, as the script takes long time to finish, a new child process is being spawned before the first child process exits, which means that the script is getting executed again.
var express = require('express');
var app = express();
var cp = require('child_process');
app.post('/triggerMQ', function(req,res){
ls = cp.spawn('./mqValidation.sh',req.body.envs,{cwd:"./MQValidation"});
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('exit', (code) => {
console.log("Child process exited");
res.sendFile(__dirname+'/MQValidation/html.txt');
});
});
app.listen(8000, function(){
console.log("app listening at port 8000.....");
});
Please help!!
Increasing timeout on the server side resolved the issue. Looks like after 120 seconds, the browser is sending another request which is triggering the script again.

Reading from a stdout in real time using node.js

I have a problem, I need to read from a console output in real time. I have a file that I need to execute, tried doing something like this test.exe > text.txt but when I try to read while exe file is running I can't see anything until exe finishes and write all lines in the same time. I need to do this using node.js
You should be able to use child_process.spawn() to start the process and read from its stdout/stderr streams:
var spawn = require('child_process').spawn;
var proc = spawn('test.exe');
proc.stdout.on('data', function(data) {
process.stdout.write(data);
});
proc.stderr.on('data', function(data) {
process.stderr.write(data);
});
proc.on('close', function(code, signal) {
console.log('test.exe closed');
});
test.exe probably buffers it's output.
Disable buffering on redirected stdout Pipe (Win32 API, C++)
windows console program stdout is buffered when using pipe redirection
Python C program subprocess hangs at "for line in iter"
You can try to run it with spawn, or with a pseudo tty
const spawn = require('child_process').spawn;
const type = spawn('type.exe');
type.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
type.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
type.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});

Resources