Exec: tampilkan stdout "live"

188

Saya punya skrip sederhana ini:

var exec = require('child_process').exec;

exec('coffee -cw my_file.coffee', function(error, stdout, stderr) {
    console.log(stdout);
});

di mana saya hanya menjalankan perintah untuk mengkompilasi file skrip kopi. Tetapi stdout tidak pernah ditampilkan di konsol, karena perintahnya tidak pernah berakhir (karena opsi -w dari kopi). Jika saya menjalankan perintah langsung dari konsol saya mendapatkan pesan seperti ini:

18:05:59 - compiled my_file.coffee

Pertanyaan saya adalah: apakah mungkin untuk menampilkan pesan-pesan ini dengan node.js exec? Jika ya, bagaimana? !

Terima kasih

Mravey
sumber
1
Saya datang ke sini untuk mencari stdout dari Python yang dapat dieksekusi. Perhatikan bahwa semua hal di bawah ini akan berfungsi, tetapi Anda harus menjalankan python dengan opsi "-u", untuk membuat keluar tanpa gangguan dan karenanya memiliki pembaruan langsung.
Andy

Jawaban:

266

Jangan gunakan exec. Gunakan spawnyang merupakan EventEmmiterobjek. Kemudian Anda dapat mendengarkan stdout/ stderracara ( spawn.stdout.on('data',callback..)) saat itu terjadi .

Dari dokumentasi NodeJS:

var spawn = require('child_process').spawn,
    ls    = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('child process exited with code ' + code.toString());
});

exec buffer output dan biasanya mengembalikannya ketika perintah telah selesai dieksekusi.

Pooria Azimi
sumber
22
Sangat bagus. FYI: Argumen callback peristiwa stdout / stderr peristiwa 'data' adalah buffer sehingga menyebutnya dengan .toString ()
SergeL
4
Bagi Anda yang tidak dapat menelurkan untuk bekerja di Windows, lihat jawaban yang bagus ini .
tomekwi
17
exec juga merupakan EventEmitter setidaknya dalam terbaru.
Nikolay Tsenkov
4
Perlu diingat juga bahwa panggilan balik itu tidak akan dipanggil, kapan pun program mengeluarkan baris baru. Jika Anda ingin menerima "peristiwa" dari proses anak, proses ini harus membilas buffer ( flush(stdout);dalam C) untuk memecat peristiwa di Node.js.
Julian F. Weinert
5
+1 pada exec juga menjadi EventEmitter .. menghabiskan 2 jam pada refactoring string saya menjadi array args (sangat panjang dan rumit baris perintah ffmpeg) .. hanya untuk mengetahui saya tidak benar-benar perlu.
deadconversations
176

exec juga akan mengembalikan objek ChildProcess yang merupakan EventEmitter.

var exec = require('child_process').exec;
var coffeeProcess = exec('coffee -cw my_file.coffee');

coffeeProcess.stdout.on('data', function(data) {
    console.log(data); 
});

ATAU pipeproses anak stdout ke stdout utama.

coffeeProcess.stdout.pipe(process.stdout);

ATAU mewarisi stdio menggunakan spawn

spawn('coffee -cw my_file.coffee', { stdio: 'inherit' });
Nathanael Smith
sumber
35
Sepertinya ini dapat disederhanakan dengan hanya menggunakan pipe:coffeeProcess.stdout.pipe(process.stdout);
Eric Freese
3
Komentar @ EricFreese adalah apa yang saya cari, karena saya ingin memanfaatkan fitur penggantian karakter stdout (memanfaatkan busur derajat dalam skrip simpul)
LoremIpsum
19
Sederhana: spawn(cmd, argv, { stdio: 'inherit' }). Lihat nodejs.org/api/child_process.html#child_process_options_stdio untuk contoh berbeda.
Morgan Touverey Quilling
3
+1 untuk saran @ MorganTouvereyQuilling untuk digunakan spawnbersama stdio: 'inherit'. Ini menghasilkan keluaran lebih akurat daripada execdan perpipaan stdout/ stderr, misalnya ketika menampilkan informasi kemajuan dari a git clone.
Livven
58

Sudah ada beberapa jawaban namun tidak satupun dari mereka menyebutkan cara terbaik (dan termudah) untuk melakukan ini, yaitu menggunakan spawndan { stdio: 'inherit' }opsi . Tampaknya menghasilkan output paling akurat, misalnya ketika menampilkan informasi kemajuan dari a git clone.

Cukup lakukan ini:

var spawn = require('child_process').spawn;

spawn('coffee', ['-cw', 'my_file.coffee'], { stdio: 'inherit' });

Kredit ke @MorganTouvereyQuilling karena menunjukkan ini dalam komentar ini .

Livven
sumber
1
Saya menemukan bahwa ketika subproses menggunakan output yang diformat seperti teks berwarna, stdio: "inherit" mempertahankan format itu sementara child.stdout.pipe(process.stdout)tidak.
Rikki Gibson
Ini dengan sempurna mempertahankan output bahkan pada proses dengan output yang kompleks seperti progress bar pada instalasi npm. Luar biasa!
Dave Koo
1
mengapa ini bukan jawaban yang diterima? itu satu-satunya yang bekerja untuk saya dan itu hanya 2 f * baris !!!
Lincoln
Tip ini sangat membantu ketika menjalankan beberapa aplikasi baris perintah Symfony yang menggunakan progress bar. Bersulang.
Halfstop
Ini harus menjadi jawaban yang diterima – hanya hal yang menjaga representasi output yang sempurna dan itu yang paling sederhana? ya tolong
evnp
21

Saya hanya ingin menambahkan bahwa satu masalah kecil dengan mengeluarkan string buffer dari proses spawned dengan console.log()itu menambahkan baris baru, yang dapat menyebarkan output proses spawned Anda melalui baris tambahan. Jika Anda keluaran stdoutatau stderrdengan process.stdout.write()bukan console.log(), maka Anda akan mendapatkan keluaran konsol dari proses spawned 'apa adanya'.

Saya melihat solusi itu di sini: Node.js: mencetak ke konsol tanpa baris baru yang tertinggal?

Harapan yang membantu seseorang menggunakan solusi di atas (yang merupakan solusi yang bagus untuk live output, bahkan jika itu dari dokumentasi).

Kevin Teljeur
sumber
1
Untuk penggunaan keluaran yang lebih akurat spawn(command, args, { stdio: 'inherit' }), seperti yang disarankan oleh @MorganTouvereyQuilling di sini stackoverflow.com/questions/10232192/…
Livven
21

Terinspirasi oleh jawaban Nathanael Smith dan komentar Eric Freese, itu bisa sesederhana:

var exec = require('child_process').exec;
exec('coffee -cw my_file.coffee').stdout.pipe(process.stdout);
Tyler Long
sumber
Ini tampaknya berfungsi dengan baik untuk perintah sederhana seperti lstetapi gagal untuk perintah yang lebih kompleks seperti npm install. Saya bahkan mencoba memipatkan stdout dan stderr ke objek proses masing-masing.
linuxdan
@linuxdan mungkin karena npm menulis di stderr (saya melihat beberapa menulis progress bar di sana). Anda juga dapat memasang stderr, atau memperluas solusi Tongfa untuk mendengarkan stderr.
Sergiu
@linuxdan Dari apa yang saya lihat cara yang paling dapat diandalkan adalah spawn(command, args, { stdio: 'inherit' }), seperti yang disarankan di sini stackoverflow.com/questions/10232192/…
Livven
Jawaban terbaik, Terima kasih untuk ini. Bekerja seperti pesona
Abhishek Sharma
12

Saya merasa terbantu untuk menambahkan skrip exec kustom ke utilitas saya yang melakukan ini.

utilities.js

const { exec } = require('child_process')

module.exports.exec = (command) => {
  const process = exec(command)

  process.stdout.on('data', (data) => {
    console.log('stdout: ' + data.toString())
  })

  process.stderr.on('data', (data) => {
    console.log('stderr: ' + data.toString())
  })

  process.on('exit', (code) => {
    console.log('child process exited with code ' + code.toString())
  })
}

app.js

const { exec } = require('./utilities.js')

exec('coffee -cw my_file.coffee')
IanLancaster
sumber
5

Setelah meninjau semua jawaban lain, saya berakhir dengan ini:

function oldSchoolMakeBuild(cb) {
    var makeProcess = exec('make -C ./oldSchoolMakeBuild',
         function (error, stdout, stderr) {
             stderr && console.error(stderr);
             cb(error);
        });
    makeProcess.stdout.on('data', function(data) {
        process.stdout.write('oldSchoolMakeBuild: '+ data);
    });
}

Terkadang dataakan banyak baris, sehingga oldSchoolMakeBuildtajuk akan muncul sekali untuk beberapa baris. Tapi ini tidak cukup mengganggu saya untuk mengubahnya.

Tongfa
sumber
3

child_process.spawn mengembalikan objek dengan stdout dan stderr stream. Anda dapat mengetuk aliran stdout untuk membaca data yang dikirim proses anak kembali ke Node. stdout menjadi stream memiliki "data", "end", dan event lainnya yang stream miliki. spawn paling baik digunakan ketika Anda ingin proses anak mengembalikan sejumlah besar data ke Node - pemrosesan gambar, membaca data biner dll.

sehingga Anda dapat menyelesaikan masalah Anda menggunakan child_process.spawn seperti yang digunakan di bawah ini.

var spawn = require('child_process').spawn,
ls = spawn('coffee -cw my_file.coffee');

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('code ' + code.toString());
});
Adeojo Emmanuel IMM
sumber
1

Berikut adalah fungsi pembantu async yang ditulis dalam naskah yang tampaknya melakukan trik untuk saya. Saya kira ini tidak akan bekerja untuk proses berumur panjang tetapi masih mungkin berguna bagi seseorang?

import * as child_process from "child_process";

private async spawn(command: string, args: string[]): Promise<{code: number | null, result: string}> {
    return new Promise((resolve, reject) => {
        const spawn = child_process.spawn(command, args)
        let result: string
        spawn.stdout.on('data', (data: any) => {
            if (result) {
                reject(Error('Helper function does not work for long lived proccess'))
            }
            result = data.toString()
        })
        spawn.stderr.on('data', (error: any) => {
            reject(Error(error.toString()))
        })
        spawn.on('exit', code => {
            resolve({code, result})
        })
    })
}
Swaner
sumber