Exec: mostrar stdout “en vivo”

6 minutos de lectura

avatar de usuario de mravey
mravey

Tengo este script simple:

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

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

donde simplemente ejecuto un comando para compilar un archivo de script de café. Pero stdout nunca se muestra en la consola, porque el comando nunca termina (debido a la opción -w de café). Si ejecuto el comando directamente desde la consola, recibo un mensaje como este:

18:05:59 - compiled my_file.coffee

Mi pregunta es: ¿es posible mostrar estos mensajes con el exec de node.js? Si es así, ¿cómo? !

  • Vine aquí buscando capturar stdout del ejecutable de Python. Tenga en cuenta que todo lo siguiente funcionará, pero debe ejecutar python con una opción “-u”, para salir sin búfer y, por lo tanto, tener actualizaciones en vivo.

    – Andy

    5 de noviembre de 2017 a las 18:36

Avatar de usuario de Pooria Azimi
Pooria Azimi

no usar exec. Usar spawn que es un EventEmmiter objeto. Entonces puedes escuchar stdout/stderr eventos (spawn.stdout.on('data',callback..)) como suceden.

De la documentación de 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 almacena en búfer la salida y generalmente la devuelve cuando el comando ha terminado de ejecutarse.

  • Muy lindo. FYI: El argumento de devolución de llamada de eventos stdout/stderr ‘datos’ es un búfer, así que llámelo con .toString()

    – SergeL

    9 mayo 2014 a las 14:01

  • Para aquellos de ustedes que no pueden hacer que spawn funcione en Windows, echen un vistazo a esta gran respuesta.

    – tomekwi

    27 de agosto de 2014 a las 8:33

  • exec también es un EventEmitter al menos en la última versión.

    – Nikolái Tsenkov

    27 de junio de 2015 a las 3:50

  • También tenga en cuenta que no se llamará a la devolución de llamada, siempre que el programa genere una nueva línea. Si desea recibir “eventos” del proceso secundario, este proceso debe vaciar el búfer (flush(stdout); en C) para disparar eventos en Node.js.

    – Julián F. Weinert

    20 de marzo de 2016 a las 1:18

  • +1 en exec que también es un EventEmitter … pasé 2 horas refactorizando mi cadena en una matriz de argumentos (línea de comando ffmpeg muy larga y complicada) … solo para descubrir que realmente no necesitaba hacerlo.

    – conversaciones muertas

    04/04/2016 a las 13:34

Avatar de usuario de Nathanael Smith
natanael smith

exec también devolverá un objeto ChildProcess que es 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 pipe la salida estándar del proceso hijo a la salida estándar principal.

coffeeProcess.stdout.pipe(process.stdout);

O heredar stdio usando spawn

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

  • Parece que esto se puede simplificar simplemente usando pipe: coffeeProcess.stdout.pipe(process.stdout);

    –Eric Freese

    19/08/2015 a las 15:00

  • El comentario de @EricFreese es lo que estaba buscando, porque quería aprovechar la función de reemplazo de caracteres de stdout (transportador de aprovechamiento en un script de nodo)

    – Lorem Ipsum

    31/10/2016 a las 11:00

  • Más simple: spawn(cmd, argv, { stdio: 'inherit' }). Ver nodejs.org/api/child_process.html#child_process_options_stdio para diferentes ejemplos.

    -Morgan Touverey Quilling

    30 de marzo de 2017 a las 14:39

  • +1 por la sugerencia de uso de @MorganTouvereyQuilling spawn con stdio: 'inherit'. Produce una salida más precisa que exec y tuberías stdout/stderrpor ejemplo, cuando se muestra la información de progreso de un git clone.

    – Livven

    18 de abril de 2017 a las 15:56

Avatar de usuario de Livven
Livven

Ya hay varias respuestas, sin embargo, ninguna de ellas menciona la mejor (y más fácil) forma de hacerlo, que es usar spawn y el { stdio: 'inherit' } opción. Parece producir la salida más precisa, por ejemplo, al mostrar la información de progreso de un git clone.

Simplemente haz esto:

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

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

Crédito a @MorganTouvereyQuilling por señalar esto en este comentario.

  • Descubrí que cuando el subproceso usa una salida formateada como texto en color, stdio: "inherit" conserva ese formato mientras child.stdout.pipe(process.stdout) no es.

    –Rikki Gibson

    21/09/2017 a las 19:59

  • Esto conserva perfectamente la salida incluso en procesos con salida compleja como las barras de progreso en las instalaciones de npm. ¡Impresionante!

    – Dave Koo

    10 de junio de 2018 a las 18:17

  • ¿Por qué esta no es la respuesta aceptada? ¡fue el único que funcionó para mí y son solo 2 f * líneas!

    –Lincoln

    23 de marzo de 2019 a las 20:56

  • Este consejo fue útil al ejecutar algunas aplicaciones de línea de comandos de Symfony que usan barras de progreso. Salud.

    – Medio punto

    16 de julio de 2019 a las 16:41

  • Esta debería ser la respuesta aceptada, lo único que preserva la representación de salida perfecta y es el mas sencillo? sí, por favor

    – evnp

    31 de marzo de 2020 a las 16:38

Avatar de usuario de Tyler Liu
tyler liu

Inspirado por la respuesta de Nathanael Smith y el comentario de Eric Freese, podría ser tan simple como:

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

Avatar de usuario de Kevin Teljeur
kevin teljeur

Solo me gustaría agregar ese pequeño problema con la salida de las cadenas de búfer de un proceso generado con console.log() es que agrega líneas nuevas, que pueden distribuir la salida del proceso generado en líneas adicionales. Si tu salida stdout o stderr con process.stdout.write() en lugar de console.log()luego obtendrá la salida de la consola del proceso generado ‘tal cual’.

Vi esa solución aquí: Node.js: ¿imprimir en la consola sin una nueva línea final?

Espero que ayude a alguien que use la solución anterior (que es excelente para la salida en vivo, incluso si es de la documentación).

  • Para un uso de salida aún más preciso spawn(command, args, { stdio: 'inherit' })como lo sugiere @MorganTouvereyQuilling aquí stackoverflow.com/questions/10232192/…

    – Livven

    18 de abril de 2017 a las 15:59


Avatar de usuario de General Grievance
Quejas Generales

He encontrado útil agregar un script exec personalizado a mis utilidades que hacen esto.

utilidades.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())
  })
}

aplicación.js

const { exec } = require('./utilities.js')
    
exec('coffee -cw my_file.coffee')

  • Para un uso de salida aún más preciso spawn(command, args, { stdio: 'inherit' })como lo sugiere @MorganTouvereyQuilling aquí stackoverflow.com/questions/10232192/…

    – Livven

    18 de abril de 2017 a las 15:59


Avatar de usuario de Tongfa
Tongfa

Después de revisar todas las otras respuestas, terminé con esto:

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 veces data serán varias líneas, por lo que oldSchoolMakeBuild el encabezado aparecerá una vez para varias líneas. Pero esto no me molestó lo suficiente como para cambiarlo.

¿Ha sido útil esta solución?