Wykonaj i pobierz dane wyjściowe polecenia powłoki w node.js.

113

W node.js chciałbym znaleźć sposób na uzyskanie danych wyjściowych polecenia terminala systemu Unix. Czy jest na to sposób?

function getCommandOutput(commandString){
    // now how can I implement this function?
    // getCommandOutput("ls") should print the terminal output of the shell command "ls"
}
Anderson Green
źródło
Czy to jest duplikat, czy też opisuje coś zupełnie innego? stackoverflow.com/questions/7183307/…
Anderson Green
To może cię zainteresować.
benekastah
Użyj npmjs.com/package/cross-spawn
Andrew Koster

Odpowiedzi:

143

Tak właśnie robię w projekcie, nad którym teraz pracuję.

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

Przykład: pobieranie użytkownika 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
źródło
3
Czy jest możliwe, aby ta funkcja zwracała dane wyjściowe polecenia? (To właśnie próbowałem zrobić.)
Anderson Green,
1
to właśnie robi ten kod. spójrz na przykład z edycji, którą właśnie zrobiłem
Renato Gama
2
@AndersonGreen Nie chciałbyś, aby funkcja powracała normalnie z klawiaturą „return”, ponieważ wykonuje ona polecenie powłoki asynchronicznie. W rezultacie lepiej jest przekazać wywołanie zwrotne z kodem, który powinien zostać uruchomiony po zakończeniu polecenia powłoki.
Nick McCurdy
1
Ups, twoja pierwsza próbka ignoruje możliwość wystąpienia błędu, gdy wywołuje to wywołanie zwrotne. Zastanawiam się, co się stanie, stdoutjeśli wystąpi błąd. Miejmy nadzieję, że deterministyczne i udokumentowane.
doug65536
31

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

Jak zauważył Renato, istnieje teraz kilka synchronicznych pakietów exec, zobacz sync-exec, które mogą być bardziej tym, czego szukasz. Pamiętaj jednak, że node.js został zaprojektowany jako jednowątkowy serwer sieciowy o wysokiej wydajności, więc jeśli do tego chcesz go używać, trzymaj się z daleka od rzeczy typu sync-exec, chyba że używasz go tylko podczas uruchamiania lub coś.

heksysta
źródło
1
W takim przypadku jak mogę uzyskać dane wyjściowe polecenia? Czy „stdout” zawiera dane wyjściowe wiersza polecenia?
Anderson Green,
Czy można zrobić coś podobnego bez korzystania z funkcji zwrotnej?
Anderson Green
Prawidłowo, standardowe wyjście zawiera dane wyjściowe programu. I nie, nie da się tego zrobić bez callbacków. Wszystko w node.js jest zorientowane na nieblokowanie, co oznacza, że ​​za każdym razem, gdy wykonujesz IO, będziesz używać wywołań zwrotnych.
hexist
Zauważ, że jeśli szukasz używania javascript do robienia skryptowych rzeczy, w których naprawdę chcesz czekać na wyjście i tego typu rzeczy, możesz spojrzeć na powłokę v8, d8
hexist
@hexist, niektóre Syncmetody są dostępne natywnie, mimo to IMHO należy ich unikać
Renato Gama
29

Jeśli używasz node później niż 7.6 i nie podoba ci się styl wywołania zwrotnego, możesz również użyć funkcji node-util promisifyz, async / awaitaby uzyskać polecenia powłoki, które odczytują czysto. Oto przykład zaakceptowanej odpowiedzi przy użyciu tej techniki:

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

Ma to również dodatkową zaletę w postaci zwracania odrzuconej obietnicy w przypadku nieudanych poleceń, które można obsłużyć try / catchwewnątrz kodu asynchronicznego.

Ansikt
źródło
Czy próbowałeś tego? Otrzymuję { stdout: string, stderr: string }wynik naawait exec(...)
fwoelffel
1
Tak, powinienem był wyjaśnić, że daje to pełne wyjście powłoki, w tym zarówno stdout, jak i stderr. Jeśli chcesz tylko wyjście, można zmienić ostatni wiersz: return { name: name.stdout.trim(), email: email.stdout.trim() }.
Ansikt,
16

Dzięki odpowiedzi Renato stworzyłem naprawdę podstawowy przykład:

const exec = require('child_process').exec

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

Po prostu wydrukuje twoją globalną nazwę użytkownika git :)

Damjan Pavlica
źródło
11

Wymagania

Będzie to wymagało Node.js 7 lub nowszego z obsługą Promises i Async / Await.

Rozwiązanie

Utwórz funkcję opakowującą, która wykorzystuje obietnice do kontrolowania zachowania child_process.execpolecenia.

Wyjaśnienie

Korzystając z obietnic i funkcji asynchronicznej, możesz naśladować zachowanie powłoki zwracającej dane wyjściowe, bez popadania w piekło wywołań zwrotnych i za pomocą całkiem zgrabnego interfejsu API. Używając awaitsłowa kluczowego, możesz stworzyć skrypt, który łatwo się czyta, a jednocześnie będzie w stanie wykonać pracę child_process.exec.

Przykład kodu

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

Stosowanie

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

Przykładowe wyjście

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

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

Wypróbuj online.

Zamień .

Zasoby zewnętrzne

Obietnice .

child_process.exec.

Node.js stół wsparcie .

Amin NAIRI
źródło