Не получать стандартный вывод из порожденного процесса nodejs

Я пытаюсь заставить nodejs взаимодействовать с adventure, старой текстовой игрой. Идея состоит в том, чтобы открыть приключение как дочерний процесс, а затем запустить игру, записав в его stdin и поместив прослушиватель событий в stdout.

Когда игра запускается, она печатает инициал:

Добро пожаловать в приключение!! Хотите инструкции?

Итак, чтобы проиллюстрировать мою проблему, у меня есть экземпляр nodejs+express с:

var childProcess = require('child_process');
var spawn = childProcess.spawn;
var child = spawn('adventure');

console.log("spawned: " + child.pid);

child.stdout.on('data', function(data) {
  console.log("Child data: " + data);
});
child.on('error', function () {
  console.log("Failed to start child.");
});
child.on('close', function (code) {
  console.log('Child process exited with code ' + code);
});
child.stdout.on('end', function () {
  console.log('Finished collecting data chunks.');
});

Но когда я запускаю сервер, текст из игры не доходит до прослушивателя событий:

spawned: 24250

Это все, что я получаю. Даже слушатель child.stdout.on никогда не вызывается. Почему не подхватывается эта начальная строка из игры?

Если я добавлю следующую строку к приведенному выше блоку javascript, то сразу появится вывод программы. Итак, adventure запускается, и теперь я могу заставить его активировать прослушиватель событий child.stdout.on... но это также завершает дочерний процесс, что противоречит цели чтения и записи в него.

...
child.stdout.on('end', function () {
  console.log('Finished collecting data chunks.');
});
child.stdin.end();

Теперь вывод:

spawned: 28778  
Child data:
Welcome to Adventure!!  Would you like instructions?
user closed input stream, quitting...

Finished collecting data chunks.
Child process exited with code 0

Я уверен, что это тривиальная оплошность с моей стороны, но я ценю любую помощь в этом.


person ctag    schedule 16.09.2015    source источник


Ответы (3)


Просмотрев документацию Nodejs еще несколько раз, я убедил себя, что либо упускаю что-то очень важное, либо команда spawn работает неправильно. Поэтому я создал выпуск github.

И ответ был немедленно опубликован: дочерний процесс не может буферизовать вывод, если вам нужен быстрый доступ.

Итак, чтобы достичь того, что я изначально искал:

var childProcess = require('child_process');
var spawn = childProcess.spawn;
var child = spawn('unbuffer', 'adventure');

console.log("spawned: " + child.pid);

child.stdout.on('data', function(data) {
  console.log("Child data: " + data);
});
child.on('error', function () {
  console.log("Failed to start child.");
});
child.on('close', function (code) {
  console.log('Child process exited with code ' + code);
});
child.stdout.on('end', function () {
  console.log('Finished collecting data chunks.');
});

Функциональное отличие заключается в использовании unbuffer, команды, которая отключает буферизацию вывода.

person ctag    schedule 17.09.2015
comment
В моем случае у меня нет команды unbuffer в моей системе, но параметр {shell: true} для параметра options spawn произвел вывод. Также в Linux есть команда stdbuf для управления буферизацией, но в моем случае она ничего не меняет. (Я создавал компилятор java из пакета openjdk.) - person shitpoet; 09.02.2020

Почему не подхватывается эта начальная строка из игры?

У меня была такая же проблема в проекте, который вызывал скомпилированную программу C++ из node.js. Я понял, что проблема была в скомпилированной программе C++: я не очищал поток stdout. Добавление fflush(stdout); после печати строки решило проблему. Надеюсь, у вас все еще есть доступ к исходникам игры!

person G. E.    schedule 08.02.2019
comment
Выяснилось, что буферизация происходила на стороне node.js, но проблема была в буферизации. Спасибо, что нашли время ответить. - person ctag; 12.02.2019

Переданный data является типом буфера, а не строкой. Следовательно, вам нужен декодер для чтения этого буфера и последующего ведения журнала.

Вот как это сделать.

var StringDecoder = require('string_decoder').StringDecoder;
var decoder = new StringDecoder('utf8');

child.stdout.on('data', function (data) {
    var message = decoder.write(data);
    console.log(message.trim());
});
person Community    schedule 16.09.2015
comment
Спасибо за совет по получению полезного вывода от data. Моя проблема в том, что child.stdout.on() вообще никогда не вызывается, хотя я знаю, что подпроцесс записывает вывод. Это так, даже если содержимое состоит только из console.log("Something received from child"); и вообще не имеет отношения к data. - person ctag; 16.09.2015
comment
Что произойдет, если вы откроете пустой терминал и наберете adventure? Он что-то печатает? Мне любопытно, успешно ли узел запустил adventure или нет. Также попробуйте использовать абсолютный путь. - person ; 16.09.2015
comment
Текст в кавычках в вопросе Добро пожаловать в приключение... печатается сразу после запуска adventure. У меня есть nodejs, чтобы распечатать PID, чтобы я мог проверить, и приключение действительно работает. Я продолжу и добавлю абсолютный путь к имени программы, это кажется мне хорошей идеей. - person ctag; 16.09.2015
comment
Потому что если adventure не находится на вашем пути, то запустить его как spawn('adventure') не получится - person ; 16.09.2015
comment
@ user528315 - В моем случае я не могу зайти в раздел child.stdout.on(...){ ... }. В чем может быть проблема. Однако элемент управления переходит к «выходу», и я могу получить код выхода как 0 (успех). Я просто вызываю сценарий оболочки, который повторяет только одну строку. - person AKS; 28.06.2016