node.js esegue il comando di sistema in modo sincrono


171

Ho bisogno della funzione node.js

result = execSync('node -v');

che eseguirà in modo sincrono la riga di comando indicata e restituirà tutto stdout'ed da quel testo di comando.

ps. La sincronizzazione è sbagliata. Lo so. Solo per uso personale.

AGGIORNARE

Ora abbiamo la soluzione di mgutz che ci dà il codice di uscita, ma non stdout! Sto ancora aspettando una risposta più precisa.

AGGIORNARE

mgutz ha aggiornato la sua risposta e la soluzione è qui :)
Inoltre, come menzionato dgo.a , c'è un modulo stand-alone exec-sync

AGGIORNAMENTO 2014-07-30

La libreria ShellJS è arrivata. Considera questa è la scelta migliore per ora.


AGGIORNAMENTO 2015-02-10

ALLA FINE! NodeJS 0.12 supporta execSyncnativamente.
Vedi documenti ufficiali


26
non lasciarti ingannare, la sincronizzazione non è sbagliata ... ANCHE in NodeJS tutto il tuo codice viene eseguito in modo sincrono a meno che tu non chiami esplicitamente un metodo asincrono ... se tutto fosse fatto in modo asincrono nulla sarebbe mai stato fatto. inoltre, preferendo metodi asincroni non significa che il tuo lungo calcolo non blocchi il tuo server. è una scelta. il fatto che i produttori di Node abbiano scelto di fornire metodi di file system sincroni insieme a quelli asincroni dimostra che esiste un posto anche per quelli.
flusso

2
Dove possiamo trovare la "libreria di emulazione della shell Unix" di cui stai parlando?
Florian,

@Florian intende ShellJS
xst

Risposte:


153

Node.js (dalla versione 0.12, quindi per un po ') supporta execSync:

child_process.execSync(command[, options])

Ora puoi farlo direttamente:

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

e farà quello che ti aspetti. (L'impostazione predefinita è convogliare i risultati di I / O al processo padre). Nota che puoi anche spawnSyncadesso.


6
dopo 10 ore di disperazione. GRAZIE DUDE
Tom Dev

Come posso disconnettermi da questo sottoprocesso?
JulianSoto,


54

Vedi la libreria execSync .

È abbastanza facile da fare con node-ffi . Non consiglierei per i processi del server, ma per le utility di sviluppo generali fa le cose. Installa la libreria.

npm install node-ffi

Script di esempio:

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

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

[MODIFICA giugno 2012: come ottenere 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'));

2
Come faresti a ricevere effettivamente qualcosa stdoutda questo? Tutto quello che posso ottenere è il codice di uscita del processo
Mark Kahn,

@cwolves: penso che async sarebbe meglio questo. ( Risposta di Ivo )
pvorb,

@pvorb - sì, tranne quando non puoi usare l'asincrono :)
Mark Kahn il

1
Esistono validi motivi per non utilizzare il martello asincrono per ogni unghia. Ad esempio, i motori di template sono asincroni in Express 3 e le funzioni di supporto (locali) devono essere sincrone. Cosa succede se queste funzioni di supporto devono compilare meno file in modo asincrono al volo?
mgutz,

8
Mi chiedo perché questo semplice execSyncnon faccia parte di child_process. Penso che dovrebbe essere.
Michael Härtl,

31

Usa il modulo ShellJS .

funzione exec senza fornire callback.

Esempio:

var version = exec('node -v').output;

2
Si noti che al momento della stesura del documento, i documenti menzionano che il sincrono exec()richiede molta CPU per processi lunghi.
Aram Kocharyan,

1
opzioni, {silent: true} è la chiave
Nick,

1
Questo fa qualcosa (oltre a fornire una sintassi più breve) questo non lo fa? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764,

23

Esiste un modulo eccellente per il controllo del flusso in node.js chiamato asyncblock . Se il wrapping del codice in una funzione è corretto per il tuo caso, è possibile prendere in considerazione il seguente esempio:

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

1
Ha chiesto esplicitamente la versione di sincronizzazione, non il controllo delle librerie di flusso.
Alexey Petrushin,

22
@AlexeyPetrushin Ogni domanda qui riguarda un obiettivo, non un modo particolare per raggiungerlo. Grazie per il downvoting però.
nab

1
Inoltre, questa è una risposta molto utile per gli utenti Windows; l'installazione exec-synco ffisu Windows ha un enorme sovraccarico (VC ++, SDK, Python, ecc.), ma questo è più leggero.
Mendhak,

10

Questo non è possibile in Node.js, entrambi child_process.spawne child_process.execsono stati costruiti da zero per essere asincroni.

Per i dettagli, consultare: https://github.com/ry/node/blob/master/lib/child_process.js

Se vuoi davvero avere questo blocco, quindi metti tutto ciò che deve accadere in seguito in un callback o costruisci la tua coda per gestirlo in modo bloccante, suppongo che potresti usare Async.js per questo compito.

Oppure, nel caso in cui tu abbia troppo tempo da spendere, scatta da solo in Node.js.


12
strano perché il modulo del file system ha chiamate sincrone. Perché non eseguire anche?
Alfred,

4
@Alfred Le chiamate di sincronizzazione FS sono principalmente disponibili per il caricamento delle configurazioni all'avvio del programma.
Ivo Wetzel,

3
@IvoWetzel - tisk tisk ... non abbiamo imparato a non dire mai qualcosa di impossibile? ;) vedere la mia soluzione di seguito.
Marcus Pope,

1
@IvoWetzel "Sincronizza le chiamate FS ..." - giusto, e talvolta vuoi, diciamo, emettere un comando per compilare qualcosa all'avvio del programma e continuare al completamento. sembra una svista. sono tutto per asincrono, ma sincrono ha i suoi pro e casi d'uso. Devo usarlo in modo giudizioso, ovviamente.
flusso

Async va bene, ma se WidgetB dipende dai risultati finali di WidgetA, tutti gli async nel mondo non faranno il loro lavoro. A volte i processi devono essere sincroni. Prova a cucinare in modo asincrono. ;)
Lloyd Sargent,

9

Questo è il modo più semplice che ho trovato:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(Da non confondere con execSync.)
Eseguire il comando shell in modo sincrono. Usalo per script di migrazione, programmi cli, ma non per un normale codice server.

Esempio:

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


5

Puoi farlo usando le fibre. Ad esempio, usando la mia libreria Common Node , il codice sarebbe simile al seguente:

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

3

Mi abituo a implementare "synchronous"roba alla fine della funzione di callback. Non molto carino, ma funziona. Se è necessario implementare una sequenza di esecuzioni da riga di comando, è necessario includere execalcune funzioni denominate e chiamarle in modo ricorsivo. Questo modello sembra essere utilizzabile per me:

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

3

Ho avuto un problema simile e ho finito per scrivere un'estensione del nodo per questo. Puoi dare un'occhiata al repository git. È open source e gratuito e tutte quelle cose buone!

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

ExecXI è un'estensione del nodo scritta in C ++ per eseguire i comandi della shell uno alla volta, trasmettendo l'output del comando alla console in tempo reale. Sono presenti modi concatenati e non concatenati opzionali; nel senso che puoi scegliere di interrompere lo script dopo che un comando ha esito negativo (incatenato), oppure puoi continuare come se nulla fosse successo!

Le istruzioni per l'uso si trovano nel file Leggimi . Sentiti libero di fare richieste pull o inviare problemi!

EDIT: Tuttavia non restituisce ancora lo stdout ... Li emette in tempo reale. Lo fa adesso. Bene, l'ho appena rilasciato oggi. Forse possiamo costruirci sopra.

Comunque, ho pensato che valesse la pena menzionarlo.


Questo è esattamente quello che stavo cercando. Grazie per aver dedicato del tempo per farlo.
thealfreds

L'unica cosa che non ho capito e implementato è la configurazione di build per diverse versioni di nodejs. Immagino di averlo scritto sul nodo 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) in modo da avere npm installsuccesso (in altre parole come compilatori di plugin), quindi è bene andare alla produzione. Oltre a scegliere diverse versioni di nodejs, direi che è stato riparato per la produzione o che è abbastanza stabile. Se ci sono dei bug, puoi inviare una richiesta pull o pubblicare un problema su github :)
Logan

1

puoi fare operazioni di shell sincrone in nodejs in questo modo:

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\\"));

EDIT: questo esempio è pensato per ambienti Windows, se necessario aggiustalo per le tue esigenze di Linux


Sì, e con la massima velocità possibile per la tua CPU ... Ehi, quando "hai bisogno" di fare qualcosa di malvagio, Satana è il tuo uomo giusto?
Marco Papa,

ok, questo è puro male, ma fantastico. Ne avevo bisogno per gestire un evento del file system browserify.on ('register'), che non aveva un callback. Mi hai salvato la giornata!
Robert Gould,

puoi spiegare perché funziona sui motori javascript? il ciclo while non dovrebbe essere eseguito all'infinito sullo stesso segno di spunta mentre exec viene eseguito sul successivo?
badunk,

Massimizzare la CPU con l'attesa impegnata è un cattivo design.
Louis,

@ Louis-DominiqueDubeau Certo, ma non esiste davvero alcuna alternativa che non dipenda da una fonte di terze parti che potrebbe essere o non essere compatibile con la piattaforma. Inoltre, non è un vero massimo dalla CPU perché il sistema operativo non darà la massima priorità al processo nodejs. Penso che le implementazioni di sincronizzazione delle operazioni della shell siano all'orizzonte o forse già qui.
Marco Papa,

1

In realtà ho avuto una situazione in cui avevo bisogno di eseguire più comandi uno dopo l'altro da uno script preinstallato package.json in un modo che funzionasse su Windows e Linux / OSX, quindi non potevo fare affidamento su un modulo non core.

Quindi questo è quello che mi è venuto in mente:

#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()

Puoi usarlo in questo modo:

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

EDIT: come sottolineato, questo in realtà non restituisce l'output o consente di utilizzare il risultato dei comandi in un programma Node. Un'altra idea è quella di utilizzare backcalls LiveScript. http://livescript.net/


Grazie, ma questa non è una risposta poiché il tuo codice esegue i comandi in serie in modo asincrono
sfigurato

Se hai bisogno di eseguire una serie di comandi in modo sincrono, come nel mio esempio, funzionerà. Penso di aver frainteso la tua domanda, scusa. Ho aggiunto un'altra idea alla risposta.
Jason Livesay
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.