node.js exécute la commande système de manière synchrone

171

J'ai besoin de la fonction node.js

result = execSync('node -v');

qui exécutera de manière synchrone la ligne de commande donnée et retournera tous les stdout'ed par ce texte de commande.

ps. La synchronisation est incorrecte. Je connais. Juste pour un usage personnel.

METTRE À JOUR

Nous avons maintenant la solution de mgutz qui nous donne le code de sortie, mais pas stdout! En attente d'une réponse plus précise.

METTRE À JOUR

mgutz a mis à jour sa réponse et la solution est ici :)
De plus, comme dgo.a l'a mentionné, il existe un module autonome exec-sync

MISE À JOUR 30/07/2014

La librairie ShellJS est arrivée. Considérez que c'est le meilleur choix pour le moment.


MISE À JOUR 10/02/2015

ENFIN! NodeJS 0.12 prend execSyncen charge nativement.
Voir la documentation officielle

défait
la source
26
ne vous laissez pas berner, la synchronisation n'est pas mauvaise ... MÊME dans NodeJS, tout votre code est exécuté de manière synchrone à moins que vous n'appeliez explicitement une méthode asynchrone ... si tout était fait de manière asynchrone, rien ne serait jamais fait. De plus, préférer les méthodes asynchrones ne signifie pas que votre long calcul ne bloquera pas votre serveur. c'est un choix. Le fait que les fabricants de Node aient choisi de fournir des méthodes de système de fichiers synchrones aux côtés des méthodes asynchrones montre simplement qu'il y a une place pour celles-ci également.
flux du
2
Où peut-on trouver la "bibliothèque d'émulation de shell Unix" dont vous parlez?
Florian
@Florian il veut dire ShellJS
xst

Réponses:

153

Node.js (depuis la version 0.12 - donc pendant un certain temps) prend en charge execSync:

child_process.execSync(command[, options])

Vous pouvez maintenant le faire directement:

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

et il fera ce que vous attendez. (Par défaut pour diriger les résultats d'E / S vers le processus parent). Notez que vous pouvez également spawnSyncmaintenant.

Benjamin Gruenbaum
la source
6
après 10 heures de désespoir. MERCI DUDE
Tom Dev
Comment puis-je me déconnecter de ce sous-processus?
JulianSoto le
@JulianSoto nodejs.org/api/…
divin
54

Voir la bibliothèque execSync .

C'est assez facile à faire avec node-ffi . Je ne recommanderais pas les processus de serveur, mais pour les utilitaires de développement généraux, cela fait avancer les choses. Installez la bibliothèque.

npm install node-ffi

Exemple de script:

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

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

[EDIT Jun 2012: Comment obtenir 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
la source
2
Comment feriez-vous pour envoyer quoi que stdoutce soit? Tout ce que je peux obtenir est le code de sortie du processus
Mark Kahn
@cwolves: Je pense qu'async serait mieux. ( Réponse d'Ivo )
pvorb
@pvorb - ouais, sauf quand vous ne pouvez pas utiliser async :)
Mark Kahn
1
Il existe des raisons valables de ne pas utiliser le marteau asynchrone pour chaque clou. Par exemple, les moteurs de modèles sont asynchrones dans Express 3 et les fonctions d'assistance (locales) doivent être synchrones. Que faire si ces fonctions d'assistance doivent compiler moins de fichiers de manière asynchrone à la volée?
mgutz
8
Je me demande pourquoi ce simple execSyncne fait pas partie de child_process. Je pense que ça devrait l'être.
Michael Härtl
31

Utilisez le module ShellJS .

exec fonction sans fournir de rappel.

Exemple:

var version = exec('node -v').output;
falko
la source
2
Notez qu'au moment de la rédaction, la documentation mentionne que le synchrone exec()est gourmand en CPU pour les processus longs.
Aram Kocharyan
1
options, {silent: true} est la clé
Nick
1
Cela fait quelque chose (autre que fournir une syntaxe plus courte), ce n'est pas le cas? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764
23

Il existe un excellent module de contrôle de flux dans node.js appelé asyncblock . Si l'encapsulation du code dans une fonction convient à votre cas, l'exemple suivant peut être envisagé:

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...');
});
attraper
la source
1
Il a demandé explicitement la version de synchronisation, pas les bibliothèques de flux de contrôle.
Alexey Petrushin
22
@AlexeyPetrushin Chaque question ici concerne un objectif, pas une manière particulière de l'atteindre. Merci d'avoir voté contre.
attraper
1
En outre, c'est une réponse très utile pour les utilisateurs de Windows; l'installation exec-syncou ffisur Windows a une surcharge énorme (VC ++, SDK, Python, etc.), mais c'est plus léger.
Mendhak le
10

Cela n'est pas possible dans Node.js, à la fois child_process.spawnetchild_process.exec ont été construits à partir de zéro pour être asynchrones.

Pour plus de détails, voir: https://github.com/ry/node/blob/master/lib/child_process.js

Si vous voulez vraiment avoir ce blocage, mettez tout ce qui doit se passer par la suite dans un rappel, ou créez votre propre file d'attente pour gérer cela de manière bloquante, je suppose que vous pouvez utiliser Async.js pour cette tâche.

Ou, au cas où vous auriez beaucoup trop de temps à passer, piratez vous-même dans Node.js.

Ivo Wetzel
la source
12
étrange car le module du système de fichiers a des appels synchrones. Pourquoi ne pas également exécuter?
Alfred
4
@Alfred Les appels sync FS sont principalement là pour charger les configurations au démarrage du programme.
Ivo Wetzel
3
@IvoWetzel - tisk tisk ... n'avons-nous pas appris à ne jamais dire que quelque chose est impossible? ;) voir ma solution ci-dessous.
Marcus Pope
1
@IvoWetzel "Le sync FS appelle ..." - à droite, et parfois vous voulez, par exemple, émettre une commande pour compiler quelque chose au démarrage du programme et continuer à la fin. ressemble à un oubli. Je suis tout à fait pour asynchrone, mais synchrone a ses avantages et ses cas d'utilisation. je dois l'utiliser de manière judicieuse, bien sûr.
débit du
Async est bien, mais si WidgetB dépend des résultats finaux de WidgetA, tout l'async dans le monde ne fera pas le travail. Parfois, les processus doivent être synchrones. Essayez de cuisiner de manière asynchrone. ;)
Lloyd Sargent
9

C'est le moyen le plus simple que j'ai trouvé:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(à ne pas confondre avec execSync.)
Exécutez la commande shell de manière synchrone. Utilisez-le pour les scripts de migration, les programmes cli, mais pas pour le code serveur normal.

Exemple:

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

Vous pouvez y parvenir en utilisant des fibres. Par exemple, en utilisant ma bibliothèque Common Node , le code ressemblerait à ceci:

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

Je m'habitue à implémenter des "synchronous"trucs à la fin de la fonction de rappel. Pas très sympa, mais ça marche. Si vous avez besoin d'implémenter une séquence d'exécutions de ligne de commande, vous devez encapsuler execdans une fonction nommée et l'appeler récursivement. Ce modèle me semble utilisable:

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
la source
3

J'ai eu un problème similaire et j'ai fini par écrire une extension de nœud pour cela. Vous pouvez extraire le référentiel git. C'est open source et gratuit et toutes ces bonnes choses!

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

ExecXI est une extension de nœud écrite en C ++ pour exécuter les commandes shell une par une, en envoyant la sortie de la commande à la console en temps réel. Des voies optionnelles chaînées et non chaînées sont présentes; ce qui signifie que vous pouvez choisir d'arrêter le script après l'échec d'une commande (chaînée), ou vous pouvez continuer comme si de rien n'était!

Les instructions d'utilisation se trouvent dans le fichier ReadMe . N'hésitez pas à faire des pull requests ou à soumettre des problèmes!

EDIT: Cependant, il ne renvoie pas encore le stdout ... Il suffit de les sortir en temps réel. C'est le cas maintenant.Eh bien, je viens de le publier aujourd'hui. Peut-être pouvons-nous bâtir dessus.

Quoi qu'il en soit, j'ai pensé qu'il valait la peine de le mentionner.

Logan
la source
C'est exactement ce que je recherchais. Merci d'avoir pris le temps de faire cela.
thealfreds
La seule chose que je n'ai pas comprise et implémentée est la configuration de construction pour les différentes versions de nodejs. Je suppose que je l'ai écrit sur le nœud 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) donc tant qu'il npm installréussit (en d'autres termes que le plugin compile), alors il est bon de passer à la production. En plus de cibler différentes versions de nodejs, je dirais qu'il est corrigé pour la production, ou qu'il est assez stable. S'il y a des bogues, vous pouvez envoyer une pull request ou publier un problème sur github :)
Logan
1

vous pouvez faire des opérations shell synchrones dans nodejs comme ceci:

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 - cet exemple est destiné aux environnements Windows, ajustez-les pour vos propres besoins Linux si nécessaire

Marcus Pope
la source
Oui, et aussi vite que votre CPU peut éventuellement invoquer ... Mais bon quand vous "avez" besoin "de faire quelque chose de mal, Satan est votre homme non?
Marcus Pope
ok, c'est du mal pur, mais génial. J'en avais besoin pour gérer un événement de système de fichiers browserify.on ('register'), qui n'avait pas de rappel. J'ai sauvé ma journée!
Robert Gould
pouvez-vous expliquer pourquoi cela fonctionne sur les moteurs javascript? la boucle while ne doit-elle pas être exécutée indéfiniment sur le même tick tandis que exec s'exécute sur le suivant?
badunk
Maximiser le processeur par une attente occupée est une mauvaise conception.
Louis
@ Louis-DominiqueDubeau Bien sûr, mais il n'y a pas vraiment d'alternative qui ne dépende pas d'une source tierce qui peut ou non être compatible multiplateforme. Ce n'est pas non plus une véritable maximisation du processeur car le système d'exploitation ne donnera pas la priorité totale au processus nodejs. Je pense que les implémentations de synchronisation des opérations shell sont à l'horizon ou peut-être déjà ici.
Marcus Pope
1

En fait, j'ai eu une situation où je devais exécuter plusieurs commandes l'une après l'autre à partir d'un script de préinstallation package.json d'une manière qui fonctionnerait à la fois sur Windows et Linux / OSX, donc je ne pouvais pas compter sur un module non-core.

Voici donc ce que j'ai proposé:

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

Vous pouvez l'utiliser comme ceci:

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

EDIT: comme indiqué, cela ne renvoie pas réellement la sortie ou ne vous permet pas d'utiliser le résultat des commandes dans un programme Node. Une autre idée pour cela est d'utiliser des backcalls LiveScript. http://livescript.net/

Jason Livesay
la source
Merci, mais ce n'est pas une réponse puisque votre code exécute des commandes en série de manière asynchrone
disfated
Si vous avez besoin d'exécuter une série de commandes de manière synchrone selon mon exemple, cela fonctionnera. Je pense que j'ai mal compris votre question, désolé. J'ai ajouté une autre idée à la réponse.
Jason Livesay