Execute e obtenha a saída de um comando shell em node.js


113

Em um node.js, gostaria de encontrar uma maneira de obter a saída de um comando de terminal Unix. Há alguma maneira de fazer isso?

function getCommandOutput(commandString){
    // now how can I implement this function?
    // getCommandOutput("ls") should print the terminal output of the shell command "ls"
}

É uma duplicata ou descreve algo completamente diferente? stackoverflow.com/questions/7183307/…
— Anderson Green,

Isso pode interessar a você.
— Benekastah,

Respostas:


142

É assim que faço em um projeto em que estou trabalhando agora.

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

Exemplo: Recuperando o usuário 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", "") });
        });
    });
};

3
É possível fazer com que esta função retorne a saída do comando? (Isso é o que eu estava tentando fazer.)
— Anderson Green,

1
isso é o que esse código faz. veja o exemplo da edição que acabei de fazer
— Renato Gama

2
@AndersonGreen Você não gostaria que a função retornasse normalmente com o teclado "return", porque ela está executando o comando shell de forma assíncrona. Como resultado, é melhor passar um retorno de chamada com código que deve ser executado quando o comando shell for concluído.
— Nick McCurdy

1
Ouch, sua primeira amostra ignora a possibilidade de um erro ao chamar esse retorno de chamada. Eu me pergunto o que acontecerá stdoutse houver um erro. Esperançosamente determinístico e documentado.
— doug65536,

31

Você está procurando 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 apontado por Renato, existem alguns pacotes exec síncronos por aí agora também, veja sync-exec que pode ser mais o que você está procurando. Lembre-se, porém, de que o node.js foi projetado para ser um servidor de rede de alto desempenho com thread único, então, se é para isso que você está procurando, fique longe de coisas do tipo sync-exec, a menos que você o use apenas durante a inicialização ou alguma coisa.


1
Nesse caso, como posso obter a saída do comando? É "stdout" que contém a saída da linha de comando?
— Anderson Green,

Além disso, é possível fazer algo semelhante sem usar um retorno de chamada?
— Anderson Green,

Correto, stdout contém a saída do programa. E não, não é possível fazer isso sem callbacks. Tudo em node.js é orientado para não bloquear, o que significa que toda vez que você fizer IO, você usará callbacks.
— hexista de

Note que se você está procurando usar javascript para fazer coisas com script onde você realmente quer esperar pela saída e esse tipo de coisa, você pode olhar para o shell v8, d8
— hexist

@hexist existem alguns Syncmétodos nativamente disponíveis, embora IMHO deva ser evitado
— Renato Gama

29

Se você está usando o node posterior a 7.6 e não gosta do estilo de retorno de chamada, você também pode usar a promisifyfunção do node-util com async / awaitpara obter comandos shell que sejam lidos de forma limpa. Aqui está um exemplo de resposta aceita, 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 }
};

Isso também tem o benefício adicional de retornar uma promessa rejeitada em comandos com falha, que pode ser tratada try / catchdentro do código assíncrono.


Você já tentou isso? Estou recebendo { stdout: string, stderr: string }como resultado paraawait exec(...)
— fwoelffel de

1
Sim, eu deveria ter esclarecido que isso fornece a saída completa do shell, incluindo stdout e stderr. Se você quer apenas a saída, você pode mudar a última linha para: return { name: name.stdout.trim(), email: email.stdout.trim() }.
— Ansikt

16

Graças à resposta do Renato, criei um exemplo realmente básico:

const exec = require('child_process').exec

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

Ele apenas imprimirá seu nome de usuário git global :)


11

Requisitos

Isso exigirá Node.js 7 ou posterior com suporte para Promises e Async / Await.

Solução

Crie uma função de wrapper que aproveite as promessas de controlar o comportamento do child_process.execcomando.

Explicação

Usando promessas e uma função assíncrona, você pode imitar o comportamento de um shell retornando a saída, sem cair em um inferno de callback e com uma API bem organizada. Usando a awaitpalavra - chave, você pode criar um script de fácil leitura e, ao mesmo tempo, realizar o trabalho child_process.exec.

Amostra 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();

Saída de amostra

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

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

Experimente online.

Repl.it .

Fontes externas

Promessas .

child_process.exec.

Mesa de apoio Node.js .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.