Exec: Anzeige stdout "live"

187

Ich habe dieses einfache Skript:

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

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

wo ich einfach einen Befehl ausführe, um eine Kaffeeskriptdatei zu kompilieren. Aber stdout wird nie in der Konsole angezeigt, da der Befehl nie endet (wegen der Option -w für Kaffee). Wenn ich den Befehl direkt von der Konsole aus ausführe, erhalte ich folgende Meldung:

18:05:59 - compiled my_file.coffee

Meine Frage ist: Ist es möglich, diese Nachrichten mit der Datei node.js exec anzuzeigen? Wenn ja wie? !

Vielen Dank

mravey
quelle
1
Ich bin hierher gekommen, um stdout aus der ausführbaren Python-Datei zu erfassen. Beachten Sie, dass alle folgenden Funktionen funktionieren, Sie jedoch Python mit der Option "-u" ausführen müssen, um ungepuffert zu sein und damit Live-Updates zu erhalten.
Andy

Antworten:

264

Nicht benutzen exec. Verwenden Sie, spawnwelches ein EventEmmiterObjekt ist. Dann können Sie stdout/ stderrevents ( spawn.stdout.on('data',callback..)) anhören, sobald sie eintreten .

Aus der NodeJS-Dokumentation:

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 puffert die Ausgabe und gibt sie normalerweise zurück, wenn der Befehl ausgeführt wurde.

Pooria Azimi
quelle
22
Sehr schön. Zu Ihrer
Information
4
Für diejenigen unter Ihnen, die Spawn nicht unter Windows zum Laufen bringen können, werfen Sie einen Blick auf diese großartige Antwort .
Tomékwi
17
exec ist zumindest spätestens auch ein EventEmitter.
Nikolay Tsenkov
4
Beachten Sie auch, dass der Rückruf nicht aufgerufen wird, wenn das Programm eine neue Zeile ausgibt. Wenn Sie "Ereignisse" vom untergeordneten Prozess empfangen möchten, muss dieser Prozess den Puffer ( flush(stdout);in C) leeren, um Ereignisse in Node.js auszulösen.
Julian F. Weinert
5
+1 on exec ist auch ein EventEmitter. Ich habe 2 Stunden damit verbracht, meine Zeichenfolge in ein args-Array umzuwandeln (sehr lange und komplizierte ffmpeg-Befehlszeile). Nur um herauszufinden, dass ich das nicht wirklich brauchte.
Deadconversations
175

exec gibt auch ein ChildProcess-Objekt zurück, das ein EventEmitter ist.

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

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

ODER pipedie Standardausgabe des untergeordneten Prozesses zur Hauptausgabe.

coffeeProcess.stdout.pipe(process.stdout);

ODER erben Sie stdio mit spawn

spawn('coffee -cw my_file.coffee', { stdio: 'inherit' });
Nathanael Smith
quelle
35
Sieht so aus, als könnte dies vereinfacht werden, indem man einfach Folgendes verwendet pipe:coffeeProcess.stdout.pipe(process.stdout);
Eric Freese
3
@ EricFreeses Kommentar ist das, wonach ich gesucht habe, weil ich die Zeichenersatzfunktion von stdout nutzen wollte (Winkelmesser in einem
Knotenskript nutzen
19
Einfacher : spawn(cmd, argv, { stdio: 'inherit' }). Weitere Beispiele finden Sie unter nodejs.org/api/child_process.html#child_process_options_stdio .
Morgan Touverey Quilling
3
+1 für @ MorganTouvereyQuillings Vorschlag zur Verwendung spawnmit stdio: 'inherit'. Es erzeugt eine genauere Ausgabe als execund piping stdout/ stderr, beispielsweise wenn die Fortschrittsinformationen von a angezeigt werden git clone.
Livven
55

Es gibt bereits mehrere Antworten, aber keine von ihnen erwähnt den besten (und einfachsten) Weg, dies zu tun, nämlich die Verwendung spawnund die { stdio: 'inherit' }Option . Es scheint die genaueste Ausgabe zu liefern, beispielsweise wenn die Fortschrittsinformationen von a angezeigt werden git clone.

Mach das einfach:

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

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

Dank an @MorganTouvereyQuilling für den Hinweis in diesem Kommentar .

Livven
quelle
1
Ich habe festgestellt, dass, wenn der Unterprozess eine formatierte Ausgabe wie farbigen Text verwendet, stdio: "inherit"diese Formatierung beibehalten wird, während child.stdout.pipe(process.stdout)dies nicht der Fall ist.
Rikki Gibson
Dadurch bleibt die Ausgabe auch bei Prozessen mit komplexer Ausgabe wie den Fortschrittsbalken bei npm-Installationen perfekt erhalten. Genial!
Dave Koo
1
Warum ist dies nicht die akzeptierte Antwort? Es war das einzige, das für mich funktioniert hat und es sind nur 2 f * Zeilen !!!
Lincoln
Dieser Tipp war hilfreich, wenn Sie einige Symfony-Befehlszeilenanwendungen ausführen, die Fortschrittsbalken verwenden. Prost.
Halfstop
Dies sollte die akzeptierte Antwort sein - nur eine Sache, die eine perfekte Ausgabedarstellung bewahrt und die einfachste ist? ja bitte
evnp
21

Ich möchte nur hinzufügen, dass ein kleines Problem bei der Ausgabe der Pufferzeichenfolgen aus einem gespawnten Prozess darin console.log()besteht, dass Zeilenumbrüche hinzugefügt werden, die die Ausgabe Ihres gespawnten Prozesses auf zusätzliche Zeilen verteilen können. Wenn Sie stdoutoder stderrmit process.stdout.write()statt ausgeben console.log(), erhalten Sie die Konsolenausgabe aus dem erzeugten Prozess "wie sie ist".

Ich habe diese Lösung hier gesehen: Node.js: Drucken auf die Konsole ohne nachgestellten Zeilenumbruch?

Ich hoffe, das hilft jemandem bei der Verwendung der oben genannten Lösung (die sich hervorragend für die Live-Ausgabe eignet, auch wenn sie aus der Dokumentation stammt).

Kevin Teljeur
quelle
1
Für eine noch genauere Ausgabe verwenden Sie spawn(command, args, { stdio: 'inherit' }), wie von @MorganTouvereyQuilling hier vorgeschlagen. Stackoverflow.com/questions/10232192/…
Livven
20

Inspiriert von Nathanael Smiths Antwort und Eric Freeses Kommentar könnte es so einfach sein wie:

var exec = require('child_process').exec;
exec('coffee -cw my_file.coffee').stdout.pipe(process.stdout);
Tyler Long
quelle
Dies scheint für einfache Befehle wie gut zu funktionieren ls, schlägt jedoch für komplexere Befehle wie fehl npm install. Ich habe sogar versucht, sowohl stdout als auch stderr zu ihren jeweiligen Prozessobjekten zu leiten.
Linuxdan
@linuxdan kann es sein, dass npm in stderr schreibt (ich habe gesehen, dass einige den Fortschrittsbalken dort geschrieben haben). Sie können auch stderr leiten oder die Tongfa-Lösung erweitern, um stderr zu hören.
Sergiu
@linuxdan Von dem, was ich am zuverlässigsten gesehen habe, ist spawn(command, args, { stdio: 'inherit' }), wie hier vorgeschlagen stackoverflow.com/questions/10232192/…
Livven
12

Ich fand es hilfreich, meinen Dienstprogrammen, die dies tun, ein benutzerdefiniertes Exec-Skript hinzuzufügen.

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
quelle
5

Nachdem ich alle anderen Antworten überprüft hatte, kam ich zu folgendem Ergebnis:

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

Manchmal datasind es mehrere Zeilen, sodass die oldSchoolMakeBuildÜberschrift für mehrere Zeilen einmal angezeigt wird. Aber das hat mich nicht genug gestört, um es zu ändern.

Tongfa
quelle
3

child_process.spawn gibt ein Objekt mit stdout- und stderr-Streams zurück. Sie können auf den Stream stdout tippen, um Daten zu lesen, die der untergeordnete Prozess an Node zurücksendet. stdout als Stream hat die "Daten", "Ende" und andere Ereignisse, die Streams haben. spawn ist am besten geeignet, wenn der untergeordnete Prozess eine große Datenmenge an Node zurückgeben soll - Bildverarbeitung, Lesen von Binärdaten usw.

So können Sie Ihr Problem mit child_process.spawn lösen, wie unten verwendet.

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
quelle
1

Hier ist eine asynchrone Hilfsfunktion, die in Typoskript geschrieben ist und den Trick für mich zu tun scheint. Ich denke, dies wird nicht für langlebige Prozesse funktionieren, könnte aber dennoch für jemanden nützlich sein?

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})
        })
    })
}
Schwaner
quelle