Node.js çocuk süreci ortaya çıkarır ve terminal çıkışını canlı olarak alır


113

'Merhaba' çıktısı veren, bir saniye uyuyan, 'merhaba' çıktısı veren, 1 saniye uyuyan vb. Bir betiğim var. Şimdi bu problemi bu modelle çözebileceğimi düşündüm.

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

temp.stdout.pipe(process.stdout);

Şimdi sorun, çıktının görüntülenmesi için görevin bitirilmesi gerektiğidir. Anladığım kadarıyla, bunun nedeni yeni ortaya çıkan sürecin yürütme kontrolünü ele geçirmesidir. Açıkçası node.js iş parçacıklarını desteklemiyor, yani herhangi bir çözüm? Benim fikrim muhtemelen iki örnek çalıştırmaktı, bunlardan ilki görevi oluşturmak için belirli bir amaç için ve bunun elde edilebileceğini düşünerek çıktıyı ikinci örneğin sürecine yönlendirmesini sağlamaktı.


1
Alt süreç yazılırsa , konsol çıktısını arabelleğe almaması için bayrağı pythoniletmeyi unutmayın -u, aksi takdirde komut dosyası canlı stackoverflow.com/a/49947671/906265
Aivaras

Kullanım npmjs.com/package/cross-spawn başka bir şey yerine. Sadece daha iyi.
Andrew Koster

Yanıtlar:


93

Hala Node.js ile ayaklarımı ıslatıyorum, ancak birkaç fikrim var. Öncelikle execFilebunun yerine kullanmanız gerektiğine inanıyorum spawn; execFilebir komut dosyasına giden yola sahip olduğunuz zamanlar içindir, oysa spawnNode.js'nin sistem yolunuza karşı çözebileceği iyi bilinen bir komutu yürütmek içindir.

1. Arabelleğe alınan çıktıyı işlemek için bir geri arama sağlayın :

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. Alt işlemin stdout akışına ( 9thport.net ) bir dinleyici ekleyin

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()); 
});

Ayrıca, ortaya çıkan işlemi Düğümün kontrol terminalinden ayırabileceğiniz ve eşzamansız olarak çalışmasına izin veren seçenekler var gibi görünmektedir. Bunu henüz test etmedim, ancak API belgelerinde şuna benzer örnekler var :

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()); 
});

8
Bir kabuk cmd'yi çalıştırmam gerektiğinden, bunun nasıl yapılacağını exec () ile açıklayabilirseniz bonus puanlar.
DynamicDan

2
Seçeneğine ayarlanmış seçenek child.spawn()ile kullanabilirsiniz . nodejs.org/api/…shelltrue
CedX

5
Ayrıca child.stdout'u doğrudan process.stdout'a child.stdout.pipe(process.stdout);
aktarabilirsiniz

@DynamicDan javascript let childProcess = exec ( './script-to-run --arg1 arg1value', ( error, stdout, stderror ) => { console.log( '[CALLBACK]: ' + error ); // or stdout or stderror } ); // Same as with spawn: childProcess.stdout.on ( 'data', ( data ) => { console.log( '[LIVE]: ' + data ); // Here's your live data! } );
Rik

130

Artık çok daha kolay (6 yıl sonra)!

Spawn , daha sonra olayları dinleyebileceğiniz bir childObject döndürür . Olaylar:

  • Sınıf: ChildProcess
    • Olay: 'hata'
    • Etkinlik: 'çıkış'
    • Olay: 'kapat'
    • Olay: 'bağlantıyı kes'
    • Olay: 'mesaj'

ChildObject'ten de bir sürü nesne var , bunlar:

  • Sınıf: ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • child.connected
    • child.kill ([sinyal])
    • child.send (mesaj [, sendHandle] [, geri arama])
    • child.disconnect ()

ChildObject hakkında daha fazla bilgiye buradan ulaşabilirsiniz: https://nodejs.org/api/child_process.html

eşzamanlı olmayan

İşleminizi, düğüm hala çalışmaya devam ederken arka planda çalıştırmak istiyorsanız, zaman uyumsuz yöntemi kullanın. İşleminiz tamamlandıktan sonra ve işlemin herhangi bir çıktısı olduğunda (örneğin, istemciye bir komut dosyasının çıktısını göndermek istiyorsanız) eylemleri gerçekleştirmeyi yine de seçebilirsiniz.

child_process.spawn (...); (Düğüm 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);
});

İşte sen asenkron yönteme + bir geri arama nasıl kullanacağını :

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

Yukarıdaki yöntemi kullanarak, komut dosyasındaki her çıktı satırını istemciye gönderebilirsiniz (örneğin, stdoutveya üzerinde olay aldığınızda her satırı göndermek için Socket.io kullanarak stderr).

Senkron

Düğümün yaptığı şeyi durdurmasını ve komut dosyası tamamlanana kadar beklemesini istiyorsanız, zaman uyumlu sürümü kullanabilirsiniz:

child_process.spawnSync (...); (Düğüm v0.11.12 +)

Bu yöntemle ilgili sorunlar:

  • Komut dosyasının tamamlanması biraz zaman alırsa, sunucunuz bu süre boyunca askıda kalacaktır!
  • Standart çıktı, yalnızca betik çalışması bittikten sonra döndürülür . Senkron olduğu için mevcut satır bitene kadar devam edemez. Bu nedenle, spawn hattı bitene kadar 'stdout' olayını yakalayamaz.

Bu nasıl kullanılır:

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

11
+1, bu şimdi doğru cevap olarak seçilmelidir. Sadece bir not, geri aramadaki veri değişkeni Buffer nesnesi olarak gelir. child.stdout.setEncoding('utf8')Utf8 dizelerinin gelmesini istiyorsanız kullanabilirsiniz .
Ashish

2
Bilgiye stdoutzaman uyumsuz olarak ihtiyaç duyarsanız , yani işlem devam ederse kalan program devam ederken bu işe yaramaz .
Christian Hujer

2
Hey @ChristianHujer! Cevabımı hem eşzamansız hem de senkronizasyonu içerecek şekilde güncelledim: D
Katie

eğer bir betiğiniz varsa: console.log("Output 1"); console.error("Boom"); console.log("Output 2");ve yapıyorum spawnAsync('node ./script.js')... çıktının sırasını nasıl korursunuz? Çıktım her zaman yanlış sırada çıkıyor gibi görünüyor.
Bryan Ray

Bilginize, uygun seçenekleri kullanırsanız pipeveya geçerseniz daha da kolaydır . pipelinespawn
RichS

25

İşte bulduğum en temiz yaklaşım:

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

15
Tam olarak ne yapıyor? Neden işe yarıyor? Bu neden daha temiz bir yaklaşımdır?
2019

16

Bir alt süreçte npm'yi ortaya çıkardığımda "npm install" komutundan günlük çıktısını almakta biraz sorun yaşadım. Bağımlılıkların gerçek zamanlı günlüğe kaydedilmesi üst konsolda gösterilmedi.

Orijinal posterin istediği şeyi yapmanın en basit yolu şudur (Windows'ta npm oluştur ve her şeyi ana konsoluna kaydet):

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

3

Kendimi std-pour adlı bir kitaplığa paketlediğim kadar sık ​​bu işlevselliğe ihtiyaç duyarken buldum . Bir komutu yürütmenize ve çıktıyı gerçek zamanlı olarak görüntülemenize izin vermelidir. Basitçe kurmak için:

npm install std-pour

Ardından, bir komutu yürütmek ve çıktıyı gerçek zamanlı olarak görmek yeterince basittir:

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

Temelli vaat edildi, böylece birden fazla komut zincirleyebilirsiniz. Hatta imzayla uyumlu işlevlere sahiptir, child_process.spawnbu nedenle kullandığınız her yerde yedek olarak düşmesi gerekir.


1
@KodieGrantham sizin için çalıştığına sevindim! Harika bir iş çıkarıyor gibisiniz, umarım koşmaya devam etmenizi sağlar.
Joel B

1

çocuk:

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

ebeveyn:

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

1

PHP benzeri geçiş

import { spawn } from 'child_process';

export default async function passthru(exe, args, options) {
    return new Promise((resolve, reject) => {
        const env = Object.create(process.env);
        const child = spawn(exe, args, {
            ...options,
            env: {
                ...env,
                ...options.env,
            },
        });
        child.stdout.setEncoding('utf8');
        child.stderr.setEncoding('utf8');
        child.stdout.on('data', data => console.log(data));
        child.stderr.on('data', data => console.log(data));
        child.on('error', error => reject(error));
        child.on('close', exitCode => {
            console.log('Exit code:', exitCode);
            resolve(exitCode);
        });
    });
}

kullanım

const exitCode = await passthru('ls', ['-al'], { cwd: '/var/www/html' })

0

child_process.execBenim de canlı geri bildirime ihtiyacım olduğu ve senaryo bitene kadar cevap alamadığımla ilgili bir cevap eklemek . Bu aynı zamanda yorumumu kabul edilen cevaba tamamlar, ancak biçimlendirildiği için biraz daha anlaşılır ve okunması daha kolay olacaktır.

Temel olarak, Gulp'i çağıran ve daha sonra child_process.execişletim sistemine bağlı olarak bir bash veya toplu komut dosyası yürütmek için kullanılan bir görevi çağıran bir npm betiğim var . Her iki betik de Gulp aracılığıyla bir derleme işlemi çalıştırır ve ardından Gulp çıktısı ile çalışan bazı ikili dosyalara bazı çağrılar yapar.

Tıpkı diğerleri gibi (spawn vb.), Ancak tamamlama uğruna, işte tam olarak nasıl yapılacağı:

// 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
    }
);

Artık bir olay dinleyicisi eklemek kadar basit. Şunun için stdout:

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

Ve için stderr:

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

Hiç fena değil - HTH

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.