Adding execArgs to Node executable using child_process.spawn - node.js

I am wondering what the correct way is to add "execArgs" to a Node process -
we have:
const cp = require('child_process');
const n = cp.spawn('node', ['some-file.js'], {});
but what if I want to add an execArg like so:
const n = cp.spawn('node --harmony', ['some-file.js'], {});
I don't think that is the right way to do it, and the docs don't seem to demonstrate this?
Is this the correct way?
const n = cp.spawn('node', ['--harmony','some-file.js'], {});

According to the docs for child_process.spawn() it clearly states that args is an array of string arguments that is passed in as the second argument.
The child_process.spawn() method spawns a new process using the given command, with command line arguments in args. If omitted, args defaults to an empty array.
A third argument may be used to specify additional options, with these defaults:
{ cwd: undefined, env: process.env }
Example of running ls -lh /usr, capturing stdout, stderr, and the exit code:
const spawn = require('child_process').spawn;
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
Based on the above pulled from the child_process docs, the below would be correct.
const n = cp.spawn('node', ['--harmony','some-file.js']);

Related

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

Data event not firing for child_process spawn when custom bash script echos something

I have been trying to call an custom bash script using child_process spawn module from my node application and I want to capture the stdout echoed by the shell script. I have tried listening to 'data' event but nothing helps. Here is the code:enter code here
const { spawn } = require('child_process');
const subprocess = spawn('.\\start_recording.sh', [], {shell:true});
subprocess.stdout.on('data', (data) => {
data = data.toString();
console.log(`Received chunk ${data}`);
});
I even tried using:
subprocess.stderr.pipe(process.stderr, {end: false});
subprocess.stdout.pipe(process.stdout, {end: false});
Still 'data' event not get listened.
Here is my bash script start_recording.sh:
#!/bin/bash
echo something
according to nodejs documentation, child_process is asynchronous so try to wrap it in an async function .
https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
try this:
async ()=>{
let spawExample = await subprocess.stdout.on('data', (data) => {
data = data.toString();
console.log(`Received chunk ${data}`);
return data
});
}

execute npm script(s) of a project from a different project/ repo

I need to execute one of the npm scripts from a different node project.
What I tried:
const installingf = spawn('cd', ['[path to directory]', '&&', 'npm', 'run', 'test']);
installingf.on('message', (data) => {
console.log(`stdout: ${data}`);
});
installingf.on('error', (data) => {
console.log(`stderr: ${data}`);
});
installingf.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
but it does not work. It shows child process exited with code 0 .
I tried mkdir to test if it gets executed but there are no directories created, it exits with code 0 but script is not executed.
Please help me figure out how to do this and if I am doing something wrong.
Maybe is because you're trying to execute one command, but you don't run the cmd.
The cd command doesn't exist on windows as long as you didn't create an alias. So you would need to run 'cmd' with arguments to run.
const spawn = require('child_process').spawn;
const bat = spawn('cmd.exe', ['/c','calc.exe']);
bat.stdout.on('data', (data) => {
console.log(data);
});
bat.stderr.on('data', (data) => {
console.log(data);
});
bat.on('closed', (code) => {
alert(`Child exited with code ${code}`);
});
IMPORTANT to read.

How to pass parameter from node child process to execute commands in batch file?

I need to put all the commands in a batch file (test.cmd) with some logic, for example:
IF condition1 (c:\Windows\System32\schtasks.exe /Create ...)
Else (c:\Windows\System32\schtasks.exe /delete ...)
If remove the if-else statement, and only leave one command in test.cmd, by using code like this can execute the command:
exec('some-path/test.cmd', (error, stdout, stderr) => {
if (error) {
console.log(error);
return;
}
console.log(stdout);
});
If add if-else statement back, does anyone know how can I pass parameter from node.js exec() function? In the terminal, it is easy to pass parameters like "test.cmd para1".
Yo can use node spawn.
Example variables
const spawn = require('child_process').spawn;
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
documentation:
https://nodejs.org/api/child_process.html

Resources