console.log('spawn');
var spawn = require('child_process').spawn,
child = spawn('C:\\Program Files\\nodejs\\node.exe');
child.stdin.setEncoding('utf-8');
setInterval(function(){
child.stdin.write("console.log('Hey there')\n");
}, 100);
child.stdout.on('data', function(data){
console.log(data);
});
console.log('spawned');
It only reproduces
spawn
spawned
but it should also give me "Hey there" messages
The issue is that you're never telling node when your entire node script is complete (no child.stdin.end()). If you throw that in after you do all of your child.stdin.write()s, you will get output.
The other solution would be to force interactive mode (you will see the repl prompt and the result of every line though):
child = spawn('C:\\Program Files\\nodejs\\node.exe', ['-i']);
No matter what, you will probably want to make sure you stringify your data before you log it, otherwise you will see the Buffer pretty format.
The docs for child.stdin explain:
If the child is waiting to read all its input, it will not continue until this stream has been closed via end().
If you just want to confirm that communication to child.stdin is functioning, change your setInterval() to this instead:
setTimeout(function(){
child.stdin.write("console.log('Hey there')\n");
child.stdin.end();
}, 100);
After second write I get
Error: write after end
at writeAfterEnd (_stream_writable.js:159:12)
at Socket.Writable.write (_stream_writable.js:204:5)
at Socket.write (net.js:618:40)
at null._repeat (C:\wamp\www\decahost\index.js:18:14)
at wrapper as _onTimeout
child.stdin #
A Writable Stream that represents the child process's stdin. Closing this stream via end() often causes the child process to terminate.
In that case, you need to unbuffer STDIN or have a mechanism to flush it on demand. How to do that varies across OSes and external applications. I do not believe it is something you can handle within Node itself.
If you have unbuffer (part of expect) installed on your operating system, your code might look like this:
console.log('spawn');
var spawn = require('child_process').spawn,
child = spawn('unbuffer', ['-p', process.execPath]);
child.stdin.setEncoding('utf-8');
setInterval(function(){
child.stdin.write("console.log('Hey there')\n");
}, 100);
child.stdout.on('data', function(data){
console.log('DATA: ' + data.toString());
});
console.log('spawned');
@Trott Thanks for clarifying :) :+1: I might use pty.js module which uses winpty and writing to spawned process works on demand.
Its 2017, is there still no way to get the original example given above to work without closing the stdin with the end method? Just so one can write again and again?
@ohenepee https://github.com/nodejs/node/issues/2985#issuecomment-142297507 provides an example of a way to do it without calling end().
@Trott Yeah I did see that, except that I'm on Windows... I also thought uncork would do the trick since it flushes the buffer, but it doesn't
If your child is node, and you want to mimic REPL behavior with it, easy thing is to coalesce the streams with parent and force "-i" flag, as already pointed out. There is no visible benefit of interacting with a REPL child through an inactive parent.
If your child is node and you want to execute arbitrary commands from parent as and when it sends them, then a better thing to do would be to use the IPC channel with send call (originator) and message event (target) mechanism.
If your child is node and you want to receive the executable script from the parent as a one time job and then execute, then a better thing to do would be to write the content and end the stream as pointed out.
If your child is node and you want to execute a script whose origin is statically known, a better thing to do would be to pass the script as an argument to the child and thereby avoid writing to the child. This is applicable for non-node child as well which receives command line arguments.
If your child is node and you want to receive line-by-line commands from parent and then a better thing would be to put up readable event at the child end, rather than following REPL route. One caveat is that for large data chunks, you may not see them receiving in the manner you sent it - cut off at arbitrary intervals, decided internally by OS. So a close match for your case would be:
cat parent.js
var spawn = require('child_process').spawn;
var child = spawn('node', ['child.js']);
setInterval(function(){
child.stdin.write("console.log('Hey there')\n");
}, 1000);
child.stdout.on('data', function(data){
console.log(data.toString());
});
cat child.js
process.stdin.on('readable', () => {
var chunk = process.stdin.read();
eval(chunk.toString());
});
You could pipe a string stream (created with string-to-stream) in your child process' stdin writable stream with options {end: false}
Example:
let stringStreamCreator = require('string-to-stream');
let cp = << something that returns a ChildProcess >>
stringStreamCreator("line1\n").pipe(cp.stdin, { end: false});
stringStreamCreator("line2\n").pipe(cp.stdin, { end: false});
I'm using this approach to delay-send some commands to a child process cli app. Works with node v6.11.1.
Hope this helps! :)
Most helpful comment
You could pipe a string stream (created with
string-to-stream) in your child process' stdin writable stream with options{end: false}Example:
I'm using this approach to delay-send some commands to a child process cli app. Works with node v6.11.1.
Hope this helps! :)