node.js synchronicznie wykonuje polecenia systemowe


171

Potrzebuję w funkcji node.js.

result = execSync('node -v');

która synchronicznie wykona podaną linię poleceń i zwróci wszystkie stdout'ed przez ten tekst polecenia.

ps. Synchronizacja jest nieprawidłowa. Wiem. Tylko do użytku osobistego.

AKTUALIZACJA

Teraz mamy rozwiązanie mgutz, które daje nam kod zakończenia, ale nie stdout! Wciąż czekam na dokładniejszą odpowiedź.

AKTUALIZACJA

mgutz zaktualizował swoją odpowiedź i rozwiązanie jest tutaj :)
Ponadto, jak wspomniał dgo.a , istnieje samodzielny moduł exec-sync

UPDATE 2014-07-30

Przybyła biblioteka ShellJS . Rozważ, że to najlepszy wybór na razie.


AKTUALIZACJA 10.02.2015

W KOŃCU! NodeJS 0.12 obsługuje execSyncnatywnie.
Zobacz oficjalne dokumenty


26
nie daj się zwieść, synchronizacja nie jest zła ... NAWET w NodeJS cały kod jest wykonywany synchronicznie, chyba że jawnie wywołasz metodę asynchroniczną ... gdyby wszystko zostało zrobione w sposób asynchroniczny, nic by się nie stało. Ponadto preferowanie metod asynchronicznych nie oznacza, że ​​długie obliczenia nie zablokują serwera. to wybór. że twórcy Node zdecydowali się udostępnić metody synchronicznego systemu plików obok metod asynchronicznych, po prostu pokazuje, że jest miejsce również dla nich.
przepływ

2
Gdzie możemy znaleźć „bibliotekę emulacji powłoki Unix”, o której mówisz?
Florian,

@Florian on oznacza ShellJS
xst

Odpowiedzi:


153

Node.js (od wersji 0.12 - czyli przez chwilę) obsługuje execSync:

child_process.execSync(command[, options])

Możesz teraz zrobić to bezpośrednio:

const execSync = require('child_process').execSync;
code = execSync('node -v');

i zrobi to, czego oczekujesz. (Domyślnie potokuje wyniki wejścia / wyjścia do procesu nadrzędnego). Zauważ, że możesz spawnSyncteraz.


6
po 10 godzinach rozpaczy. DZIĘKUJĘ KOLEŚ
Tom Dev

Jak mogę odłączyć się od tego podprocesu?
JulianSoto

@JulianSoto nodejs.org/api/…
boski

54

Zobacz bibliotekę execSync .

Dość łatwo to zrobić z node-ffi . Nie polecałbym procesów serwerowych, ale w przypadku ogólnych narzędzi programistycznych to załatwia sprawę. Zainstaluj bibliotekę.

npm install node-ffi

Przykładowy skrypt:

var FFI = require("node-ffi");
var libc = new FFI.Library(null, {
  "system": ["int32", ["string"]]
});

var run = libc.system;
run("echo $USER");

[EDIT Jun 2012: How to get STDOUT]

var lib = ffi.Library(null, {
    // FILE* popen(char* cmd, char* mode);
    popen: ['pointer', ['string', 'string']],

    // void pclose(FILE* fp);
    pclose: ['void', [ 'pointer']],

    // char* fgets(char* buff, int buff, in)
    fgets: ['string', ['string', 'int','pointer']]
});

function execSync(cmd) {
  var
    buffer = new Buffer(1024),
    result = "",
    fp = lib.popen(cmd, 'r');

  if (!fp) throw new Error('execSync error: '+cmd);

  while(lib.fgets(buffer, 1024, fp)) {
    result += buffer.readCString();
  };
  lib.pclose(fp);

  return result;
}

console.log(execSync('echo $HOME'));

2
Jak byś się właściwie zabrał do wysyłania czegokolwiek stdoutz tego? Wszystko, co mogę uzyskać, to kod zakończenia procesu
Mark Kahn

@cwolves: Myślę, że asynchroniczne byłoby lepsze to. ( Odpowiedź Ivo )
pvorb

@pvorb - tak, z wyjątkiem sytuacji, gdy nie możesz użyć asynchronicznego :)
Mark Kahn

1
Istnieją ważne powody, dla których nie należy używać młotka asynchronicznego do każdego gwoździa. Na przykład silniki szablonów są asynchroniczne w Express 3, a funkcje pomocnicze (lokalne) muszą być synchroniczne. Co jeśli te funkcje pomocnicze będą musiały kompilować pliki Less asynchronicznie w locie?
mgutz

8
Zastanawiam się, dlaczego to proste execSyncnie jest częścią child_process. Myślę, że tak powinno być.
Michael Härtl,

31

Użyj modułu ShellJS .

exec bez wywołania zwrotnego.

Przykład:

var version = exec('node -v').output;

2
Zwróć uwagę, że w momencie pisania tego tekstu dokumentacja wspomina, że ​​synchronizacja exec()obciąża procesor w przypadku długich procesów.
Aram Kocharyan

1
opcje, kluczem jest {cichy: prawda}
Nick

1
To coś robi (poza zapewnieniem krótszej składni), co nie? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

W node.js jest doskonały moduł do kontroli przepływu o nazwie asyncblock . Jeśli zawijanie kodu w funkcję jest w porządku w Twoim przypadku, można wziąć pod uwagę następujący przykład:

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

asyncblock(function (flow) {
    exec('node -v', flow.add());
    result = flow.wait();
    console.log(result);    // There'll be trailing \n in the output

    // Some other jobs
    console.log('More results like if it were sync...');
});

1
Zapytał wprost o wersję do synchronizacji, a nie o biblioteki przepływu kontroli.
Alexey Petrushin

22
@AlexeyPetrushin Każde pytanie dotyczy celu, a nie konkretnego sposobu jego osiągnięcia. Jednak dzięki za odrzucenie głosów.
złap

1
Jest to również bardzo przydatna odpowiedź dla użytkowników systemu Windows; instalacja exec-synclub ffiw systemie Windows wiąże się z ogromnym narzutem (VC ++, SDK, Python itp.), Ale to jest lżejsze.
Mendhak

10

Nie jest to możliwe w Node.js, oba child_process.spawni child_process.execzostały zbudowane od podstaw, aby były asynchroniczne.

Aby uzyskać szczegółowe informacje, patrz: https://github.com/ry/node/blob/master/lib/child_process.js

Jeśli naprawdę chcesz mieć to blokowanie, umieść wszystko, co musi się wydarzyć później, w wywołaniu zwrotnym lub zbuduj własną kolejkę, aby obsłużyć to w sposób blokujący, przypuszczam, że możesz użyć Async.js do tego zadania.

Lub, jeśli masz zbyt dużo czasu do spędzenia, włamać się do Node.js samodzielnie.


12
dziwne, ponieważ moduł systemu plików ma wywołania synchroniczne. Dlaczego nie wykonać również?
Alfred,

4
@Alfred Wywołania sync FS służą głównie do ładowania konfiguracji podczas uruchamiania programu.
Ivo Wetzel

3
@IvoWetzel - tisk tisk ... czy nie nauczyliśmy się nigdy nie mówić, że coś jest niemożliwe? ;) zobacz moje rozwiązanie poniżej.
Marcus Pope

1
@IvoWetzel "Synchronizacja FS wywołuje ..." - tak, a czasami chcesz, powiedzmy, wydać polecenie kompilacji czegoś przy uruchomieniu programu i kontynuować po zakończeniu. - biorąc pod uwagę, że są wywołania synchronizacji FS, a nie ma wykonania synchronizacji wygląda na przeoczenie. Jestem zwolennikiem asynchroniczności, ale synchronizacja ma swoje zalety i przypadki użycia. oczywiście muszę go używać rozsądnie.
przepływ

Async jest w porządku, ale jeśli WidgetB zależy od ostatecznych wyników WidgetA, cała asynchronizacja na świecie nie wykona zadania. Czasami procesy muszą być synchroniczne. Spróbuj gotować asynchronicznie. ;)
Lloyd Sargent

9

To najłatwiejszy sposób, jaki znalazłem:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(nie mylić z execSync).
Wykonaj polecenie powłoki synchronicznie. Użyj tego dla skryptów migracji, programów CLI, ale nie dla zwykłego kodu serwera.

Przykład:

var execSync = require('exec-sync');   
var user = execSync('echo $USER');
console.log(user);


5

Możesz to osiągnąć za pomocą włókien. Na przykład, używając mojej biblioteki Common Node , kod wyglądałby następująco:

result = require('subprocess').command('node -v');

3

Przyzwyczajam się do implementacji "synchronous"rzeczy na końcu funkcji zwrotnej. Niezbyt ładne, ale działa. Jeśli chcesz zaimplementować sekwencję wykonań wiersza poleceń, musisz zawinąć execw jakąś nazwaną funkcję i rekurencyjnie ją wywołać. Ten wzór wydaje mi się przydatny:

SeqOfExec(someParam);

function SeqOfExec(somepParam) {
    // some stuff
    // .....
    // .....

    var execStr = "yourExecString";
    child_proc.exec(execStr, function (error, stdout, stderr) {
        if (error != null) {
            if (stdout) {
                throw Error("Smth goes wrong" + error);
            } else {
                // consider that empty stdout causes
                // creation of error object
            }
        }
        // some stuff
        // .....
        // .....

        // you also need some flag which will signal that you 
        // need to end loop
        if (someFlag ) {
            // your synch stuff after all execs
            // here
            // .....
        } else {
            SeqOfExec(someAnotherParam);
        }
    });
};

3

Miałem podobny problem i napisałem do tego rozszerzenie węzła. Możesz sprawdzić repozytorium git. To open source, darmowe i wszystkie dobre rzeczy!

https://github.com/aponxi/npm-execxi

ExecXI to rozszerzenie węzła napisane w C ++ w celu wykonywania poleceń powłoki jeden po drugim, wysyłając dane wyjściowe polecenia do konsoli w czasie rzeczywistym. Obecne są opcjonalne łańcuchowe i niezwiązane sposoby; co oznacza, że ​​możesz zatrzymać skrypt po niepowodzeniu polecenia (połączonym w łańcuch) lub możesz kontynuować, jakby nic się nie stało!

Instrukcje użytkowania znajdują się w pliku ReadMe . Zapraszam do składania żądań ściągnięcia lub zgłaszania problemów!

EDYCJA: Jednak nie zwraca jeszcze standardowego wyjścia ... Po prostu wyświetla je w czasie rzeczywistym. Teraz tak. Cóż, właśnie go dzisiaj wydałem. Może możemy na tym zbudować.

Zresztą pomyślałem, że warto o tym wspomnieć.


To jest dokładnie to, czego szukałem. Dziękuję za poświęcenie czasu na zrobienie tego.
thealfreds

Jedyne, czego nie wymyśliłem i nie zaimplementowałem, to konfiguracja kompilacji dla różnych wersji nodejs. Wydaje mi się, że napisałem to w węźle 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ), więc dopóki się npm installpowiedzie (innymi słowy jako kompilacja wtyczki), dobrze jest iść do produkcji. Oprócz kierowania na różne wersje nodejs, powiedziałbym, że jest poprawiony do produkcji lub że jest całkiem stabilny. Jeśli są jakieś błędy, możesz wysłać prośbę o ściągnięcie lub opublikować problem na github :)
Logan

1

możesz wykonywać synchroniczne operacje powłoki w nodejs w ten sposób:

var execSync = function(cmd) {

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

    //for linux use ; instead of &&
    //execute your command followed by a simple echo 
    //to file to indicate process is finished
    exec(cmd + " > c:\\stdout.txt && echo done > c:\\sync.txt");

    while (true) {
        //consider a timeout option to prevent infinite loop
        //NOTE: this will max out your cpu too!
        try {
            var status = fs.readFileSync('c:\\sync.txt', 'utf8');

            if (status.trim() == "done") {
                var res = fs.readFileSync("c:\\stdout.txt", 'utf8');
                fs.unlinkSync("c:\\stdout.txt"); //cleanup temp files
                fs.unlinkSync("c:\\sync.txt");
                return res;
            }
        } catch(e) { } //readFileSync will fail until file exists
    }

};

//won't return anything, but will take 10 seconds to run
console.log(execSync("sleep 10")); 

//assuming there are a lot of files and subdirectories, 
//this too may take a while, use your own applicable file path
console.log(execSync("dir /s c:\\usr\\docs\\"));

EDYCJA - ten przykład jest przeznaczony dla środowisk Windows, w razie potrzeby dostosuj do własnych potrzeb Linuksa


Tak, i tak szybko, jak twój procesor może przywołać ... Ale hej, kiedy "potrzebujesz" zrobić coś złego, szatan jest twoim człowiekiem, prawda?
Marcus Pope

ok, to jest czyste zło, ale niesamowite. Potrzebowałem tego do obsługi zdarzenia browserify.on systemu plików ('register'), które nie miało wywołania zwrotnego. Uratowałem mój dzień!
Robert Gould,

czy możesz wyjaśnić, dlaczego to działa w silnikach javascript? czy pętla while nie powinna być wykonywana w nieskończoność na tym samym tiku, podczas gdy jest wykonywana na następnym?
badunk

Maksymalne wykorzystanie mocy procesora przez zajęte oczekiwanie to zły projekt.
Louis

@ Louis-DominiqueDubeau Jasne, ale tak naprawdę nie ma żadnej alternatywy, która nie zależy od jakiegoś źródła zewnętrznego, które może, ale nie musi, być kompatybilne z wieloma platformami. Nie jest to również prawdziwe maksymalne wykorzystanie procesora, ponieważ system operacyjny nie da pełnego priorytetu procesowi nodejs. Myślę, że implementacje synchronizacji operacji powłoki są na horyzoncie, a może już tutaj.
Marcus Pope

1

W rzeczywistości miałem sytuację, w której musiałem uruchamiać wiele poleceń jedno po drugim ze skryptu preinstalacji package.json w sposób, który działałby zarówno w systemie Windows, jak i Linux / OSX, więc nie mogłem polegać na module spoza jądra.

Oto co wymyśliłem:

#cmds.coffee
childproc = require 'child_process'

exports.exec = (cmds) ->
  next = ->
    if cmds.length > 0
      cmd = cmds.shift()
      console.log "Running command: #{cmd}"
      childproc.exec cmd, (err, stdout, stderr) ->
        if err? then console.log err
        if stdout? then console.log stdout
        if stderr? then console.log stderr
        next()
    else
      console.log "Done executing commands."

  console.log "Running the follows commands:"
  console.log cmds
  next()

Możesz go używać w ten sposób:

require('./cmds').exec ['grunt coffee', 'nodeunit test/tls-config.js']

EDYCJA: jak wskazano, w rzeczywistości nie zwraca to danych wyjściowych ani nie pozwala na użycie wyniku poleceń w programie Node. Innym pomysłem na to jest użycie wywołań zwrotnych LiveScript. http://livescript.net/


Dzięki, ale to nie jest odpowiedź, ponieważ Twój kod uruchamia polecenia szeregowo asynchronicznie
disfated

Jeśli chcesz wykonać serię poleceń synchronicznie, jak na moim przykładzie, zadziała. Myślę, że źle zrozumiałem twoje pytanie, przepraszam. Do odpowiedzi dodałem kolejny pomysł.
Jason Livesay
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.