Exit child_process which execute sh file - node.js

I use child_process to execute a sh file which download file with curl but when I use .exit() function the process didn't stop
const { spawn } = require('child_process');
let command = spawn('./download.sh', ["url", "name"])
setTimeout(() => {
command.kill()
}, 2000);
command.stdout.on('data', data => {
console.log("Data")
})
command.on('exit', (code, signal) => {
console.log("exit");
})
command.on('close', (code, signal) => {
console.log("close");
})
And this is my output
Data
Data
Data
Data
Exit
Data
Data
Data
...
This is my download.sh
curl {test url} --output test.mp4
But when I execute spawn with the curl command directly, the process stop so I don't understand why.
const { spawn } = require('child_process');
let command = spawn('curl', ["test url", "--output", "test.mp4"])
setTimeout(() => {
command.kill()
}, 2000);
command.stdout.on('data', data => {
console.log("Data")
})
command.on('exit', (code, signal) => {
console.log("exit");
})
command.on('close', (code, signal) => {
console.log("close");
})
And this is my output
Data
Data
Data
Data
Exit
Close
If someone has an idea

Since you mentioned that you receive all the data in both cases, there is no problem.
Processes are asynchronous, so there is no guarantee that you will receive exit before data is finished. exit simply means that your process has exited at that point (and thus, the download should be complete). If the process wrote something to standard output/standard error, that data might still be stored in some buffers until it is consumed.

Related

Communicating with java from typescript

I wrote java program that takes one argument and writes it.
Also, it reads lines and writes them.
Then, I compiled it and ran from Typescript (vscode extension).
const { spawn } = require('node:child_process');
const runJava = spawn('java', ['-cp', 'extension1/src', 'package.Main', 'aaa']);
runJava.stdout.on('data', (data: String) => {
console.log(`stdout: ${data}`);
});
runJava.stderr.on('data', (data: String) => {
console.error(`stderr: ${data}`);
});
runJava.on('close', (code: Number) => {
console.log(`child process exited with code ${code}`);
});
It writes 'aaa' to stdout successfully.
What do I need to do next to send some text to it?
I tried to write with echo, but that didn't work.

How to pipe text into command with promisified node exec

I am using node to execute a jar file that usually takes a CSV file as an input path.
I would like to try and circumvent writing the CSV file and pipe in the CSV as a string into the process if possible.
I have this working with execSync but I would prever to use exec wrapped with promisify
The problem is that exec does not have the input option like execSync so I can't pipe data into it. How do you get around this? Or is the best practice to wrap execSync in a Promise?
import {execSync} from 'child_process';
export const runJar = async (input: string, cwd: string) => {
const out = execSync(`java -jar model.jar`, {
cwd,
input,
})
return out.toString('utf-8');
};
Minimalistic example usage of a childs process stdio.
https://nodejs.org/dist/latest-v14.x/docs/api/child_process.html#child_process_child_process_exec_command_options_callback
const child_process = require("child_process");
const fs = require("fs");
// exec returns a child process instance
// https://nodejs.org/dist/latest-v14.x/docs/api/child_process.html#child_process_class_childprocess
const child = child_process.exec("cat");
// write to child process stdin
child.stdin.write("Hello World");
// to read/parse your csv file
//fs.createReadStream("./file.csv").pipe(child.stdin);
// listen on child process stdout
child.stdout.on("data", (chunk) => {
console.log(chunk);
child.kill();
});
To promisfy this, you can listen on the exit (status) on the child process and resolve or reject the promise based on the exit code:
child.on("close", (code) => {
if (code != 0) {
reject();
} else {
resolve();
}
});
Example given:
const readParseCSV = function (file = "./file.csv") {
return new Promise((resolve, reject) => {
const child = child_process.exec("java -jar model.jar");
fs.createReadStream(file).pipe(child.stdin);
let response = "";
// listen on child process stdout
child.stdout.on("data", (chunk) => {
response += chunk;
});
child.on("close", (code) => {
if (code != 0) {
reject();
} else {
resolve(response);
}
});
});
};
Im not sure if this works on windows the same way as on linux.

How to start a child process successfully in node without waiting for it to complete?

How do I ensure the child process has started without waiting for it to complete?
Here is my code.
const runMyProc = () => {
const cmd = "MyProc";
const MyProcProc = spawn(cmd);
if(!MyProcProc)
return false;
logger.info(`Started ${cmd} process with PID [${MyProcProc.pid}].`);
MyProcProc.on('error', (err) => {
logger.error(`Failed to run MyProc app ${err}`);
});
MyProcProc.on('close', (code) => {
logger.info(`child process close all stdio with code ${code}`)
});
MyProcProc.on('exit', (code) => {
logger.info(`Process exited with exit code ${code}`);
});
return true;
}

How to run long running commands with pipes using Node.js child_process spawn [Edit: Especially piping into grep]

Consider a simple example below where I'm printing the response from a long-running command as it sends.
const { spawn } = require('child_process');
const ping = spawn('ping', ['www.google.com']);
ping.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ping.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ping.on('close', (code) => {
if (code !== 0) {
console.log(`ping process exited with code ${code}`);
}
});
This works ok. But when I try to pipe this result to grep, it stops working.
See sample below
const { spawn } = require('child_process');
const ping = spawn('ping', ['www.google.com']);
const grep = spawn('grep', ['bytes'])
ping.stdout.on('data', (data) => {
grep.stdin.write(data)
});
ping.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ping.on('close', (code) => {
if (code !== 0) {
console.log(`ping process exited with code ${code}`);
}
grep.stdin.end();
});
grep.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
grep.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
grep.on('close', (code) => {
console.log(`grep process exited with code ${code}`);
});
The above code does not give me any results.
It looks like it is waiting for the first command to end before it pipes the result to the next.
I observed this by experimenting with the ls command.
Isn't the whole point of piping not to wait? Or am I missing something here?
Some variations I tried to no success:
ping.stdout.pipe(grep.stdin); instead of grep.stdin.write(data), although I don't believe there is much difference between the two.
const ping = spawn('sh', ['-c', 'ping www.google.com | grep bytes']); I have tried this with non-long-running commands and it works ok including the pipe and everything.
The problem is that grep block-buffers its output by default, so until several kilobytes of output are available, it won't send anything back to node. Note that if you waited long enough with your program as-is, you'd eventually see dozens of lines all suddenly returned at once. The --line-buffered option changes this, so do spawn('grep', ['--line-buffered', 'bytes']) instead of spawn('grep', ['bytes']).

How to kill node process within mocha test

Within my tests, I'm using require('child_process').exec to run "npm run start-app" which starts up a webpack-dev-server. After my tests have run I want to kill the process that I have started.
Currently I am able to successfully kill the process when running "mocha tests.js" directly. However, when I run the tests using "npm run tests" which calls "mocha tests.js", the process is not killed. Is this because the node process is blocking me from killing the process?
I am killing the process by discovering pids using ps-tree and using a kill -9 or taskkill depending on operating system.
test.after(function () {
psTree(appStartProcess.pid, function (err, children) {
if(/^win/.test(process.platform)) {
for(var i = 0; i < children.length; i++) {
exec('taskkill /pid ' + children[i].PID + ' /T /F');
}
} else{
cp.spawn('kill', ['-9'].concat(children.map(function (p) {
return p.PID;
})));
}
});
});
Any suggestions will be greatly appreciated !
You can use the ChildProcess returned by spawn after our test to kill it afterwards.
Given the following server to act as a running process:
#!/usr/bin/env node
require('http').createServer((_, res) => {
res.end('Hi from process')
}).listen(3000, console.log)
Your tests might look like this:
const { exec } = require('child_process')
// this would typically be done in a separate helper
// that mocha executes before your tests
before(function (done) {
// `this` is the shared mocha execution context
this.runningProcess = exec('test/server.js')
setTimeout(done, 500)
})
after(function () {
this.runningProcess.kill()
})
describe('my tests', () => {
it('properly initializes process', (done) => {
require('http').get({
port: 3000,
agent: false
}, (res) => {
let data = ''
res
.setEncoding('utf8')
.on('data', chunk => data += chunk)
.on('end', () => {
require('assert')(data === 'Hi from process')
done()
})
})
})
})
Alternately, you can use something outside of mocha (e.g. a custom script that starts your server, runs mocha, and then shuts your server down) but the concept is essentially the same.

Resources