如何在node.js中运行交互式shell命令?

时间:2022-08-03 21:58:00

I have to run some interactive shell command inside node.js. Lets our interactive shell be $ python:

我必须在node.js中运行一些交互式shell命令。让我们的交互shell是$ 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).

此代码不输出任何内容(但是stdout应该是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!

但如果是这样,就会有另一个问题:如何使它具有交互性?当我调用pythonChildProcess.stdin.end()时,这个过程就结束了。但我只是想结束stdin然后写下一个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.

UPD:如果我可以模拟按下enter按钮-我将能够交互地写入进程。但是在输入字符串的末尾添加\n并没有帮助。

2 个解决方案

#1


4  

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.

首先也是最重要的一点是,防止节点与其他交互shell进行交互的原因之一是,即使stdin看起来不像终端,子应用程序也必须保持其“交互”行为。这里的python知道它的stdin不是终端,所以它拒绝工作。可以通过向python命令添加-i标志来覆盖它。

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.

第二,正如您在更新中提到的,您忘记向流写入一个新的行字符,因此程序的行为就好像用户没有按Enter一样。是的,这是正确的方法,但是缺乏交互模式使您无法检索任何结果。

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.

您可以做一些事情,将多个输入发送到交互式shell,同时仍然能够逐个检索每个结果。这段代码将对冗长的输出产生抵制,在执行另一条指令之前,将它们累加到一个完整的行。同时还可以执行多个指令,如果它们不依赖于父进程的状态,这可能是更好的选择。可以*地尝试其他异步结构来实现您的目标。

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');

#2


3  

A tl;dr version of @E_net4's answer, for those who understand just by reading the code. For a detailed explanation, please do read his answer. He has described it well.

一个tl;dr版本的@E_net4的答案,对于那些仅仅通过阅读代码就能理解的人来说。要得到详细的解释,请阅读他的答案。他描述得很好。

var spawn = require('child_process').spawn

var p = spawn('node',['-i']);

p.stdout.on('data',function (data) {
    console.log(data.toString())
});

p.stdin.write('1 + 0\n');

Output:

输出:

> 
1

#1


4  

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.

首先也是最重要的一点是,防止节点与其他交互shell进行交互的原因之一是,即使stdin看起来不像终端,子应用程序也必须保持其“交互”行为。这里的python知道它的stdin不是终端,所以它拒绝工作。可以通过向python命令添加-i标志来覆盖它。

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.

第二,正如您在更新中提到的,您忘记向流写入一个新的行字符,因此程序的行为就好像用户没有按Enter一样。是的,这是正确的方法,但是缺乏交互模式使您无法检索任何结果。

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.

您可以做一些事情,将多个输入发送到交互式shell,同时仍然能够逐个检索每个结果。这段代码将对冗长的输出产生抵制,在执行另一条指令之前,将它们累加到一个完整的行。同时还可以执行多个指令,如果它们不依赖于父进程的状态,这可能是更好的选择。可以*地尝试其他异步结构来实现您的目标。

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');

#2


3  

A tl;dr version of @E_net4's answer, for those who understand just by reading the code. For a detailed explanation, please do read his answer. He has described it well.

一个tl;dr版本的@E_net4的答案,对于那些仅仅通过阅读代码就能理解的人来说。要得到详细的解释,请阅读他的答案。他描述得很好。

var spawn = require('child_process').spawn

var p = spawn('node',['-i']);

p.stdout.on('data',function (data) {
    console.log(data.toString())
});

p.stdin.write('1 + 0\n');

Output:

输出:

> 
1