node.js ejecuta el comando del sistema sincrónicamente

171

Necesito en la función node.js

result = execSync('node -v');

eso ejecutará sincrónicamente la línea de comando dada y devolverá todo stdout'ed por ese texto de comando.

PD. La sincronización es incorrecta. Lo sé. Solo para uso personal.

ACTUALIZAR

Ahora tenemos la solución de mgutz que nos da el código de salida, ¡pero no stdout! Todavía estoy esperando una respuesta más precisa.

ACTUALIZAR

mgutz actualizó su respuesta y la solución está aquí :)
Además, como mencionó dgo.a , hay un módulo independiente exec-sync

ACTUALIZACIÓN 2014-07-30

ShellJS lib llegó. Considere que esta es la mejor opción por ahora.


ACTUALIZACIÓN 2015-02-10

¡AL FINAL! NodeJS 0.12 es compatible de execSyncforma nativa.
Ver documentos oficiales

disgustado
fuente
26
no te dejes engañar, la sincronización no está mal ... INCLUSO en NodeJS todo tu código se ejecuta sincrónicamente a menos que llames explícitamente a un método asíncrono ... si todo se hiciera de forma asincrónica , nunca se haría nada . Además, preferir métodos asincrónicos no significa que su largo cálculo no bloqueará su servidor. Es una elección. que los creadores de Node optaron por proporcionar métodos de sistema de archivos síncronos junto con los asíncronos solo demuestra que también hay un lugar para ellos.
fluye el
2
¿Dónde podemos encontrar la "biblioteca de emulación de shell Unix" de la que estás hablando?
Florian
@Florian se refiere a ShellJS
xst

Respuestas:

153

Node.js (desde la versión 0.12, así que por un tiempo) admite execSync:

child_process.execSync(command[, options])

Ahora puede hacer esto directamente:

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

y hará lo que esperas. (El valor predeterminado es canalizar los resultados de E / S al proceso principal). Tenga en cuenta que también puede hacerlo spawnSyncahora.

Benjamin Gruenbaum
fuente
66
Después de 10 horas de desesperación. GRACIAS DUDE
Tom Dev
¿Cómo puedo desconectarme de este subproceso?
JulianSoto
@JulianSoto nodejs.org/api/…
divino
54

Ver biblioteca execSync .

Es bastante fácil de hacer con node-ffi . No lo recomendaría para los procesos del servidor, pero para las utilidades de desarrollo en general hace las cosas. Instala la biblioteca.

npm install node-ffi

Script de ejemplo:

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

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

[EDITAR junio de 2012: Cómo obtener 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
fuente
2
¿Cómo harías para recibir algo stdoutde esto? Todo lo que puedo obtener es el código de salida del proceso
Mark Kahn
@cwolves: Creo que asíncrono sería mejor esto. ( Respuesta de Ivo )
pvorb
@pvorb - sí, excepto cuando no puedes usar async :)
Mark Kahn
1
Hay razones válidas para no usar el martillo asíncrono para cada clavo. Por ejemplo, los motores de plantillas son asíncronos en Express 3 y las funciones auxiliares (locales) deben ser sincrónicas. ¿Qué sucede si esas funciones auxiliares necesitan compilar archivos Less de forma asincrónica sobre la marcha?
mgutz
8
Me pregunto por qué esto simple execSyncno es parte de child_process. Creo que debería ser.
Michael Härtl
31

Utilice el módulo ShellJS .

función ejecutiva sin proporcionar devolución de llamada.

Ejemplo:

var version = exec('node -v').output;
falko
fuente
2
Tenga en cuenta que en el momento de la escritura, los documentos mencionan que el síncrono exec()es intensivo en CPU para procesos largos.
Aram Kocharyan
1
opciones, {silent: true} es la clave
Nick
1
¿Esto hace algo (aparte de proporcionar una sintaxis más corta) que esto no hace? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764
23

Hay un excelente módulo para el control de flujo en node.js llamado asyncblock . Si envolver el código en una función está bien para su caso, se puede considerar el siguiente ejemplo:

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...');
});
coger
fuente
1
Preguntó explícitamente sobre la versión de sincronización, no sobre las bibliotecas de flujo de control.
Alexey Petrushin
22
@AlexeyPetrushin Cada pregunta aquí es sobre un objetivo, no sobre una forma particular de lograrlo. Gracias por votar a favor sin embargo.
nab
1
Además, esta es una respuesta muy útil para los usuarios de Windows; instalar exec-synco ffien Windows tiene una gran sobrecarga (VC ++, SDK, Python, etc.), pero esto es más ligero.
Mendhak
10

Esto no es posible en Node.js, ambos child_process.spawnychild_process.exec se construyeron desde cero para ser asíncronos.

Para más detalles, consulte: https://github.com/ry/node/blob/master/lib/child_process.js

Si realmente desea tener este bloqueo, coloque todo lo que debe suceder después en una devolución de llamada, o cree su propia cola para manejar esto de manera bloqueante, supongo que podría usar Async.js para esta tarea.

O, en caso de que tenga demasiado tiempo para pasar, piratee en Node.js mismo.

Ivo Wetzel
fuente
12
extraño porque el módulo del sistema de archivos tiene llamadas sincrónicas. ¿Por qué no ejecutar también?
Alfred
44
@Alfred Las llamadas de sincronización FS están principalmente allí para cargar configuraciones al inicio del programa.
Ivo Wetzel
3
@IvoWetzel - tisk tisk ... ¿no hemos aprendido a nunca decir que algo es imposible? ;) ver mi solución a continuación.
Marcus Pope el
1
@IvoWetzel "La sincronización FS llama ...": correcto, y a veces desea, por ejemplo, emitir un comando para compilar algo al inicio del programa y continuar al finalizar. parece un descuido. Estoy a favor de asíncrono, pero sincrónico tiene sus ventajas y casos de uso. tengo que usarlo de una manera juiciosa, por supuesto.
fluye el
Async está bien, pero si WidgetB depende de los resultados finales de WidgetA, toda la async en el mundo no hará el trabajo. A veces los procesos tienen que ser sincrónicos. Intenta cocinar asincrónicamente. ;)
Lloyd Sargent
9

Esta es la forma más fácil que encontré:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(no debe confundirse con execSync).
Ejecute el comando de shell sincrónicamente. Use esto para scripts de migración, programas cli, pero no para el código de servidor normal.

Ejemplo:

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

Puedes lograr esto usando fibras. Por ejemplo, usando mi biblioteca de Nodo común , el código se vería así:

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

Me acostumbro a implementar "synchronous"cosas al final de la función de devolución de llamada. No es muy agradable, pero funciona. Si necesita implementar una secuencia de ejecuciones de línea de comando, debe ajustarse execa alguna función con nombre y llamarla recursivamente. Este patrón parece ser utilizable para mí:

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
fuente
3

Tuve un problema similar y terminé escribiendo una extensión de nodo para esto. Puedes consultar el repositorio de git. ¡Es de código abierto y gratuito y todas esas cosas buenas!

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

ExecXI es una extensión de nodo escrita en C ++ para ejecutar comandos de shell uno por uno, enviando la salida del comando a la consola en tiempo real. Las formas opcionales encadenadas y desencadenadas están presentes; lo que significa que puede optar por detener el script después de que un comando falle (encadenado), o puede continuar como si nada hubiera pasado.

Las instrucciones de uso están en el archivo Léame . ¡No dude en hacer solicitudes de extracción o enviar problemas!

EDITAR: Sin embargo, aún no devuelve el stdout ... Solo los genera en tiempo real. Lo hace ahora Bueno, lo acabo de lanzar hoy. Tal vez podamos construir sobre eso.

De todos modos, pensé que valía la pena mencionarlo.

Logan
fuente
Esto es exactamente lo que he estado buscando. Gracias por dedicar tiempo para hacer esto.
thealfreds
Lo único que no he descubierto e implementado es la configuración de compilación para diferentes versiones de nodejs. Supongo que lo escribí en el nodo 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) siempre que npm installtenga éxito (en otras palabras, como compilaciones de complementos), entonces es bueno ir a la producción. Además de apuntar a diferentes versiones de nodejs, diría que está parcheado para producción, o que es bastante estable. Si hay algún error, puede enviar una solicitud de extracción o publicar un problema en github :)
Logan
1

puedes hacer operaciones de shell síncronas en nodejs así:

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

EDITAR: este ejemplo está destinado a entornos de Windows, ajústelo para sus propias necesidades de Linux si es necesario

Marcus Pope
fuente
Sí, y tan rápido como tu CPU pueda convocar ... Pero oye, cuando "necesitas" hacer algo malo, Satanás es tu hombre, ¿verdad?
Marcus Pope
ok, esto es puro mal, pero asombroso. Necesitaba esto para manejar un evento del sistema de archivos browserify.on ('registrarse'), que no tenía una devolución de llamada. Me salvó el día!
Robert Gould
¿Puedes explicar por qué esto funciona en los motores de JavaScript? ¿No debería ejecutarse el bucle while infinitamente en el mismo tic mientras se ejecuta exec en el siguiente?
badunk
Maximizar la CPU al esperar ocupado es un mal diseño.
Louis
@ Louis-DominiqueDubeau Claro, pero en realidad no hay ninguna alternativa que no dependa de alguna fuente de terceros que pueda o no ser compatible con múltiples plataformas. Tampoco es un verdadero máximo de la CPU porque el sistema operativo no dará prioridad total al proceso de nodejs. Creo que las implementaciones de sincronización de operaciones de shell están en el horizonte o tal vez aquí.
Marcus Pope el
1

En realidad, tuve una situación en la que necesitaba ejecutar múltiples comandos uno tras otro desde un script de preinstalación package.json de una manera que funcionara tanto en Windows como en Linux / OSX, por lo que no podía confiar en un módulo no básico.

Entonces esto es lo que se me ocurrió:

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

Puedes usarlo así:

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

EDITAR: como se señaló, esto en realidad no devuelve la salida ni le permite usar el resultado de los comandos en un programa Node. Otra idea para eso es usar las llamadas de LiveScript. http://livescript.net/

Jason Livesay
fuente
Gracias, pero esta no es una respuesta, ya que su código ejecuta comandos en serie de forma asincrónica
desapareció
Si necesita ejecutar una serie de comandos sincrónicamente según mi ejemplo, funcionará. Sin embargo, creo que entendí mal tu pregunta, lo siento. Agregué otra idea a la respuesta.
Jason Livesay