node.js führt den Systembefehl synchron aus

171

Ich brauche in node.js Funktion

result = execSync('node -v');

das wird synchron die gegebene Befehlszeile ausgeführt werden und kehren alle stdout'ed von diesem Befehlstext.

ps. Die Synchronisierung ist falsch. Ich weiß. Nur für den persönlichen Gebrauch.

AKTUALISIEREN

Jetzt haben wir die Lösung von mgutz, die uns den Exit-Code gibt, aber nicht stdout! Ich warte immer noch auf eine genauere Antwort.

AKTUALISIEREN

mgutz hat seine Antwort aktualisiert und die Lösung ist hier :)
Wie dgo.a bereits erwähnt hat, gibt es auch ein eigenständiges Modul exec-sync

UPDATE 30.07.2014

ShellJS lib ist angekommen. Betrachten Sie dies als die beste Wahl für den Moment.


UPDATE 10.02.2015

ZU GUTER LETZT! NodeJS 0.12 unterstützt execSyncnativ.
Siehe offizielle Dokumente

enttäuscht
quelle
26
lassen Sie sich nicht täuschen zu bekommen, ist sync nicht falsch ... auch in NodeJS gesamten Code ausgeführt wird synchron , wenn Sie explizit eine Asynchron - Methode aufrufen , ... wenn alles die asynchrone Art und Weise getan wurde nichts würde jemals tun. Wenn Sie asynchrone Methoden bevorzugen, bedeutet dies nicht, dass Ihre langwierige Berechnung Ihren Server nicht blockiert. Es ist eine Wahl. Dass die Macher von Node neben den asynchronen auch synchrone Dateisystemmethoden bereitgestellt haben, zeigt nur, dass es auch für diese einen Platz gibt.
Flow
2
Wo finden wir die "Unix-Shell-Emulationsbibliothek", von der Sie sprechen?
Florian
@Florian er meint ShellJS
xst

Antworten:

153

Node.js (seit Version 0.12 - also für eine Weile) unterstützt execSync:

child_process.execSync(command[, options])

Sie können dies jetzt direkt tun:

const execSync = require('child_process').execSync;
code = execSync('node -v');

und es wird tun, was Sie erwarten. (Standardmäßig werden die E / A-Ergebnisse an den übergeordneten Prozess weitergeleitet.) Beachten Sie, dass Sie auch spawnSyncjetzt können.

Benjamin Gruenbaum
quelle
6
nach 10 Stunden Verzweiflung. DANKE DUDE
Tom Dev
Wie kann ich die Verbindung zu diesem Unterprozess trennen?
JulianSoto
@JulianSoto nodejs.org/api/…
göttlich
54

Siehe execSync- Bibliothek.

Mit Node-ffi ist das ziemlich einfach . Ich würde es nicht für Serverprozesse empfehlen, aber für allgemeine Entwicklungsdienstprogramme werden die Dinge erledigt. Installieren Sie die Bibliothek.

npm install node-ffi

Beispielskript:

var FFI = require("node-ffi");
var libc = new FFI.Library(null, {
  "system": ["int32", ["string"]]
});

var run = libc.system;
run("echo $USER");

[EDIT Jun 2012: Wie bekomme ich STDOUT]

var lib = ffi.Library(null, {
    // FILE* popen(char* cmd, char* mode);
    popen: ['pointer', ['string', 'string']],

    // void pclose(FILE* fp);
    pclose: ['void', [ 'pointer']],

    // char* fgets(char* buff, int buff, in)
    fgets: ['string', ['string', 'int','pointer']]
});

function execSync(cmd) {
  var
    buffer = new Buffer(1024),
    result = "",
    fp = lib.popen(cmd, 'r');

  if (!fp) throw new Error('execSync error: '+cmd);

  while(lib.fgets(buffer, 1024, fp)) {
    result += buffer.readCString();
  };
  lib.pclose(fp);

  return result;
}

console.log(execSync('echo $HOME'));
mgutz
quelle
2
Wie würden Sie vorgehen, um tatsächlich etwas stdoutdavon zu erhalten? Alles was ich bekommen kann ist der Prozess-Exit-Code
Mark Kahn
@cwolves: Ich denke, Async wäre das besser. (
Ivos
@pvorb - ja, außer wenn Sie nicht asynchron verwenden können :)
Mark Kahn
1
Es gibt triftige Gründe, den Async-Hammer nicht für jeden Nagel zu verwenden. Beispielsweise sind Template-Engines in Express 3 asynchron und Hilfsfunktionen (lokale Funktionen) müssen synchron sein. Was ist, wenn diese Hilfsfunktionen Less-Dateien im laufenden Betrieb asynchron kompilieren müssen?
mgutz
8
Ich frage mich, warum dies einfach execSyncnicht Teil von ist child_process. Ich denke, das sollte es sein.
Michael Härtl
31

Verwenden Sie das ShellJS- Modul.

Exec- Funktion ohne Rückruf.

Beispiel:

var version = exec('node -v').output;
falko
quelle
2
Beachten Sie, dass zum Zeitpunkt des Schreibens in den Dokumenten erwähnt wird, dass die Synchronisierung exec()für lange Prozesse CPU-intensiv ist.
Aram Kocharyan
1
Optionen, {Silent: True} ist der Schlüssel
Nick
1
Dies macht etwas (außer kürzere Syntax), was nicht? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764
23

In node.js gibt es ein hervorragendes Modul zur Flusskontrolle namens asyncblock . Wenn das Umschließen des Codes in eine Funktion für Ihren Fall in Ordnung ist, kann das folgende Beispiel in Betracht gezogen werden:

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

asyncblock(function (flow) {
    exec('node -v', flow.add());
    result = flow.wait();
    console.log(result);    // There'll be trailing \n in the output

    // Some other jobs
    console.log('More results like if it were sync...');
});
schnappen
quelle
1
Er fragte explizit nach der Synchronisierungsversion und nicht nach Kontrollflussbibliotheken.
Alexey Petrushin
22
@AlexeyPetrushin Bei jeder Frage geht es um ein Ziel, nicht um einen bestimmten Weg, es zu erreichen. Vielen Dank für die Ablehnung.
Nab
1
Dies ist auch eine sehr nützliche Antwort für Windows-Benutzer. Die Installation exec-syncoder ffiunter Windows hat einen enormen Overhead (VC ++, SDKs, Python usw.), der jedoch leichter ist.
Mendhak
10

Dies ist in Node.js nicht möglich child_process.spawn und child_process.execwurde von Grund auf asynchron erstellt.

Für Details siehe: https://github.com/ry/node/blob/master/lib/child_process.js

Wenn Sie diese Blockierung wirklich möchten, dann setzen Sie alles, was danach passieren muss, in einen Rückruf oder erstellen Sie eine eigene Warteschlange, um dies blockierend zu behandeln. Ich nehme an, Sie könnten Async.js verwenden für diese Aufgabe verwenden.

Oder, falls Sie viel zu viel Zeit haben, hacken Sie in Node.js selbst herum.

Ivo Wetzel
quelle
12
seltsam, weil das Dateisystemmodul synchrone Aufrufe hat. Warum nicht auch ausführen?
Alfred
4
@Alfred Die synchronisierten FS-Aufrufe sind hauptsächlich zum Laden von Konfigurationen beim Programmstart vorhanden.
Ivo Wetzel
3
@IvoWetzel - tisk tisk ... haben wir nicht gelernt, nie zu sagen, dass etwas unmöglich ist? ;) siehe meine Lösung unten.
Marcus Pope
1
@IvoWetzel "Die Synchronisierungs-FS-Aufrufe ..." - richtig, und manchmal möchten Sie beispielsweise einen Befehl ausgeben, um beim Programmstart etwas zu kompilieren und nach Abschluss fortzufahren. - vorausgesetzt, es gibt Synchronisierungs-FS-Aufrufe ohne Synchronisierungsausführung sieht aus wie ein Versehen. Ich bin alles für asynchron, aber synchron hat seine Vorteile und Anwendungsfälle. Ich muss es natürlich vernünftig einsetzen.
Flow
Async ist in Ordnung, aber wenn WidgetB von den Endergebnissen von WidgetA abhängt, wird der gesamte Async der Welt den Job nicht erledigen. Manchmal müssen Prozesse synchron sein. Versuchen Sie, asynchron zu kochen. ;)
Lloyd Sargent
9

Dies ist der einfachste Weg, den ich gefunden habe:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(Nicht zu verwechseln mit execSync.)
Shell-Befehl synchron . Verwenden Sie diese Option für Migrationsskripte und CLI-Programme, jedoch nicht für regulären Servercode.

Beispiel:

var execSync = require('exec-sync');   
var user = execSync('echo $USER');
console.log(user);
dgo.a.
quelle
5

Sie können dies mit Fasern erreichen. Wenn Sie beispielsweise meine Common Node-Bibliothek verwenden , sieht der Code folgendermaßen aus:

result = require('subprocess').command('node -v');
Oleg
quelle
3

Ich gewöhne mich daran, "synchronous"Dinge am Ende der Rückruffunktion zu implementieren . Nicht sehr schön, aber es funktioniert. Wenn Sie eine Folge von Befehlszeilenausführungen implementieren müssen, müssen Sie execin eine benannte Funktion einbrechen und diese rekursiv aufrufen. Dieses Muster scheint für mich brauchbar zu sein:

SeqOfExec(someParam);

function SeqOfExec(somepParam) {
    // some stuff
    // .....
    // .....

    var execStr = "yourExecString";
    child_proc.exec(execStr, function (error, stdout, stderr) {
        if (error != null) {
            if (stdout) {
                throw Error("Smth goes wrong" + error);
            } else {
                // consider that empty stdout causes
                // creation of error object
            }
        }
        // some stuff
        // .....
        // .....

        // you also need some flag which will signal that you 
        // need to end loop
        if (someFlag ) {
            // your synch stuff after all execs
            // here
            // .....
        } else {
            SeqOfExec(someAnotherParam);
        }
    });
};
scherka
quelle
3

Ich hatte ein ähnliches Problem und schrieb schließlich eine Knotenerweiterung dafür. Sie können das Git-Repository auschecken. Es ist Open Source und kostenlos und all das gute Zeug!

https://github.com/aponxi/npm-execxi

ExecXI ist eine in C ++ geschriebene Knotenerweiterung, mit der Shell-Befehle einzeln ausgeführt werden und die Ausgabe des Befehls in Echtzeit an die Konsole ausgegeben wird. Optional sind verkettete und nicht verkettete Wege vorhanden. Dies bedeutet, dass Sie das Skript stoppen können, nachdem ein Befehl fehlgeschlagen (verkettet) ist, oder Sie können fortfahren, als wäre nichts passiert!

Gebrauchsanweisungen finden Sie in der ReadMe-Datei . Fühlen Sie sich frei, Pull-Anfragen zu stellen oder Probleme einzureichen!

EDIT: Allerdings gibt es das stdout noch nicht zurück ... Gibt sie nur in Echtzeit aus. Das tut es jetzt.Nun, ich habe es heute gerade veröffentlicht. Vielleicht können wir darauf aufbauen.

Jedenfalls dachte ich, es lohnt sich, es zu erwähnen.

Logan
quelle
Genau das habe ich gesucht. Vielen Dank, dass Sie sich die Zeit dafür genommen haben.
Thealfreds
Das einzige, was ich nicht herausgefunden und implementiert habe, ist die Build-Konfiguration für verschiedene NodeJS-Versionen. Ich denke, ich habe es auf Knoten 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) geschrieben, solange npm installes erfolgreich ist (mit anderen Worten, wenn Plugins kompiliert werden), ist es gut, für die Produktion zu gehen. Abgesehen davon, dass es auf verschiedene NodeJS-Versionen abzielt, würde ich sagen, dass es für die Produktion gepatcht ist oder dass es ziemlich stabil ist. Wenn es irgendwelche Fehler gibt, können Sie eine Pull-Anfrage senden oder ein Problem bei github posten :)
Logan
1

Sie können synchrone Shell-Operationen in NodeJs wie folgt ausführen:

var execSync = function(cmd) {

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

    //for linux use ; instead of &&
    //execute your command followed by a simple echo 
    //to file to indicate process is finished
    exec(cmd + " > c:\\stdout.txt && echo done > c:\\sync.txt");

    while (true) {
        //consider a timeout option to prevent infinite loop
        //NOTE: this will max out your cpu too!
        try {
            var status = fs.readFileSync('c:\\sync.txt', 'utf8');

            if (status.trim() == "done") {
                var res = fs.readFileSync("c:\\stdout.txt", 'utf8');
                fs.unlinkSync("c:\\stdout.txt"); //cleanup temp files
                fs.unlinkSync("c:\\sync.txt");
                return res;
            }
        } catch(e) { } //readFileSync will fail until file exists
    }

};

//won't return anything, but will take 10 seconds to run
console.log(execSync("sleep 10")); 

//assuming there are a lot of files and subdirectories, 
//this too may take a while, use your own applicable file path
console.log(execSync("dir /s c:\\usr\\docs\\"));

BEARBEITEN - Dieses Beispiel ist für Windows-Umgebungen gedacht. Passen Sie es bei Bedarf an Ihre eigenen Linux-Anforderungen an

Marcus Pope
quelle
Ja, und so schnell Ihre CPU möglicherweise beschwören kann ... Aber hey, wenn Sie etwas Böses "tun" müssen, ist Satan Ihr Mann, oder?
Marcus Pope
ok, das ist rein böse, aber großartig. Ich brauchte dies, um ein Dateisystemereignis browserify.on ('register') zu behandeln, das keinen Rückruf hatte. Rettete meinen Tag!
Robert Gould
Können Sie erklären, warum dies bei Javascript-Engines funktioniert? Sollte die while-Schleife nicht unendlich auf demselben Tick ausgeführt werden, während exec auf dem nächsten ausgeführt wird?
Badunk
Das Maximieren der CPU durch Warten ist ein schlechtes Design.
Louis
@ Louis-DominiqueDubeau Sicher, aber es gibt keine Alternative, die nicht von einer Quelle eines Drittanbieters abhängt, die möglicherweise plattformübergreifend kompatibel ist oder nicht. Es ist auch kein echtes Maximum an CPU, da das Betriebssystem dem NodeJS-Prozess nicht die volle Priorität einräumt. Ich denke, Synchronisierungsimplementierungen von Shell-Ops sind am Horizont oder vielleicht schon hier.
Marcus Pope
1

Ich hatte tatsächlich eine Situation, in der ich mehrere Befehle nacheinander von einem package.json-Vorinstallationsskript aus ausführen musste, so dass es sowohl unter Windows als auch unter Linux / OSX funktioniert, sodass ich mich nicht auf ein Nicht-Core-Modul verlassen konnte.

Das habe ich mir also ausgedacht:

#cmds.coffee
childproc = require 'child_process'

exports.exec = (cmds) ->
  next = ->
    if cmds.length > 0
      cmd = cmds.shift()
      console.log "Running command: #{cmd}"
      childproc.exec cmd, (err, stdout, stderr) ->
        if err? then console.log err
        if stdout? then console.log stdout
        if stderr? then console.log stderr
        next()
    else
      console.log "Done executing commands."

  console.log "Running the follows commands:"
  console.log cmds
  next()

Sie können es so verwenden:

require('./cmds').exec ['grunt coffee', 'nodeunit test/tls-config.js']

BEARBEITEN: Wie bereits erwähnt, wird dadurch weder die Ausgabe zurückgegeben noch das Ergebnis der Befehle in einem Knotenprogramm verwendet. Eine andere Idee dafür ist die Verwendung von LiveScript-Backcalls. http://livescript.net/

Jason Livesay
quelle
Danke, aber dies ist keine Antwort, da Ihr Code Befehle in Serie asynchron
ausführt
Wenn Sie eine Reihe von Befehlen gemäß meinem Beispiel synchron ausführen müssen, funktioniert dies. Ich glaube, ich habe Ihre Frage falsch verstanden, sorry. Ich habe der Antwort eine weitere Idee hinzugefügt.
Jason Livesay