node.js, sistem komutunu eşzamanlı olarak yürütür


172

Node.js işlevine ihtiyacım var

result = execSync('node -v');

o olacak eşzamanlı verilen komut satırını yürütmek ve bu komut metni bütün stdout'ed döner.

ps. Senkronizasyon yanlış. Biliyorum. Sadece kişisel kullanım içindir.

GÜNCELLEME

Şimdi mgutz'un çıkış kodunu veren bir çözümümüz var ama stdout değil! Hala daha kesin bir cevap bekliyorum.

GÜNCELLEME

mgutz cevabını güncelledi ve çözüm burada :)
Ayrıca, dgo.a'nın da belirttiği gibi, bağımsız modül exec-sync var

GÜNCELLEME 2014-07-30

ShellJS lib geldi. Bunun şimdilik en iyi seçim olduğunu düşünün.


GÜNCELLEME 2015-02-10

SONUNDA! NodeJS 0.12 execSyncyerel olarak desteklenir .
Resmi dokümanlara bakın


26
Kendinizi kandırmaya izin vermeyin, senkronizasyon yanlış değil ... NodeJS'de BİRLİKTE açıkça bir asenkron yöntem çağrılmadıkça tüm kodlarınız senkronize olarak yürütülür ... eğer her şey asenkron yolla yapıldıysa hiçbir şey yapılmazdı. Ayrıca, eşzamansız yöntemleri tercih etmek, uzun hesaplamanızın sunucunuzu engellemeyeceği anlamına gelmez. bu bir seçim. Düğüm yapıcılarının, zaman uyumsuz olanların yanında senkronize dosya sistemi yöntemleri sağlamayı seçtikleri, bunlar için de bir yer olduğunu gösteriyor.
akış

2
Bahsettiğiniz "Unix kabuk öykünme kitaplığını" nerede bulabiliriz?
Florian

@Florian, ShellJS
xst

Yanıtlar:


154

Node.js (0.12 sürümünden beri - bir süreliğine) execSyncşunları destekler :

child_process.execSync(command[, options])

Şimdi doğrudan bunu yapabilirsiniz:

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

ve beklediğinizi yapacak. (I / o sonuçlarını üst sürece aktarma varsayılanlarıdır). spawnSyncŞimdi de yapabileceğinizi unutmayın .


6
10 saat umutsuzluktan sonra. TEŞEKKÜRLER DUDE
Tom Dev

Bu alt işlemle bağlantıyı nasıl kesebilirim?
JulianSoto

@JulianSoto nodejs.org/api/…
ilahi

54

ExecSync kütüphanesine bakın .

Düğüm-ffi ile yapmak oldukça kolaydır . Sunucu işlemleri için tavsiye etmem, ancak genel geliştirme programları için işleri halleder. Kütüphaneyi kurun.

npm install node-ffi

Örnek komut dosyası:

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

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

[Haziran 2012 EDIT: STDOUT nasıl alınır]

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
Bundan gerçekten bir şey yollamaya ne dersiniz stdout? Tüm alabileceğim işlem çıkış kodu
Mark Kahn

@cwolves: Async'in daha iyi olacağını düşünüyorum. (
Ivo'nun

@pvorb - evet, zaman uyumsuz kullanamayacağın sürece :)
Mark Kahn

1
Her çivi için asenk çekiç kullanmamanın geçerli nedenleri vardır. Örneğin, şablon motorları Express 3'te zaman uyumsuzdur ve yardımcı işlevlerin (yerel ayarlar) senkronize olması gerekir. Bu yardımcı işlevlerin daha az dosyayı anında eşzamansız olarak derlemesi gerekiyorsa ne olur?
mgutz

8
Acaba bu neden basit execSyncdeğil child_process. Bence öyle olmalı.
Michael Härtl

31

ShellJS modülünü kullanın .

geri arama sağlamadan exec fonksiyonu.

Misal:

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

2
Yazma sırasında, belgelerin senkronizasyonun exec()uzun işlemler için CPU yoğun olduğundan bahsettiğine dikkat edin .
Aram Kocharyan

1
seçenekleri, {sessiz: doğru} anahtardır
Nick

1
Bu (daha kısa sözdizimi sağlamak dışında) bir şey yapmaz mı? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

Denilen node.js akış kontrolü için mükemmel bir modül var asyncblock . Kodu bir işleve sarmak sizin durumunuz için uygunsa, aşağıdaki örnek düşünülebilir:

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
Akış kitaplıklarını değil, senkronizasyon sürümünü açıkça sordu.
Alexey Petrushin

22
@AlexeyPetrushin Buradaki her soru, bir hedefe yöneliktir, belirli bir hedefe ulaşmak için değil. Gerçi oy verdiğiniz için teşekkürler.
nab

1
Ayrıca, bu Windows kullanıcıları için çok yararlı bir cevaptır; yükleme exec-syncveya ffiWindows üzerinde büyük bir yük var (VC ++, SDK'lar, Python, vb), ancak bu daha hafiftir.
Mendhak

10

Bu node.js mümkün değildir, hem child_process.spawnve child_process.execzaman uyumsuz olması sıfırdan inşa edildi.

Ayrıntılar için bkz. Https://github.com/ry/node/blob/master/lib/child_process.js

Bu engellemeyi gerçekten yapmak istiyorsanız, daha sonra olması gereken her şeyi geri aramaya koyun veya bunu engelleme tarzında ele almak için kendi kuyruğunuzu oluşturun, sanırım bu görev için Async.js'yi kullanabilirsiniz .

Ya da, harcamak için çok fazla zamanınız varsa, Node.js'de kendiniz hackleyin.


12
tuhaf çünkü dosya sistemi modülünde senkron çağrılar var. Neden idam etmiyorsun?
Alfred

4
@Alfred Senkronizasyon FS çağrıları esas olarak program başlangıcında yapılandırmaların yüklenmesi içindir.
Ivo Wetzel

3
@IvoWetzel - tisk tisk ... bir şeyin imkansız olduğunu asla söylemeyi öğrenmedik mi? ;) aşağıdaki çözümüme bakın.
Marcus Pope

1
@IvoWetzel "FS çağrılarını senkronize et ..." - doğru ve bazen program başlangıcında bir şeyler derlemek ve tamamlanmaya devam etmek için bir komut vermek istediğinizi söylemek istersiniz. bir gözetim gibi görünüyor. Ben asenkronum, ama senkronize artıları ve kullanım durumları var. elbette bunu makul bir şekilde kullanmalıyım.
akış

Async iyi, ancak WidgetB WidgetA'nın nihai sonuçlarına bağlıysa, dünyadaki tüm async işi yapmaz. Bazen süreçlerin senkronize olması gerekir. Zaman uyumsuz olarak pişirmeyi deneyin. ;)
Lloyd Sargent

9

Bulduğum en kolay yol:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(execSync ile karıştırılmamalıdır.)
Kabuk komutunu eşzamanlı olarak yürütür . Geçiş komut dosyaları, cli programları için kullanın, ancak normal sunucu kodu için kullanmayın.

Misal:

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



3

"synchronous"Geri arama işlevinin sonunda bir şeyler uygulamaya alıştım. Çok hoş değil, ama işe yarıyor. Bir dizi komut satırı yürütmesi gerçekleştirmeniz gerekiyorsa, execadlandırılmış bazı işlevlere sarmanız ve yinelemeli olarak çağırmanız gerekir. Bu model benim için kullanılabilir gibi görünüyor:

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

Benzer bir sorun yaşadım ve bunun için bir düğüm uzantısı yazdım. Git deposunu kontrol edebilirsiniz. Açık kaynak kodlu ve ücretsiz ve tüm bu iyi şeyler!

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

ExecXI, kabuk komutlarını tek tek yürütmek ve komutun çıktısını konsola gerçek zamanlı olarak göndermek için C ++ ile yazılmış bir düğüm uzantısıdır. İsteğe bağlı zincirleme ve zincirsiz yollar mevcuttur; yani komut başarısız olduğunda (zincirleme) komut dosyasını durdurmayı seçebilir veya hiçbir şey olmamış gibi devam edebilirsiniz!

Kullanım talimatları Benioku dosyasındadır . Çekme taleplerinde bulunmaktan veya sorun göndermekten çekinmeyin!

EDIT: Ancak henüz stdout dönmüyor ... Sadece gerçek zamanlı çıktılar. Şimdi öyle. Bugün yeni yayınladım. Belki bunun üzerine inşa edebiliriz.

Her neyse, bahsetmeye değer olduğunu düşündüm.


Tam da aradığım şey bu. Bunu yapmak için zaman ayırdığınız için teşekkür ederiz.
thealfreds

Anlamadığım ve uygulayamadığım tek şey, farklı nodejs sürümleri için yapı yapılandırmasıdır. Sanırım bunu 0.8 düğümüne ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) yazdım, ancak npm installbaşarılı olduğu sürece (başka bir deyişle eklenti derlerken), o zaman üretime gitmek iyidir. Farklı nodejs sürümlerini hedeflemenin yanı sıra, üretim için yamalı veya oldukça kararlı olduğunu söyleyebilirim. Herhangi bir hata varsa, bir çekme isteği gönderebilir veya github'da bir sorun gönderebilirsiniz :)
Logan

1

nodejs'de senkron kabuk işlemlerini şu şekilde yapabilirsiniz:

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\\"));

EDIT - bu örnek Windows ortamları içindir, gerekirse kendi linux ihtiyaçlarınız için ayarlayın


Evet, ve CPU'nuzun mümkün olduğu kadar çabuk çağırabileceği kadar ... Ama şeytani bir şey yapmak için “ihtiyacınız” olduğunda Şeytan sizin adamınız değil mi?
Marcus Pope

Tamam, bu saf kötülük, ama harika. Ben bir geri arama yoktu bir dosya sistemi olay browserify.on ('kayıt') işlemek için gerekli. Günümü kurtardım!
Robert Gould

bunun neden javascript motorlarında çalıştığını açıklayabilir misiniz? while döngüsü bir sonraki komutta yürütülürken aynı kene üzerinde sonsuz olarak yürütülmemelidir?
badunk

Meşgul bekleyerek CPU'yu en üst düzeye çıkarmak kötü tasarımdır.
Louis

@ Louis-DominiqueDubeau Elbette, ancak platformlar arası uyumlu olabilecek veya olmayabilecek bazı üçüncü taraf kaynaklara bağlı olmayan herhangi bir alternatif yoktur. Ayrıca, CPU'nun gerçek bir maksimum değeri değildir, çünkü işletim sistemi nodejs işlemine tam öncelik vermez. Shell ops'un senkronizasyon uygulamaları ufukta ya da belki de burada zaten.
Marcus Pope

1

Aslında bir paketten birbiri ardına birden fazla komut çalıştırmam gereken bir durum vardı.

Ben de bu yüzden geldim:

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

Bu şekilde kullanabilirsiniz:

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

EDIT: belirtildiği gibi, bu aslında çıktıyı döndürmez veya bir Düğüm programındaki komutların sonucunu kullanmanıza izin vermez. Bunun bir diğer fikri LiveScript geri çağrılarını kullanmaktır. http://livescript.net/


Teşekkürler, ancak kodunuz zaman uyumsuz olarak
02:32

Örneğime göre bir dizi komutu eşzamanlı olarak yürütmeniz gerekiyorsa çalışır. Sanırım sorunuzu yanlış anladım, üzgünüm. Cevaba başka bir fikir daha ekledim.
Jason Livesay
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.