Reputation: 58611
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?
n.b. I am open to using any other means of creating child process.
Upvotes: 2
Views: 2204
Reputation: 58611
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();
});
}();
Upvotes: 0
Reputation: 1095
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();
});
}();
Upvotes: 2