简体   繁体   中英

How to get input in child process from custom node REPL

I am trying to create a node repl, and want to spawn a child process, which could take user input, then return control back to repl.

... contents of main repl file ( a.js )

!function(){
  var repl = require("repl"),
    spawn = require('child_process').spawn;

  function eval(cmd, context, filename, callback) {

    var child = spawn('node', ['./b.js']);

    child.stdout.pipe(process.stdout);
    child.stdin.pipe(process.stdin);

    child.on('close', function (code) {
      console.log('closing')
      callback(code, 'closing callback');
    });

  }

  repl.start({
    prompt: 'repl> ',
    input: process.stdin,
    output: process.stdout,
    eval: eval
  }).on('exit', function () {
    process.exit();
  });

}();

... contents of script called in child process ( b.js )

!function(){

  promptly = require('promptly');

  promptly.prompt('some question: ', function (err, answer) {
    console.log(answer);
    process.exit();
  });

}();

When I run node b all is as expected...

$ node b
some question: something
something
$

But called from repl, it gets into loop, where it keeps asking question, never returns to repl...

$ node a
repl> anything
some question: my answer
some question: another answer
some question: etc...

It seems that the stdin is not being captured in the child process, and it is still being captured in repl.

How do I pass control to child process, until it is complete, then pass back to parent?

nb I am open to using any other means of creating child process.

A few changes to a.js should make this work. You need to pipe stdin input from the main repl process to the stdin of the child process using process.stdin.pipe(child.stdin);

To get the data back from the child process you need to pipe child.stdout to process.stdin using child.stdout.pipe(process.stdout);

Perhaps there is a better way of tracking child processes. But I added a flag to mark if a child is spawned or not, and reset it when the child process closes.

Lastly, when the child process ends, resume the main processes stdin with process.stdin.resume(); , otherwise the repl will stop on the next input.

!function(){
  var repl = require("repl"),
    spawn = require('child_process').spawn;

  var child_spawned = false;
  function eval(cmd, context, filename, callback) {
    // don't start a new child process if one is already running
    if(child_spawned) return;

    var child = spawn('node', ['./b.js']);
    child_spawned = true;

    // pipe child output back up to parent process
    child.stdout.pipe(process.stdout);
    // pipe the main process input to the child process
    process.stdin.pipe(child.stdin);

    child.on('close', function (code) {
      console.log('closing')
      callback(code, 'closing callback');

      // mark child process as closed
      child_spawned = false;

      // resume the main process stdin after child ends so the repl continues to run
      process.stdin.resume();
    });

  }

  repl.start({
    prompt: 'repl> ',
    input: process.stdin,
    output: process.stdout,
    eval: eval
  }).on('exit', function () {
    process.exit();
  });

}();

I solved this by using a combination of spawnSync (to simplify flow) and { stdio: 'inherit' } as passed option...

!function(){
  var repl = require("repl"),
    spawnSync = require('child_process').spawnSync;

  function eval(cmd, context, filename, callback) {
    spawnSync('node', ['./b.js'], { stdio: 'inherit' });
    callback();
  }

  repl.start({
    prompt: 'repl> ',
    input: process.stdin,
    output: process.stdout,
    eval: eval
  }).on('exit', function () {
    process.exit();
  });

}();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM