NodeJS: Make it possible for user input when running child_process - node.js

I'm trying to create a Node script that will ask the user a few questions, save the input and using the answers run a mvn archetype:generate command to install their environment.
I got as far to where I get the Maven command running. But when Maven asks for user input for values such as groupId and `` I can enter the values, give an [enter] and that's where it stops.
It doesn't take input and process them. All it does is display it, as the CLI does, but doesn't accept them.
Here's a snippet of code with the values for user input pre-filled:
var spawn = require('child_process').spawn;
var answerCollection = {
"name": "nameOfMyArchetype", //answer of inquiry
"version": "1.2.3.4" //answer of inquiry
};
var cmd = "mvn";
var args = [
"archetype:generate",
"-DarchetypeArtifactId=" + answerCollection.name,
"-DarchetypeGroupId=com.backbase.expert.tools",
"-DarchetypeVersion=" + answerCollection.version
];
var runCmd = function(cmd, args, callback) {
var child = spawn(cmd, args);
child.stdin.pipe(process.stdin);
child.stdout.pipe(process.stdout);
child.stdout.on('end', function(res) {
console.log("stdout:end");
callback(res);
});
child.stderr.on('data', function(text) {
console.log("stderr:data");
console.log(data);
});
child.stderr.on('exit', function(data) {
console.log("stderr:exit");
console.log(data);
});
};
So far I've tried the above code with child_process and spawn = require('child_process').spawn('bash').
Question: Is there any other way to make sure I can trigger a script and if that returns with a prompt and asks for input I can type and enter and the script will continue?

From Facebook I got this tip to use cross-spawn, instead of child_process:
From Robert Haritonov:
Use cross-spawn: spawn('bower', bowerCommand, {stdio:'inherit'}).on('close', function () {});
This works perfectly well and provides exactly the behaviour I need.

Related

child_process.execSync(start "" "example.exe") freezing/locking console with executables have params option

When I try to execute an executable that have an option to have parameters. It will freeze the nodejs output and input until the executable is closed. Executables that do not need params will just run, and the nodejs console will not freeze/lock input nor output.
Example with param: test.exe -thisisaparam. Example without Params: test.exe.
Here is my code below. (Its a cli)
const cp = require('child_process');
let start = async function (start) {
let command = `start "" ${start}`;
cp.execSync(command);
console.log("Returning to menu in 10 seconds...")
setTimeout(() => {
run()
}, 10000);
};
Here is how I call the function.
async function startTest() {
await start("C:\users\user\downloads\test_param.exe")
}
Thanks, Kiefer.
Any command you run using execSync will be synchronous meaning it will wait for the command to exit and then returns the output.
If you don't need the output of the command and want to just start and detach you should use spawn with unref().
example:
const scriptPath = "C:\users\user\downloads\test_param.exe"
cp.spawn('start', [scriptPath], {detached: true, stdio: 'ignore'}).unref()

How can I handle an input prompt with node when executing a command via child_process?

For context, I'm on a Mac and I'm trying to script a 1Password CLI signin via their command-line tool. I'm trying to programmatically signing using a command that looks like:
op signin <signinaddress> <emailaddress> <secretkey> --output=raw
and I've tried with/without the --output=raw argument, but every time I simply get an error that looks like
[LOG] 2019/06/04 00:57:45 (ERROR) operation not supported on socket
child process exited with code 1
My initial hunch was that it had something to do with the command executions prompt displaying this special key character in the following image:
The relevant code is written in TypeScript and looks like this:
import { spawn } from 'child_process'
// ends up being `op signin <signinaddress> <emailaddress> <secretkey>`
const op = spawn(opExecutable, args);
let result: string | null = null
op.on('message', (message, sendHandle) => {
console.log('message', message, sendHandle)
});
op.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
if (data && typeof data.toString === 'function') {
result = data.toString()
}
});
op.on('close', (code, ...args) => {
console.log(`child process exited with code ${code}`, args);
});
Eventually, I'd like to run on all platforms and be able pass in stdin for the master password required to sign in, but I'm trying to figure out why my node app is crashing first :)
Apparently I was pretty close to a solution by using spawn, but I needed to specify configuration for stdio. Here's an example snippet of how I used spawn that worked for me:
const proc = spawn(
cmd, // the command you want to run, in my case `op`
args, // arguments you want to use with the above cmd `signin`, etc.
{
stdio: [
'inherit', // stdin: changed from the default `pipe`
'pipe', // stdout
'inherit' // stderr: changed from the default `pipe`
]
});

Use child_process.execSync but keep output in console

I'd like to use the execSync method which was added in NodeJS 0.12 but still have the output in the console window from which i ran the Node script.
E.g. if I run a NodeJS script which has the following line I'd like to see the full output of the rsync command "live" inside the console:
require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"');
I understand that execSync returns the ouput of the command and that I could print that to the console after execution but this way I don't have "live" output...
You can pass the parent´s stdio to the child process if that´s what you want:
require('child_process').execSync(
'rsync -avAXz --info=progress2 "/src" "/dest"',
{stdio: 'inherit'}
);
You can simply use .toString().
var result = require('child_process').execSync('rsync -avAXz --info=progress2 "/src" "/dest"').toString();
console.log(result);
Edit: Looking back on this, I've realised that it doesn't actually answer the specific question because it doesn't show the output to you 'live' — only once the command has finished running.
However, I'm leaving this answer here because I know quite a few people come across this question just looking for how to print the result of the command after execution.
Unless you redirect stdout and stderr as the accepted answer suggests, this is not possible with execSync or spawnSync. Without redirecting stdout and stderr those commands only return stdout and stderr when the command is completed.
To do this without redirecting stdout and stderr, you are going to need to use spawn to do this but it's pretty straight forward:
var spawn = require('child_process').spawn;
//kick off process of listing files
var child = spawn('ls', ['-l', '/']);
//spit stdout to screen
child.stdout.on('data', function (data) { process.stdout.write(data.toString()); });
//spit stderr to screen
child.stderr.on('data', function (data) { process.stdout.write(data.toString()); });
child.on('close', function (code) {
console.log("Finished with code " + code);
});
I used an ls command that recursively lists files so that you can test it quickly. Spawn takes as first argument the executable name you are trying to run and as it's second argument it takes an array of strings representing each parameter you want to pass to that executable.
However, if you are set on using execSync and can't redirect stdout or stderr for some reason, you can open up another terminal like xterm and pass it a command like so:
var execSync = require('child_process').execSync;
execSync("xterm -title RecursiveFileListing -e ls -latkR /");
This will allow you to see what your command is doing in the new terminal but still have the synchronous call.
Simply:
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
console.log(`Status Code: ${error.status} with '${error.message}'`;
}
Ref: https://stackoverflow.com/a/43077917/104085
// nodejs
var execSync = require('child_process').execSync;
// typescript
const { execSync } = require("child_process");
try {
const cmd = 'git rev-parse --is-inside-work-tree';
execSync(cmd).toString();
} catch (error) {
error.status; // 0 : successful exit, but here in exception it has to be greater than 0
error.message; // Holds the message you typically want.
error.stderr; // Holds the stderr output. Use `.toString()`.
error.stdout; // Holds the stdout output. Use `.toString()`.
}
When command runs successful:
Add {"encoding": "utf8"} in options.
execSync(`pwd`, {
encoding: "utf8"
})

Node JS: Executing command lines and getting outputs asynchronously

How can I run a command line and get the outputs as soon as available to show them somewhere.
For example if a run ping command on a linux system, it will never stop, now is it possible to get the responses while the command is still processing ?
Or let's take apt-get install command, what if i want to show the progress of the installation as it is running ?
Actually i'm using this function to execute command line and get outputs, but the function will not return until the command line ends, so if i run a ping command it will never return!
var sys = require('sys'),
exec = require('child_process').exec;
function getOutput(command,callback){
exec(
command,
(
function(){
return function(err,data,stderr){
callback(data);
}
}
)(callback)
);
}
Try using spawn instead of exec, then you can tap into the stream and listen to the data and end events.
var process = require('child_process');
var cmd = process.spawn(command);
cmd.stdout.on('data', function(output){
console.log(output.toString()):
});
cmd.on('close', function(){
console.log('Finished');
});
//Error handling
cmd.stderr.on('data', function(err){
console.log(err);
});
See the Node.js documentation for spawn here: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

How to interact with multiple console windows?

how can interact with multiple console windows, from one node.js script?
so far i have researched a bit, and not have found anything that covers my case.
What i want to accomplish is to have one main console window, which it reads my input,
1. action#1
2. action#2
> do 1 // select action
and it would redirect its output to another console window named as Logger which shows the stdout of the action that the user selected, but keeps the main "select action" console window clean.
well i manage to find a way around it, since i wanted to stay with node.js all the way.
start.js
var cp = require("child_process");
cp.exec('start "Logger" cmd /K node logger.js',[],{});
cp.exec("start cmd /K node startAdminInterface.js",[],{});
setTimeout(function(){process.exit(0);},2000);
logger.js
var net = require('net');
net.createServer(function (socket) {
socket.on('data',function(d){
console.log(": "+d.toString("utf8"));
});
socket.on('error',function(err){
console.log("- An error occured : "+err.message);
});
}).listen(9999);
startAdminInterface.js
var net = require("net");
var logger = net.connect(9999);
var readline = require('readline'),
rl = readline.createInterface(process.stdin,process.stdout);
rl.setPrompt('> ');
rl.prompt();
rl.on('line', function(line) {
logger.write(line);
rl.prompt();
}).on('close', function() {
process.exit(0);
});
bottom, line its a workaround not exactly what i was after, put i saw potential, on logger.js it could listen from multiple sources, which is an enormous plus in the application that i'm building.

Resources