Подтвердить что ты не робот

Node.js запускает дочерний процесс и получает выход терминала в реальном времени

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

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

temp.stdout.pipe(process.stdout);

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

4b9b3361

Ответ 1

Я все еще промокаю ногами от Node.js, но у меня есть несколько идей. во-первых, я считаю, что вам нужно использовать execFile вместо spawn; execFile предназначен для execFile когда у вас есть путь к сценарию, тогда как spawn предназначен для выполнения известной команды, которую Node.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 process terminates.
    console.log(stdout); 
});  

2. Добавьте слушателя в поток stdout дочернего процесса (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()); 
});

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

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 event loop from the parent's: 
child.unref(); 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

Ответ 2

Теперь намного проще (6 лет спустя)!

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

  • Класс: ChildProcess
    • Событие: "ошибка"
    • Событие: "выход"
    • Событие: "закрыть"
    • Событие: "отключить"
    • Событие: "сообщение"

Есть также несколько объектов из childObject, они:

  • Класс: ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • child.connected
    • child.kill([сигнал])
    • child.send(message [, sendHandle] [, callback])
    • child.disconnect()

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

Асинхронный

Если вы хотите запустить ваш процесс в фоновом режиме, пока узел все еще может продолжать выполнение, используйте асинхронный метод. Вы по-прежнему можете выполнять действия после завершения процесса и при наличии каких-либо результатов.

child_process.spawn(...); (Узел v0.1.90)

var spawn = require('child_process').spawn;
var child = spawn('node ./commands/server.js');

// You can also use a variable to save the output 
// for when the script closes later
var scriptOutput = "";

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
    //Here is where the output goes

    console.log('stdout: ' + data);

    data=data.toString();
    scriptOutput+=data;
});

child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
    //Here is where the error output goes

    console.log('stderr: ' + data);

    data=data.toString();
    scriptOutput+=data;
});

child.on('close', function(code) {
    //Here you can get the exit code of the script

    console.log('closing code: ' + code);

    console.log('Full output of script: ',scriptOutput);
});

Вот как бы вы использовали callback + асинхронный метод:

var child_process = require('child_process');

console.log("Node Version: ", process.version);

run_script("ls", ["-l", "/home"], function(output, exit_code) {
    console.log("Process Finished.");
    console.log('closing code: ' + exit_code);
    console.log('Full output of script: ',output);
});

console.log ("Continuing to do node things while the process runs at the same time...");

// This function will output the lines from the script 
// AS is runs, AND will return the full combined output
// as well as exit code when it done (using the callback).
function run_script(command, args, callback) {
    console.log("Starting Process.");
    var child = child_process.spawn(command, args);

    var scriptOutput = "";

    child.stdout.setEncoding('utf8');
    child.stdout.on('data', function(data) {
        console.log('stdout: ' + data);

        data=data.toString();
        scriptOutput+=data;
    });

    child.stderr.setEncoding('utf8');
    child.stderr.on('data', function(data) {
        console.log('stderr: ' + data);

        data=data.toString();
        scriptOutput+=data;
    });

    child.on('close', function(code) {
        callback(scriptOutput,code);
    });
}

Используя описанный выше метод, вы можете отправить каждую строку вывода из скрипта клиенту (например, используя Socket.io для отправки каждой строки, когда вы получаете события на stdout или stderr).

синхронный

Если вы хотите, чтобы узел остановил свою работу и дождался завершения сценария, вы можете использовать синхронную версию:

child_process.spawnSync(...); (Узел v0.11. 12+)

Проблемы с этим методом:

  • Если выполнение сценария занимает некоторое время, ваш сервер будет зависать в течение этого времени!
  • Stdout будет возвращен только после завершения работы скрипта. Поскольку он синхронный, он не может продолжаться, пока не закончится текущая строка. Поэтому он не может захватить событие 'stdout', пока линия возрождения не закончится.

Как это использовать:

var child_process = require('child_process');

var child = child_process.spawnSync("ls", ["-l", "/home"], { encoding : 'utf8' });
console.log("Process finished.");
if(child.error) {
    console.log("ERROR: ",child.error);
}
console.log("stdout: ",child.stdout);
console.log("stderr: ",child.stderr);
console.log("exist code: ",child.status);

Ответ 3

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

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

Ответ 4

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

Самый простой способ сделать то, что хочет исходный плакат, - это создать (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');
});

Ответ 5

ребенок:

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 stdio

Ответ 6

Я достаточно часто требовал этой функции, чтобы упаковать ее в библиотеку 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, поэтому он должен быть заменой в любом месте, где вы его используете.

Ответ 7

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

По сути, у меня есть сценарий npm, который вызывает Gulp, вызывая задачу, которая впоследствии использует child_process.exec для выполнения сценария bash или batch в зависимости от ОС. Любой из сценариев запускает процесс сборки через 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