Node.js menelurkan proses anak dan mendapatkan keluaran terminal secara langsung


113

Saya memiliki skrip yang mengeluarkan 'hai', tidur sebentar, mengeluarkan 'hai', tidur selama 1 detik, dan seterusnya dan seterusnya. Sekarang saya pikir saya akan bisa mengatasi masalah ini dengan model ini.

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

temp.stdout.pipe(process.stdout);

Sekarang masalahnya adalah bahwa tugas tersebut harus diselesaikan agar keluarannya dapat ditampilkan. Seperti yang saya pahami, ini karena fakta bahwa proses yang baru muncul mengambil kendali eksekusi. Jelas node.js tidak mendukung utas jadi ada solusi? Ide saya adalah untuk menjalankan dua contoh, yang pertama untuk tujuan khusus membuat tugas dan membuatnya menyalurkan output ke proses contoh kedua, mengingat ini dapat dicapai.


1
Jika proses anak ditulis pythonmaka jangan lupa untuk memberikan -ubendera agar tidak menyangga keluaran konsol, jika tidak maka akan terlihat seperti skrip tidak live stackoverflow.com/a/49947671/906265
Aivaras

Gunakan npmjs.com/package/cross-spawn daripada yang lainnya. Itu lebih baik.
Andrew Koster

Jawaban:


92

Saya masih merasa basah dengan Node.js, tapi saya punya beberapa ide. pertama, saya yakin Anda perlu menggunakan execFilealih-alih spawn; execFileadalah untuk saat Anda memiliki jalur ke skrip, sedangkan spawnuntuk menjalankan perintah terkenal yang dapat diselesaikan oleh Node.js di jalur sistem Anda.

1. Sediakan callback untuk memproses keluaran yang di-buffer:

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. Tambahkan pendengar ke aliran stdout proses anak ( 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()); 
});

Selanjutnya, tampaknya ada opsi di mana Anda dapat melepaskan proses yang muncul dari terminal pengendali Node, yang akan memungkinkannya untuk berjalan secara asinkron. Saya belum mengujinya, tetapi ada contoh di dokumen API yang berjalan seperti ini:

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
Poin bonus jika Anda dapat menjelaskan cara melakukan ini dengan exec () karena saya perlu menjalankan shell cmd.
DynamicDan

2
Anda dapat menggunakan child.spawn()dengan shellopsi disetel ke true. nodejs.org/api/…
CedX

5
Anda juga dapat menyalurkan child.stdout langsung ke child.stdout.pipe(process.stdout);
proses.stdout

@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

Jauh lebih mudah sekarang (6 tahun kemudian)!

Spawn mengembalikan childObject , yang kemudian bisa Anda dengarkan kejadiannya . Acara-acara tersebut adalah:

  • Kelas: Proses Anak
    • Acara: 'error'
    • Acara: 'exit'
    • Acara: 'tutup'
    • Acara: 'putuskan'
    • Acara: 'message'

Ada juga banyak objek dari childObject , yaitu:

  • Kelas: Proses Anak
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • child.connected
    • child.kill ([sinyal])
    • child.send (pesan [, sendHandle] [, callback])
    • child.disconnect ()

Lihat informasi lebih lanjut di sini tentang childObject: https://nodejs.org/api/child_process.html

Asinkron

Jika Anda ingin menjalankan proses Anda di latar belakang sementara node masih dapat terus dijalankan, gunakan metode asynchronous. Anda masih dapat memilih untuk melakukan tindakan setelah proses Anda selesai, dan ketika proses tersebut memiliki keluaran (misalnya jika Anda ingin mengirim keluaran skrip ke klien).

child_process.spawn (...); (Node 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);
});

Inilah cara Anda menggunakan metode callback + asynchronous :

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

Dengan menggunakan metode di atas, Anda dapat mengirim setiap baris output dari skrip ke klien (misalnya menggunakan Socket.io untuk mengirim setiap baris saat Anda menerima acara pada stdoutatau stderr).

Sinkronis

Jika Anda ingin node menghentikan apa yang dilakukannya dan menunggu hingga skrip selesai , Anda dapat menggunakan versi sinkron:

child_process.spawnSync (...); (Node v0.11.12 +)

Masalah dengan metode ini:

  • Jika skrip membutuhkan waktu beberapa saat untuk diselesaikan, server Anda akan hang selama itu!
  • Stdout hanya akan dikembalikan setelah skrip selesai dijalankan . Karena sinkron, itu tidak dapat dilanjutkan hingga baris saat ini selesai. Oleh karena itu, ia tidak dapat menangkap acara 'stdout' hingga baris pemijahan selesai.

Bagaimana cara menggunakannya:

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, ini harus dipilih sebagai jawaban yang benar sekarang. Sebagai catatan, variabel data di callback masuk sebagai objek Buffer. Anda dapat menggunakan child.stdout.setEncoding('utf8')jika Anda ingin string utf8 masuk.
Ashish

2
Ini tidak berfungsi jika Anda memerlukan informasi dari stdoutasynchronous, yaitu, sementara program lainnya berlanjut, jika proses berlanjut.
Christian Hujer

2
Hai @ChristianHujer! Saya memperbarui jawaban saya untuk menyertakan asinkron dan sinkronisasi: D
Katie

jika Anda memiliki skrip yaitu: console.log("Output 1"); console.error("Boom"); console.log("Output 2");dan saya lakukan spawnAsync('node ./script.js')... bagaimana Anda mempertahankan urutan keluaran? Keluaran saya sepertinya selalu keluar dengan urutan yang salah.
Bryan Ray

FYI, akan lebih mudah jika Anda menggunakan pipeatau pipelineatau meneruskan opsi yang sesuai untuk spawn.
RichS

25

Inilah pendekatan terbersih yang saya temukan:

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

15
Apa sebenarnya yang dilakukannya? Mengapa ini berhasil? Mengapa ini pendekatan yang lebih bersih?
menaikkan tanggal

16

Saya mengalami sedikit kesulitan mendapatkan keluaran logging dari perintah "npm install" ketika saya menelurkan npm dalam proses anak. Pencatatan log dependensi secara realtime tidak ditampilkan di konsol induk.

Cara termudah untuk melakukan apa yang diinginkan poster asli adalah seperti ini (menelurkan npm di windows dan mencatat semuanya ke konsol induk):

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

Saya mendapati diri saya membutuhkan fungsionalitas ini cukup sering sehingga saya mengemasnya ke dalam perpustakaan yang disebut std-pour . Ini harus memungkinkan Anda menjalankan perintah dan melihat output secara real time. Untuk menginstal secara sederhana:

npm install std-pour

Maka cukup sederhana untuk menjalankan perintah dan melihat hasilnya secara realtime:

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

Itu berdasarkan janji sehingga Anda dapat merangkai banyak perintah. Bahkan berfungsi dengan tanda tangan child_process.spawnsehingga harus menjadi pengganti di mana pun Anda menggunakannya.


1
@Kodierantham senang itu bekerja untuk Anda! Sepertinya Anda sedang melakukan beberapa pekerjaan keren, jadi saya harap itu membuat Anda terus berjalan.
Joel B

1

anak:

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

induk:

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

1

Passthru seperti PHP

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

Pemakaian

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

0

Menambahkan jawaban yang terkait dengan child_process.execkarena saya juga membutuhkan umpan balik langsung dan tidak mendapatkannya sampai skrip selesai. Ini juga melengkapi komentar saya untuk jawaban yang diterima, tetapi karena diformat, itu akan sedikit lebih dapat dimengerti dan lebih mudah dibaca.

Pada dasarnya, saya memiliki skrip npm yang memanggil Gulp, menjalankan tugas yang kemudian digunakan child_process.execuntuk menjalankan skrip bash atau batch tergantung pada OS. Salah satu skrip menjalankan proses pembangunan melalui Gulp dan kemudian membuat beberapa panggilan ke beberapa binari yang bekerja dengan keluaran Gulp.

Ini persis seperti yang lain (bertelur, dll.), Tetapi demi penyelesaian, berikut cara melakukannya:

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

Sekarang sesederhana menambahkan pendengar acara. Untuk stdout:

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

Dan untuk stderr:

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

Tidak terlalu buruk sama sekali - HTH

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.