node.js kabuk komutu yürütme


113

Hala bir linux veya windows shell komutunu nasıl çalıştırabileceğime ve node.js içinde çıktıyı nasıl yakalayabileceğime dair daha ince noktaları kavramaya çalışıyorum; sonuçta böyle bir şey yapmak istiyorum ...

//pseudocode
output = run_command(cmd, args)

Önemli outputolan, küresel kapsamlı bir değişken (veya nesne) için mevcut olması gerektiğidir. Aşağıdaki işlevi denedim, ancak nedense undefinedkonsola yazdırıldım ...

function run_cmd(cmd, args, cb) {
  var spawn = require('child_process').spawn
  var child = spawn(cmd, args);
  var me = this;
  child.stdout.on('data', function(me, data) {
    cb(me, data);
  });
}
foo = new run_cmd('dir', ['/B'], function (me, data){me.stdout=data;});
console.log(foo.stdout);  // yields "undefined" <------

Yukarıdaki kodun nerede kırıldığını anlamakta güçlük çekiyorum ... bu modelin çok basit bir prototipi çalışıyor ...

function try_this(cmd, cb) {
  var me = this;
  cb(me, cmd)
}
bar = new try_this('guacamole', function (me, cmd){me.output=cmd;})
console.log(bar.output); // yields "guacamole" <----

Birisi neden try_this()işe yarayıp yaramadığını anlamama yardım edebilir run_cmd()mi? FWIW, kullanmam gerekiyor child_process.spawnçünkü child_process.exec200KB tampon limiti var.

Nihai Çözüm

James White'ın cevabını kabul ediyorum, ancak bu benim için işe yarayan kodun aynısı ...

function cmd_exec(cmd, args, cb_stdout, cb_end) {
  var spawn = require('child_process').spawn,
    child = spawn(cmd, args),
    me = this;
  me.exit = 0;  // Send a cb to set 1 when cmd exits
  me.stdout = "";
  child.stdout.on('data', function (data) { cb_stdout(me, data) });
  child.stdout.on('end', function () { cb_end(me) });
}
foo = new cmd_exec('netstat', ['-rn'], 
  function (me, data) {me.stdout += data.toString();},
  function (me) {me.exit = 1;}
);
function log_console() {
  console.log(foo.stdout);
}
setTimeout(
  // wait 0.25 seconds and print the output
  log_console,
250);

2
Nihai Kararı sen belirlesin me.stdout = "";içinde cmd_exec()birleştirerek önlemek için undefinedsonuç başlangıcına.
aorcsik

Hey, bu son çözüm kodu tamamen berbat, ya netstat'ınızı yürütmek 0.25 saniyeden uzun sürerse?
Steven Lu

Ummmm ... belki bonus verdiğim cevaplardan birini kullanabilirsin ?????
Mike Pennington

Olası Execute
Damjan

Yanıtlar:


89

Burada düzeltilmesi gereken üç sorun var:

Birincisi , standart çıktıyı eşzamansız olarak kullanırken eşzamanlı davranış beklemenizdir. run_cmdİşlevinizdeki tüm çağrılar eşzamansızdır, bu nedenle alt süreci oluşturur ve verilerin bir kısmının, tamamının veya hiçbirinin stdout'tan okunup okunmadığına bakılmaksızın hemen geri döner. Gibi, koştuğun zaman

console.log(foo.stdout);

Şu anda foo.stdout'ta depolanacak her şeyi alırsınız ve bunun ne olacağına dair hiçbir garanti yoktur çünkü çocuğunuz hala çalışıyor olabilir.

İkincisi , stdout'un okunabilir bir akış olmasıdır , bu nedenle 1) veri olayı birden çok kez çağrılabilir ve 2) geri aramaya bir dizge değil, bir tampon verilir. Çaresi kolay; sadece değiştir

foo = new run_cmd(
    'netstat.exe', ['-an'], function (me, data){me.stdout=data;}
);

içine

foo = new run_cmd(
    'netstat.exe', ['-an'], function (me, buffer){me.stdout+=buffer.toString();}
);

böylece tamponumuzu bir dizgeye çeviririz ve bu dizeyi stdout değişkenimize ekleriz.

Üçüncüsü , yalnızca 'end' olayını aldığınızda tüm çıktıları aldığınızı bilebilirsiniz, bu da başka bir dinleyiciye ve geri aramaya ihtiyacımız olduğu anlamına gelir:

function run_cmd(cmd, args, cb, end) {
    // ...
    child.stdout.on('end', end);
}

Yani, nihai sonucunuz şudur:

function run_cmd(cmd, args, cb, end) {
    var spawn = require('child_process').spawn,
        child = spawn(cmd, args),
        me = this;
    child.stdout.on('data', function (buffer) { cb(me, buffer) });
    child.stdout.on('end', end);
}

// Run C:\Windows\System32\netstat.exe -an
var foo = new run_cmd(
    'netstat.exe', ['-an'],
    function (me, buffer) { me.stdout += buffer.toString() },
    function () { console.log(foo.stdout) }
);

yakın "Çocuğunuz süreci hala çalışıyor olabilir çünkü ne olacağını hiçbir garantisi yoktur" ... ama olacağının bir garantisi yoktur değil zaman içinde bu noktada ayarlanabilir ve geri arama nihayet çağrıldığında sadece set olacak başka bir yerde belirttiğiniz gibi

4
Bu, bazı çok önemli JS kavramlarının harika açıklamalarıyla mükemmel bir cevaptır. Güzel!
L0j1k

1
Fonksiyonun this.stdout = "";içinde yapmak isteyeceksiniz run(), aksi takdirde console.log(foo.sdtout);öneki olacaktır undefined.
f1lt3r

78

Kabul edilen cevabın basitleştirilmiş bir versiyonu (üçüncü nokta), az önce benim için çalıştı.

function run_cmd(cmd, args, callBack ) {
    var spawn = require('child_process').spawn;
    var child = spawn(cmd, args);
    var resp = "";

    child.stdout.on('data', function (buffer) { resp += buffer.toString() });
    child.stdout.on('end', function() { callBack (resp) });
} // ()

Kullanımı:

run_cmd( "ls", ["-l"], function(text) { console.log (text) });

run_cmd( "hostname", [], function(text) { console.log (text) });

1
İşlem sıfırdan farklı bir döndürdüğünde dönüş değeri ne olacak, nasıl elde edebilirim?
Vitim.us

2
child.stdout.on('close', (errCode) => { console.log(errCode) } )
Tushar Gautam

52

Bunu daha kısaca kullandım:

var sys = require('sys')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
exec("ls -la", puts);

mükemmel çalışıyor. :)


1
Bu iyi çalışır ve herhangi bir ek düğüm modülü gerektirmez. Bunu sevdim!
bearvarine

9
sys.puts()2011'de kullanımdan kaldırıldı (Node.js v0.2.3 ile). Bunun console.log()yerine kullanmalısınız .
tfmontague

1
bu gerçekten işe yarıyor ... bu yüzden merak ediyorum, neden cevap bu değil? çok basit
ekkis

Vay!! bu cevap mükemmel ve zariftir. teşekkür ederim.
Em Ji Madhu

43

En basit yol sadece ShellJS kütüphanesini kullanmaktır ...

$ npm install [-g] shelljs

EXEC Örneği:

require('shelljs/global');

// Sync call to exec()
var version = exec('node --version', {silent:true}).output;

// Async call to exec()
exec('netstat.exe -an', function(status, output) {
  console.log('Exit status:', status);
  console.log('Program output:', output);
});

ShellJs.org , aşağıdakiler dahil olmak üzere NodeJS işlevleri olarak eşlenen birçok yaygın kabuk komutunu destekler:

  • kedi
  • CD
  • chmod
  • cp
  • dirs
  • Eko
  • exec
  • çıkış
  • bulmak
  • grep
  • ln
  • ls
  • mkdir
  • mv
  • popd
  • pushd
  • pwd
  • rm
  • sed
  • Ölçek
  • hangi

shell.exec ("foo.sh") tarafından adlandırılan kabuk betiğine bir parametre nasıl eklenir?
pseudozach

1
Sadece kendi argümanları dize top ekleyebilirsiniz: shell.exec("foo.sh arg1 arg2 ... "). Sizin foo.shkomut bu kullanarak başvurabilir $1, $2... vb
Tony O'Hagan

Yürütmeye çalıştığınız komutun kullanıcıdan girdiye ihtiyacı olacaksa, ShellJS exec () KULLANMAYIN. Bu işlev doğası gereği etkileşimli değildir, sadece komut alıp çıktı alacağı için, aradaki girdileri kabul edemez. Bunun yerine yerleşik child_process kullanın. Örneğin, https://stackoverflow.com/a/31104898/9749509
MPatel1

4

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

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

ExecXI, kabuk komutlarını tek tek yürütmek için C ++ ile yazılmış ve komutun çıktısını gerçek zamanlı olarak konsola çıkaran bir düğüm uzantısıdır. İsteğe bağlı zincirleme ve zincirsiz yollar mevcuttur; Yani bir komut başarısız olduktan sonra (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!

Bundan bahsetmeye değer olduğunu düşündüm.


4

@ TonyO'Hagan kapsamlı bir shelljscevap, ancak cevabının eş zamanlı versiyonunu vurgulamak istiyorum:

var shell = require('shelljs');
var output = shell.exec('netstat -rn', {silent:true}).output;
console.log(output);

1

Senkron tek astar:

require('child_process').execSync("echo 'hi'", function puts(error, stdout, stderr) { console.log(stdout) });


0

run_cmdİşlevinizde değişken bir çatışma var :

  var me = this;
  child.stdout.on('data', function(me, data) {
    // me is overriden by function argument
    cb(me, data);
  });

Bunu basitçe şuna değiştirin:

  var me = this;
  child.stdout.on('data', function(data) {
    // One argument only!
    cb(me, data);
  });

Hataları görmek için her zaman şunu ekleyin:

  child.stderr.on('data', function(data) {
      console.log( data );
  });

DÜZENLEME Kaçak çalıştığınız çünkü sen kod başarısız dirolduğu değil , ayrı bir bağımsız program olarak sağladı. cmdSüreçte bir emirdir . Dosya sistemi ile oynamak istiyorsanız yerel kullanın require( 'fs' ).

Alternatif olarak (tavsiye etmiyorum), daha sonra çalıştırabileceğiniz bir toplu iş dosyası oluşturabilirsiniz. İşletim sisteminin varsayılan olarak toplu iş dosyalarını cmd.


... Yardımlarınız için ancak, koşmak bile teşekkür ederim C:\Windows\System32\netstat.exe, bu yine verim vermez sonuçları ... Benim tam sözdizimi oldu foo = new run_cmd('netstat.exe', ['-an'], function (me, data){me.stdout=data;});... Ben de bugüne kadar hiçbir başarı ile tam yolu denedik
Mike Pennington

0

Aslında run_cmd işlevinizden hiçbir şey döndürmüyorsunuz.

function run_cmd(cmd, args, done) {
    var spawn = require("child_process").spawn;
    var child = spawn(cmd, args);
    var result = { stdout: "" };
    child.stdout.on("data", function (data) {
            result.stdout += data;
    });
    child.stdout.on("end", function () {
            done();
    });
    return result;
}

> foo = run_cmd("ls", ["-al"], function () { console.log("done!"); });
{ stdout: '' }
done!
> foo.stdout
'total 28520...'

Gayet iyi çalışıyor. :)


returnNesne niteliklerini doğru şekilde ayarladığınız sürece gerekli olduğunu sanmıyorum
Mike Pennington

0

En çok ödül alan cevabın umut verici bir versiyonu:

  runCmd: (cmd, args) => {
    return new Promise((resolve, reject) => {
      var spawn = require('child_process').spawn
      var child = spawn(cmd, args)
      var resp = ''
      child.stdout.on('data', function (buffer) { resp += buffer.toString() })
      child.stdout.on('end', function () { resolve(resp) })
    })
  }

Kullanmak:

 runCmd('ls').then(ret => console.log(ret))
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.