Ejecute y obtenga el resultado de un comando de shell en node.js

113

En un node.js, me gustaría encontrar una manera de obtener el resultado de un comando de terminal Unix. ¿Hay alguna forma de hacer esto?

function getCommandOutput(commandString){
    // now how can I implement this function?
    // getCommandOutput("ls") should print the terminal output of the shell command "ls"
}
Anderson Green
fuente
¿Es esto un duplicado o describe algo completamente diferente? stackoverflow.com/questions/7183307/…
Anderson Green
Esto te puede interesar.
benekastah
Use npmjs.com/package/cross-spawn
Andrew Koster

Respuestas:

142

Así es como lo hago en un proyecto en el que estoy trabajando ahora.

var exec = require('child_process').exec;
function execute(command, callback){
    exec(command, function(error, stdout, stderr){ callback(stdout); });
};

Ejemplo: recuperando el usuario de git

module.exports.getGitUser = function(callback){
    execute("git config --global user.name", function(name){
        execute("git config --global user.email", function(email){
            callback({ name: name.replace("\n", ""), email: email.replace("\n", "") });
        });
    });
};
Renato Gama
fuente
3
¿Es posible hacer que esta función devuelva la salida del comando? (Eso es lo que estaba tratando de hacer)
Anderson Green
1
eso es lo que hace ese código. Eche un vistazo al ejemplo de la edición que acabo de hacer
Renato Gama
2
@AndersonGreen No querrá que la función regrese normalmente con el teclado de "retorno", porque está ejecutando el comando de shell de forma asincrónica. Como resultado, es mejor pasar una devolución de llamada con código que debería ejecutarse cuando el comando de shell esté completo.
Nick McCurdy
1
Ay, su primera muestra ignora la posibilidad de un error cuando llama a esa devolución de llamada. Me pregunto qué pasa con stdoutsi hay un error. Con suerte determinista y documentado.
doug65536
31

Estás buscando child_process

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

child = exec(command,
   function (error, stdout, stderr) {
      console.log('stdout: ' + stdout);
      console.log('stderr: ' + stderr);
      if (error !== null) {
          console.log('exec error: ' + error);
      }
   });

Como señaló Renato, ahora también hay algunos paquetes ejecutivos síncronos, consulte sync-exec que podrían ser más lo que está buscando. Sin embargo, tenga en cuenta que node.js está diseñado para ser un servidor de red de alto rendimiento de un solo subproceso, por lo que si para eso está buscando usarlo, manténgase alejado de las cosas de sync-exec a menos que solo lo esté usando durante el inicio o algo.

hexista
fuente
1
En este caso, ¿cómo puedo obtener la salida del comando? ¿Es "stdout" el que contiene la salida de la línea de comandos?
Anderson Green
Además, ¿es posible hacer algo similar sin usar una devolución de llamada?
Anderson Green
Correcto, stdout contiene la salida del programa. Y no, no es posible hacerlo sin devoluciones de llamada. Todo en node.js está orientado a no ser bloqueante, lo que significa que cada vez que hagas IO, usarás devoluciones de llamada.
hexista
Tenga en cuenta que si está buscando usar javascript para hacer cosas tipo scripty en las que realmente desea esperar en la salida y ese tipo de cosas, puede mirar el shell v8, d8
hexist
@hexist hay algunos Syncmétodos disponibles de forma nativa, aun así en mi humilde opinión debería evitarse
Renato Gama
29

Si está usando un nodo posterior a 7.6 y no le gusta el estilo de devolución de llamada, también puede usar la promisifyfunción de node-util con async / awaitpara obtener comandos de shell que se lean limpiamente. Aquí hay un ejemplo de la respuesta aceptada, usando esta técnica:

const { promisify } = require('util');
const exec = promisify(require('child_process').exec)

module.exports.getGitUser = async function getGitUser () {
  const name = await exec('git config --global user.name')
  const email = await exec('git config --global user.email')
  return { name, email }
};

Esto también tiene el beneficio adicional de devolver una promesa rechazada en comandos fallidos, que se pueden manejar try / catchdentro del código asíncrono.

Ansikt
fuente
¿Has probado esto? Estoy consiguiendo { stdout: string, stderr: string }como resultado de laawait exec(...)
fwoelffel
1
Sí, debería haber aclarado que esto le brinda la salida completa de shell, incluidos stdout y stderr. Si sólo desea la salida, se puede cambiar la última línea a: return { name: name.stdout.trim(), email: email.stdout.trim() }.
Ansikt
16

Gracias a la respuesta de Renato, he creado un ejemplo realmente básico:

const exec = require('child_process').exec

exec('git config --global user.name', (err, stdout, stderr) => console.log(stdout))

Simplemente imprimirá su nombre de usuario global de git :)

Damjan Pavlica
fuente
11

Requisitos

Esto requerirá Node.js 7 o posterior con soporte para Promises y Async / Await.

Solución

Cree una función contenedora que aproveche las promesas para controlar el comportamiento del child_process.execcomando.

Explicación

Usando promesas y una función asincrónica, puede imitar el comportamiento de un shell que devuelve la salida, sin caer en un infierno de devolución de llamada y con una API bastante ordenada. Con la awaitpalabra clave, puede crear una secuencia de comandos que se lea fácilmente, sin dejar de poder realizar el trabajo child_process.exec.

Muestra de código

const childProcess = require("child_process");

/**
 * @param {string} command A shell command to execute
 * @return {Promise<string>} A promise that resolve to the output of the shell command, or an error
 * @example const output = await execute("ls -alh");
 */
function execute(command) {
  /**
   * @param {Function} resolve A function that resolves the promise
   * @param {Function} reject A function that fails the promise
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
   */
  return new Promise(function(resolve, reject) {
    /**
     * @param {Error} error An error triggered during the execution of the childProcess.exec command
     * @param {string|Buffer} standardOutput The result of the shell command execution
     * @param {string|Buffer} standardError The error resulting of the shell command execution
     * @see https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
     */
    childProcess.exec(command, function(error, standardOutput, standardError) {
      if (error) {
        reject();

        return;
      }

      if (standardError) {
        reject(standardError);

        return;
      }

      resolve(standardOutput);
    });
  });
}

Uso

async function main() {
  try {
    const passwdContent = await execute("cat /etc/passwd");

    console.log(passwdContent);
  } catch (error) {
    console.error(error.toString());
  }

  try {
    const shadowContent = await execute("cat /etc/shadow");

    console.log(shadowContent);
  } catch (error) {
    console.error(error.toString());
  }
}

main();

Salida de muestra

root:x:0:0::/root:/bin/bash
[output trimmed, bottom line it succeeded]

Error: Command failed: cat /etc/shadow
cat: /etc/shadow: Permission denied

Pruébelo en línea.

Responde .

Recursos externos

Promesas .

child_process.exec.

Node.js mesa de apoyo .

Amin NAIRI
fuente