Exécutez et obtenez la sortie d'une commande shell dans node.js

113

Dans un node.js, j'aimerais trouver un moyen d'obtenir la sortie d'une commande de terminal Unix. Y a-t-il un moyen de faire ça?

function getCommandOutput(commandString){
    // now how can I implement this function?
    // getCommandOutput("ls") should print the terminal output of the shell command "ls"
}
Vert Anderson
la source
S'agit-il d'un doublon ou décrit-il quelque chose de complètement différent? stackoverflow.com/questions/7183307/…
Anderson Green
Cela pourrait vous intéresser.
benekastah
Utilisez npmjs.com/package/cross-spawn
Andrew Koster

Réponses:

143

C'est comme ça que je le fais dans un projet sur lequel je travaille actuellement.

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

Exemple: récupération d'un utilisateur 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
la source
3
Est-il possible de faire en sorte que cette fonction renvoie la sortie de la commande? (C'est ce que j'essayais de faire.)
Anderson Green
1
c'est ce que fait ce code. jetez un oeil à l'exemple au montage que je viens de faire
Renato Gama
2
@AndersonGreen Vous ne voudriez pas que la fonction retourne normalement avec le clavier "return", car elle exécute la commande shell de manière asynchrone. En conséquence, il est préférable de transmettre un rappel avec du code qui devrait s'exécuter lorsque la commande shell est terminée.
Nick McCurdy
1
Aïe, votre premier échantillon ignore la possibilité d'une erreur lorsqu'il appelle ce rappel. Je me demande ce qui arrive stdouts'il y a une erreur. Espérons que déterministe et documenté.
doug65536
31

Vous recherchez 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);
      }
   });

Comme l'a souligné Renato, il existe maintenant des packages exécutables synchrones , voir sync-exec qui pourrait être plus ce que vous recherchez. Gardez à l'esprit cependant que node.js est conçu pour être un serveur réseau haute performance à thread unique, donc si c'est pour cela que vous cherchez à l'utiliser, évitez les trucs de sync-exec à moins que vous ne l'utilisiez uniquement au démarrage. ou quelque chose.

hexist
la source
1
Dans ce cas, comment puis-je obtenir la sortie de la commande? Est-ce que "stdout" contient la sortie de ligne de commande?
Anderson Green
De plus, est-il possible de faire quelque chose de similaire sans utiliser de rappel?
Anderson Green
Correct, stdout contient la sortie du programme. Et non, ce n'est pas possible de le faire sans callback. Tout dans node.js est orienté vers le non-blocage, ce qui signifie que chaque fois que vous effectuez des E / S, vous allez utiliser des rappels.
hexist
Notez que si vous cherchez à utiliser javascript pour faire des choses un peu scripty où vous voulez vraiment attendre la sortie et ce genre de chose, vous pouvez regarder le shell v8, d8
hexist
@hexist, il existe des Syncméthodes disponibles en mode natif, même si à mon humble avis , cela devrait être évité
Renato Gama
30

Si vous utilisez node plus tard que 7.6 et que vous n'aimez pas le style de rappel, vous pouvez également utiliser la promisifyfonction de node-util avec async / awaitpour obtenir des commandes shell qui lisent proprement. Voici un exemple de réponse acceptée, en utilisant cette technique:

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

Cela présente également l'avantage supplémentaire de renvoyer une promesse rejetée sur les commandes ayant échoué, qui peut être gérée à l' try / catchintérieur du code asynchrone.

Ansikt
la source
Avez-vous essayé cela? Je reçois { stdout: string, stderr: string }en conséquence pour leawait exec(...)
fwoelffel
1
Ouais, j'aurais dû préciser que cela vous donne la sortie complète du shell, y compris stdout et stderr. Si vous voulez juste la sortie, vous pouvez changer la dernière ligne à: return { name: name.stdout.trim(), email: email.stdout.trim() }.
Ansikt
16

Grâce à Renato answer, j'ai créé un exemple très basique:

const exec = require('child_process').exec

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

Il imprimera simplement votre nom d'utilisateur git global :)

Damjan Pavlica
la source
11

Exigences

Cela nécessitera Node.js 7 ou version ultérieure avec un support pour Promises et Async / Await.

Solution

Créez une fonction wrapper qui exploite les promesses pour contrôler le comportement de la child_process.execcommande.

Explication

En utilisant des promesses et une fonction asynchrone, vous pouvez imiter le comportement d'un shell renvoyant la sortie, sans tomber dans un enfer de rappel et avec une API assez soignée. En utilisant le awaitmot - clé, vous pouvez créer un script qui se lit facilement, tout en restant en mesure de faire le travail child_process.exec.

Exemple de code

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

Usage

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

Exemple de sortie

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

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

Essayez-le en ligne.

Repl.it .

Ressources externes

Des promesses .

child_process.exec.

Table de support Node.js .

Amin NAIRI
la source