Exec: visualizza stdout “live”


188

Ho questo semplice script:

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

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

dove eseguo semplicemente un comando per compilare un file script caffè. Ma stdout non viene mai visualizzato nella console, perché il comando non termina mai (a causa dell'opzione -w di coffee). Se eseguo il comando direttamente dalla console ricevo un messaggio come questo:

18:05:59 - compiled my_file.coffee

La mia domanda è: è possibile visualizzare questi messaggi con il exec node.js? Se si come? !

Grazie


1
Sono venuto qui in cerca di catturare stdout dall'eseguibile di Python. Si noti che tutto quanto segue funzionerà, ma è necessario eseguire Python con un'opzione "-u", per rendere il buffer senza buffer e quindi avere aggiornamenti live.
Andy,

Risposte:


266

Non usare exec. Usa spawnquale è un EventEmmiteroggetto. Quindi puoi ascoltare stdout/ stderrevents ( spawn.stdout.on('data',callback..)) mentre si verificano .

Dalla documentazione di 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 l'output e in genere lo restituisce al termine dell'esecuzione del comando.


22
Molto bella. Cordiali saluti: L'argomento di callback 'data' degli eventi stdout / stderr è un buffer, quindi chiamatelo con .toString ()
SergeL

4
Per quelli di voi che non riescono a far spawnare su Windows, date un'occhiata a questa ottima risposta .
Tomekwi,

17
exec è anche un EventEmitter almeno negli ultimi.
Nikolay Tsenkov,

4
Inoltre, tieni presente che il callback non verrà chiamato ogni volta che il programma genera una nuova riga. Se si desidera ricevere "eventi" dal processo figlio, questo processo deve svuotare il buffer ( flush(stdout);in C) per generare eventi in Node.js.
Julian F. Weinert,

5
+1 su exec è anche un EventEmitter .. ha trascorso 2 ore a refactoring della mia stringa in un array args (riga di comando ffmpeg molto lunga e complicata) .. solo per scoprire che non ne avevo davvero bisogno.
deadconversations

176

exec restituirà anche un oggetto ChildProcess che è un EventEmitter.

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

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

O pipelo stdout del processo figlio nello stdout principale.

coffeeProcess.stdout.pipe(process.stdout);

O ereditare stdio usando spawn

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

35
Sembra che questo possa essere semplificato semplicemente usando pipe:coffeeProcess.stdout.pipe(process.stdout);
Eric Freese,

3
Il commento di EricFreese è quello che stavo cercando, perché volevo sfruttare la funzione di sostituzione dei personaggi di stdout (sfruttando il goniometro in uno script del nodo)
LoremIpsum

19
Più semplice: spawn(cmd, argv, { stdio: 'inherit' }). Vedi nodejs.org/api/child_process.html#child_process_options_stdio per diversi esempi.
Morgan Touverey Quilling,

3
+1 per il suggerimento di @ MorganTouvereyQuilling da utilizzare spawncon stdio: 'inherit'. Produce un output più accurato di exece piping stdout/ stderr, ad esempio quando si visualizzano le informazioni sull'avanzamento da a git clone.
Livven,

58

Esistono già diverse risposte, ma nessuna di esse menziona il modo migliore (e più semplice) per farlo, che sta usando spawne l' { stdio: 'inherit' }opzione . Sembra produrre l'output più accurato, ad esempio quando si visualizzano le informazioni sull'avanzamento da a git clone.

Basta fare questo:

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

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

Ringraziamo @MorganTouvereyQuilling per averlo sottolineato in questo commento .


1
Ho scoperto che quando il sottoprocesso utilizza output formattato come testo colorato, stdio: "inherit"conserva la formattazione mentre child.stdout.pipe(process.stdout)no.
Rikki Gibson,

Ciò preserva perfettamente l'output anche su processi con output complessi come le barre di avanzamento delle installazioni di npm. Eccezionale!
Dave Koo,

1
perché questa non è la risposta accettata? è stato l'unico che ha funzionato per me ed è solo 2 f * righe !!!
Lincoln

Questo suggerimento è stato utile durante l'esecuzione di alcune applicazioni della riga di comando di Symfony che usano barre di avanzamento. Saluti.
Halfstop,

Questa dovrebbe essere la risposta accettata, l'unica cosa che conserva una perfetta rappresentazione dell'output ed è la più semplice? si per favore
evnp

21

Vorrei solo aggiungere quel piccolo problema con l'output delle stringhe di buffer da un processo generato console.log()è che aggiunge nuove righe, che possono diffondere l'output del processo generato su linee aggiuntive. Se esegui l'output stdouto stderrcon process.stdout.write()invece di console.log(), otterrai l'output della console dal processo generato "così com'è".

Ho visto questa soluzione qui: Node.js: stampa su console senza una nuova riga finale?

Spero che aiuti qualcuno a utilizzare la soluzione sopra (che è ottima per l'output live, anche se proviene dalla documentazione).


1
Per l'uso di uscita ancora più accurato spawn(command, args, { stdio: 'inherit' }), come suggerito da @MorganTouvereyQuilling qui stackoverflow.com/questions/10232192/...
Livven

21

Ispirato dalla risposta di Nathanael Smith e dal commento di Eric Freese, potrebbe essere semplice come:

var exec = require('child_process').exec;
exec('coffee -cw my_file.coffee').stdout.pipe(process.stdout);

Questo sembra funzionare bene per comandi semplici come, lsma fallisce per comandi più complessi come npm install. Ho anche provato a collegare sia stdout che stderr ai rispettivi oggetti di processo.
linuxdan,

@linuxdan potrebbe essere perché npm sta scrivendo in stderr (ho visto alcuni scrivere la barra di avanzamento lì). puoi anche reindirizzare stderr o estendere la soluzione Tongfa per ascoltare su stderr.
Sergiu,

@linuxdan Da quello che ho visto il modo più affidabile è spawn(command, args, { stdio: 'inherit' }), come suggerito qui stackoverflow.com/questions/10232192/...
Livven

Migliore risposta, grazie per questo. Ha funzionato come un incantesimo
Abhishek Sharma,

12

Ho trovato utile aggiungere uno script exec personalizzato alle mie utility che lo fanno.

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

5

Dopo aver esaminato tutte le altre risposte, ho finito con questo:

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

A volte datasaranno più righe, quindi l' oldSchoolMakeBuildintestazione apparirà una volta per più righe. Ma questo non mi ha disturbato abbastanza per cambiarlo.


3

child_process.spawn restituisce un oggetto con flussi stdout e stderr. È possibile toccare il flusso stdout per leggere i dati che il processo figlio restituisce al nodo. essendo stdout uno stream ha i "dati", "end" e altri eventi che hanno gli stream. spawn è meglio utilizzato quando si desidera che il processo figlio restituisca una grande quantità di dati a Nodo: elaborazione di immagini, lettura di dati binari ecc.

così puoi risolvere il tuo problema usando child_process.spawn come usato di seguito.

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

1

Ecco una funzione di supporto asincrono scritta in dattiloscritto che sembra fare il trucco per me. Immagino che questo non funzionerà per processi di lunga durata ma potrebbe comunque essere utile per qualcuno?

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})
        })
    })
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.