19

I'm running Windows 10, and I have a program, let's call it program, that can be run from the command line. When run, it responds to commands the user enters. The user enters a command, presses the return key, and the program prints a response. I did not make this program and do not have the source, so I cannot modify it.

I want to run this program from within Node.js, and have my Node.js program act as the user, sending it commands and getting the responses. I spawn my program like this:

var spawn = require('child_process').spawn;
var child = spawn('program');

child.stdout.on('data', function(data) {
    console.log(`stdout: ${data}`);
});

Then I attempt to send it a command, for example, help.

child.stdin.write("help\n");

And nothing happens. If I manually run the program, type help, and press the return key, I get output. I want Node.js to run the program, send it input, and receive the output exactly as a human user would. I assumed that stdin.write() would send the program a command as if the user typed it in the console. However, as the program does not respond, I assume this is not the case. How can I send the program input?

I've seen many similar questions, but unfortunately the solutions their authors report as "working" did not work for me.

gfrung4
  • 1,658
  • 3
  • 14
  • 22
  • 1
    Try the same with vim. I mean write this `var child = spawn('vim');` and `child.stdin.write("q\n");`. This should run `vim` and exit. Does it work? – FreeLightman Mar 06 '18 at 13:14
  • just in case someone is using IPC and JSON serialization, what I observed is that it sends message from Child-to-Parent but not Parent-to-Child using `send()` function. When I used `fork` instead of `spawn`, it started sending messages in both ways. – Ankur Thakur Apr 27 '23 at 14:05

3 Answers3

3

This worked for me, when running from Win10 CMD or Git Bash:

console.log('Running child process...');
const spawn = require('child_process').spawn;
const child = spawn('node');
// Also worked, from Git Bash:
//const child = spawn('cat');

child.stdout.on('data', (data) => {
    console.log(`stdout: "${data}"`);
});

child.stdin.write("console.log('Hello!');\n");
child.stdin.end(); // EOF

child.on('close', (code) => {
    console.log(`Child process exited with code ${code}.`);
});

Result:

D:\Martin\dev\node>node test11.js
Running child process...
stdout: "Hello!
"
Child process exited with code 0.

I also tried running aws configure like this, first it didn't work because I sent only a single line. But when sending four lines for the expected four input values, it worked.

Maybe your program expects special properties for stdin, like being a real terminal, and therefore doesn't take your input?
Or did you forget to send the EOF using child.stdin.end();? (If you remove that call from my example, the child waits for input forever.)

mh8020
  • 1,784
  • 1
  • 11
  • 14
1

Here is what worked for me. I have used child_process exec to create a child process. Inside this child process Promise, I am handling the i/o part of the cmd given as parameter. Its' not perfect, but its working.

Sample function call where you dont need any human input.

executeCLI("cat ~/index.html");

Sample function call where you interact with aws cli. Here

executeCLI("aws configure --profile dev")

Code for custom executeCLI function.

var { exec } = require('child_process');
async function executeCLI(cmd) {
  console.log("About to execute this: ", cmd);
  var child = exec(cmd);
  return new Promise((resolve, reject) => {
    child.stdout.on('data', (data) => {
      console.log(`${data}`);
      process.stdin.pipe(child.stdin);
    });

    child.on('close', function (err, data) {
      if (err) {
        console.log("Error executing cmd: ", err);
        reject(err);
      } else {
        //   console.log("data:", data)
        resolve(data);
      }
    });
  });
}
  • how is this sending data to the spawned process? You simply exec a command. We are looking for a way to have the process running with spawn and then execute the command. – The Fool May 22 '20 at 19:08
  • @TheFool When we pass the parameter "cmd" as a string var; we can pass any no of arguments or flags within the string. e.g given "aws configure --profile dev" or "/scripts/blabla.sh -a 1 -b 2" – Baibhav Vishal May 26 '20 at 09:15
  • you don't understand what im saying and you don't understand the question. Yes you do execute a command but you don't have the process running in the background first. You start the process, capture its output and end the process. That is NOT the question. – The Fool May 26 '20 at 10:09
  • 1
    the answer by the way would be by child_process.spawn and then proc.write(data) at any time to the running process. – The Fool May 26 '20 at 10:11
-4

Extract the user input code from browser and save that code into a file on your system using fs module. Let that file be 'program.cpp' and save the user data input in a text file.

As we can compile our c++ code on our terminal using g++ similarly we will be using child_process to access our system terminal and run user's code.

execFile can be used for executing our program

var { execFile } = require('child_process');

execFile("g++", ['program.cpp'], (err, stdout, stderr) => {
    if (err) {
        console.log("compilation error: ",err);
    } else{
        execFile ('./a.out' ,['<', 'input.txt'], {shell: true}, (err, stdout, stderr) => {
            console.log("output: ", stdout);         
        })
    }
})

In this code we simply require the child_process and uses its execFile function.

  • First we compile the code present in program.cpp, which creates a default a.out as output file
  • Then we pass the a.out file with input that is present in input.txt

Hence you can view the generated output in your terminal and pass that back to the user.

for more details you can check: Child Processes

Alex Svetkin
  • 1,339
  • 14
  • 17