Узел.JS порождает дочерний процесс и получает вывод терминала в реальном времени


У меня есть скрипт, который выводит "привет", спит на секунду, выводит "привет", спит в течение 1 секунды, и так далее и так далее. Теперь я думал, что смогу решить эту проблему с помощью этой модели.

var spawn = require('child_process').spawn,
temp    = spawn('PATH TO SCRIPT WITH THE ABOVE BEHAVIOUR');

temp.stdout.pipe(process.stdout);

теперь проблема в том, что задача должна быть завершена для того, чтобы вывод будет отображаться. Насколько я понимаю, это связано с тем, что вновь порожденный процесс берет на себя контроль выполнения. Очевидно, узел.js не поддерживает потоки, поэтому любые решения? Моя идея была чтобы, возможно, запустить два экземпляра, первый из них для конкретной цели создания задачи и передать его вывод в процесс второго экземпляра, учитывая, что это может быть достигнуто.

7 57

7 ответов:

Я все еще получаю мои ноги мокрые с узлом.JS, но у меня есть несколько идей. во-первых, я считаю, что вы должны использовать execFile вместо spawn;execFile - когда у вас есть путь к скрипту, а spawn предназначен для выполнения известной команды этого узла.js может решить против вашего системного пути.

1. обеспечивает обратный вызов для обработки буферизованного вывода:

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], function(err, stdout, stderr) { 
    // Node.js will invoke this callback when the 
    console.log(stdout); 
});  

2. Добавить прослушиватель для ребенка процесса стандартный вывод поток (9thport.net)

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3' ]); 
// use event hooks to provide a callback to execute when data are available: 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

кроме того, есть варианты, с помощью которых вы можете отсоединить порожденный процесс от управляющего терминала узла, что позволит ему работать асинхронно. Я еще не проверял это, но есть примеры в API docs что-то вроде этого:

child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], { 
    // detachment and ignored stdin are the key here: 
    detached: true, 
    stdio: [ 'ignore', 1, 2 ]
}); 
// and unref() somehow disentangles the child's event loop from the parent's: 
child.unref(); 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

теперь это намного проще (2 года спустя)!

Spawn возвращает a childObject, который вы можете тогда прослушивать события С. События таковы:

  • Класс: ChildProcess
    • событие: 'ошибка'
    • событие: 'выход'
    • событие: 'закрыть'
    • событие: 'disconnect'
    • событие: 'сообщение'

есть также куча объекты childObject, являются:

  • Класс: ChildProcess
    • ребенок.stdin
    • ребенок.стандартный вывод
    • ребенок.поток stderr
    • ребенок.stdio
    • ребенок.пид
    • ребенок.подключено
    • ребенок.убить([сигнал])
    • ребенок.отправить (сообщение [, sendHandle] [, обратный вызов])
    • ребенок.отключить ()

смотрите дополнительную информацию здесь о childObject: https://nodejs.org/api/child_process.html

Итак, объединив все это, вы получите:

var spawn = require('child_process').spawn;
var child = spawn('node ./commands/server.js');
child.stdout.on('data', function(data) {
    console.log('stdout: ' + data);
    //Here is where the output goes
});
child.stderr.on('data', function(data) {
    console.log('stderr: ' + data);
    //Here is where the error output goes
});
child.on('close', function(code) {
    console.log('closing code: ' + code);
    //Here you can get the exit code of the script
});

и вот как вы можете получить выход с узлом!

У меня были небольшие проблемы с получением выходных данных журнала из команды "npm install", когда я породил npm в дочернем процессе. Ведение журнала зависимостей в реальном времени не отображается в родительской консоли.

самый простой способ сделать то, что хочет оригинальный плакат, кажется, это (spawn npm на windows и регистрировать все в родительской консоли):

var args = ['install'];

var options = {
    stdio: 'inherit' //feed all child process logging into parent process
};

var childProcess = spawn('npm.cmd', args, options);
childProcess.on('close', function(code) {
    process.stdout.write('"npm install" finished with code ' + code + '\n');
});

вот самый чистый подход, который я нашел:

require("child_process").spawn('bash', ['./script.sh'], {
  cwd: process.cwd(),
  detached: true,
  stdio: "inherit"
});

ребенок:

setInterval(function() {
    process.stdout.write("hi");
}, 1000); // or however else you want to run a timer

родители:

require('child_process').fork('./childfile.js');
// fork'd children use the parent's stdio

я обнаружил, что мне требуется эта функциональность достаточно часто, чтобы я упаковал ее в библиотеку под названием std-pour. Это должно позволить вам выполнить команду и просмотреть вывод в режиме реального времени. Чтобы установить просто:

npm install std-pour

тогда достаточно просто выполнить команду и посмотреть вывод в реальном времени:

const { pour } = require('std-pour');
pour('ping', ['8.8.8.8', '-c', '4']).then(code => console.log(`Error Code: ${code}`));

Он обещал основе, так что вы можете связать несколько команд. Это даже сигнатура функции-совместимая с child_process.spawn так и должно быть капля в замене везде, где вы его используете.

добавление ответа, связанного с child_process.exec так как я тоже нуждался в живой обратной связи и не получал ее до тех пор, пока сценарий не закончился. Это также дополняет мой комментарий к принятому ответу, но поскольку он отформатирован, он будет немного понятнее и легче читать.

в принципе, у меня есть скрипт npm, который вызывает Gulp, вызывая задачу, которая впоследствии использует child_process.exec для выполнения bash или пакетного сценария в зависимости от ОС. Любой скрипт запускает процесс сборки через Gulp, а затем делает некоторые вызовы некоторых двоичных файлов, которые работают с выходом Gulp.

это точно так же, как и другие (икра и т. д.), но ради завершения, вот как именно это сделать:

// INCLUDES
import * as childProcess from 'child_process'; // ES6 Syntax


// GLOBALS
let exec = childProcess.exec; // Or use 'var' for more proper 
                              // semantics, though 'let' is 
                              // true-to-scope


// Assign exec to a variable, or chain stdout at the end of the call
// to exec - the choice, yours (i.e. exec( ... ).stdout.on( ... ); )
let childProcess = exec
(
    './binary command -- --argument argumentValue',
    ( error, stdout, stderr ) =>
    {
        if( error )
        {
            // This won't show up until the process completes:
            console.log( '[ERROR]: "' + error.name + '" - ' + error.message );
            console.log( '[STACK]: ' + error.stack );

            console.log( stdout );
            console.log( stderr );
            callback();            // Gulp stuff
            return;
        }

        // Neither will this:
        console.log( stdout );
        console.log( stderr );
        callback();                // Gulp stuff
    }
);

теперь его так просто, как добавление прослушивателя событий. Ибо stdout:

childProcess.stdout.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live':
        console.log( '[STDOUT]: ' + data );
    }
);

и stderr:

childProcess.stderr.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live' too:
        console.log( '[STDERR]: ' + data );
    }
);

совсем не плохо-HTH