I have to run some interactive shell command inside node.js. Lets our interactive shell be $ python
:
var cp = require('child_process');
var pythonChildProcess = cp.spawn('python');
pythonChildProcess.stdout.on("data", function(data) {
console.log('data successfully written!', data); // never outputs anything
});
pythonChildProcess.stdin.write('1 + 1');
pythonChildProcess.stdin.end();
This code does not output anything (but stdout should be 2
).
But if it would, there will be another problem: how to make it interactive? The process ends when I call pythonChildProcess.stdin.end();
! But I just wanted to end stdin and write next stdin!
UPD:
If I could emulate pressing of enter
button - I would be able to interactively write to the process. But adding \n
to the end of the input string does not help.
Node. js can run shell commands by using the standard child_process module. If we use the exec() function, our command will run and its output will be available to us in a callback. If we use the spawn() module, its output will be available via event listeners.
The Node. js Read-Eval-Print-Loop (REPL) is an interactive shell that processes Node. js expressions. The shell reads JavaScript code the user enters, evaluates the result of interpreting the line of code, prints the result to the user, and loops until the user signals to quit.
The exec() function in Node. js creates a new shell process and executes a command in that shell. The output of the command is kept in a buffer in memory, which you can accept via a callback function passed into exec() . Let's begin creating our first child processes in Node.
Starting the REPL is simple - just run node on the command line without a filename. It then drops you into a simple prompt ('>') where you can type any JavaScript command you wish. As in most shells, you can press the up and down arrow keys to scroll through your command history and modify previous commands.
This works great for me:
const { spawn } = require('child_process')
const shell = spawn('sh',[], { stdio: 'inherit' })
shell.on('close',(code)=>{console.log('[shell] terminated :',code)})
First and foremost, one of the things preventing node from interfacing with other interactive shells is that the child application must keep its "interactive" behavior, even when stdin
doesn't look like a terminal. python
here knew that its stdin
wasn't a terminal, so it refused to work. This can be overridden by adding the -i
flag to the python command.
Second, as you well mentioned in the update, you forgot to write a new line character to the stream, so the program behaved as if the user didn't press Enter. Yes, this is the right way to go, but the lack of an interactive mode prevented you from retrieving any results.
Here's something you can do to send multiple inputs to the interactive shell, while still being able to retrieve each result one by one. This code will be resistant to lengthy outputs, accumulating them until a full line is received before performing another instruction. Multiple instructions can be performed at a time as well, which may be preferable if they don't depend on the parent process' state. Feel free to experiment with other asynchronous structures to fulfil your goal.
var cp = require('child_process');
var childProcess = cp.spawn('python', ['-i']);
childProcess.stdout.setEncoding('utf8')
var k = 0;
var data_line = '';
childProcess.stdout.on("data", function(data) {
data_line += data;
if (data_line[data_line.length-1] == '\n') {
// we've got new data (assuming each individual output ends with '\n')
var res = parseFloat(data_line);
data_line = ''; // reset the line of data
console.log('Result #', k, ': ', res);
k++;
// do something else now
if (k < 5) {
// double the previous result
childProcess.stdin.write('2 * + ' + res + '\n');
} else {
// that's enough
childProcess.stdin.end();
}
}
});
childProcess.stdin.write('1 + 0\n');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With