Esegui e ottieni l'output di un comando di shell in node.js


113

In un node.js, vorrei trovare un modo per ottenere l'output di un comando da terminale Unix. C'è un modo per fare questo?

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

È un duplicato o descrive qualcosa di completamente diverso? stackoverflow.com/questions/7183307/…
Anderson Green

Questo potrebbe interessarti.
benekastah

Risposte:


142

Questo è il modo in cui lo faccio in un progetto a cui sto lavorando ora.

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

Esempio: recupero dell'utente 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
È possibile fare in modo che questa funzione restituisca l'output del comando? (Questo è quello che stavo cercando di fare.)
Anderson Green

1
questo è ciò che fa quel codice. dai un'occhiata all'esempio della modifica che ho appena fatto
Renato Gama

2
@AndersonGreen Non vorresti che la funzione ritorni normalmente con la tastiera "return", perché sta eseguendo il comando della shell in modo asincrono. Di conseguenza, è meglio passare un callback con codice che dovrebbe essere eseguito quando il comando della shell è completo.
Nick McCurdy

1
Ahi, il tuo primo campione ignora la possibilità di un errore quando richiama quel callback. Mi chiedo cosa succede stdoutse c'è un errore. Si spera deterministico e documentato.
doug65536

31

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

Come sottolineato da Renato, ci sono anche alcuni pacchetti exec sincroni ora disponibili, vedi sync-exec che potrebbe essere più quello che stai cercando. Tieni presente, tuttavia, che node.js è progettato per essere un server di rete ad alte prestazioni con thread singolo, quindi se è per questo che stai cercando di usarlo, stai lontano da cose di tipo sync-exec a meno che tu non lo usi solo durante l'avvio o qualcosa.


1
In questo caso, come posso ottenere l'output del comando? È "stdout" che contiene l'output della riga di comando?
Anderson Green

Inoltre, è possibile fare qualcosa di simile senza utilizzare una richiamata?
Anderson Green

Corretto, stdout contiene l'output del programma. E no, non è possibile farlo senza callback. Tutto in node.js è orientato all'essere non bloccante, il che significa che ogni volta che fai IO utilizzerai i callback.
hexist

Nota che se stai cercando di utilizzare javascript per fare cose tipo script in cui vuoi davvero aspettare l'output e quel genere di cose, potresti guardare la shell v8, d8
hexist

@hexist ci sono alcuni Syncmetodi nativamente disponibili, anche se IMHO dovrebbe essere evitato
Renato Gama

29

Se stai usando node dopo la 7.6 e non ti piace lo stile di callback, puoi anche usare la promisifyfunzione di node-util con async / awaitper ottenere comandi di shell che leggono in modo pulito. Ecco un esempio della risposta accettata, utilizzando questa tecnica:

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

Questo ha anche l'ulteriore vantaggio di restituire una promessa rifiutata su comandi non riusciti, che può essere gestita try / catchall'interno del codice asincrono.


Hai provato questo? Ricevo { stdout: string, stderr: string }come risultato per ilawait exec(...)
fwoelffel

1
Sì, avrei dovuto chiarire che questo ti dà l' output completo della shell, inclusi sia stdout che stderr. Se si desidera solo l'uscita, è possibile modificare l'ultima riga a: return { name: name.stdout.trim(), email: email.stdout.trim() }.
Ansikt

16

Grazie alla risposta di Renato, ho creato un esempio davvero semplice:

const exec = require('child_process').exec

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

Stamperà solo il tuo nome utente git globale :)


11

Requisiti

Ciò richiederà Node.js 7 o versioni successive con supporto per Promises e Async / Await.

Soluzione

Crea una funzione wrapper che faccia leva sulle promesse di controllare il comportamento del child_process.execcomando.

Spiegazione

Utilizzando promesse e una funzione asincrona, puoi imitare il comportamento di una shell che restituisce l'output, senza cadere in un inferno di callback e con un'API piuttosto ordinata. Usando la awaitparola chiave, puoi creare uno script che si legge facilmente, pur essendo in grado di portare a termine il lavoro child_process.exec.

Esempio di codice

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

Output di esempio

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

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

Provalo online.

Repl.it .

Risorse esterne

Promesse .

child_process.exec.

Piano di appoggio Node.js .

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.