node.js menjalankan perintah sistem secara sinkron

171

Saya membutuhkan fungsi node.js

result = execSync('node -v');

yang secara sinkron akan mengeksekusi baris perintah yang diberikan dan mengembalikan semua stdout'ed oleh teks perintah itu.

ps. Sinkronisasi salah. Aku tahu. Hanya untuk penggunaan pribadi.

MEMPERBARUI

Sekarang kami memiliki solusi mgutz yang memberi kami kode keluar, tetapi tidak stdout! Masih menunggu jawaban yang lebih tepat.

MEMPERBARUI

mgutz memperbarui jawabannya dan solusinya ada di sini :)
Juga, seperti yang disebutkan dgo.a , ada modul yang berdiri sendiri exec-sync

UPDATE 2014-07-30

Lib ShellJS tiba. Anggap ini adalah pilihan terbaik untuk saat ini.


UPDATE 2015-02-10

AKHIRNYA! NodeJS 0.12 mendukung execSyncsecara asli.
Lihat dokumen resmi

dikecewakan
sumber
26
jangan biarkan diri Anda dibodohi, sinkronisasi tidak salah ... BAHKAN di NodeJS semua kode Anda dieksekusi secara sinkron kecuali jika Anda secara eksplisit memanggil metode async ... jika semuanya dilakukan dengan cara asinkron, tidak ada yang akan dilakukan. juga, lebih memilih metode asinkron tidak berarti perhitungan panjang Anda tidak akan memblokir server Anda. itu pilihan. bahwa pembuat Node memilih untuk menyediakan metode sistem file sinkron bersama yang async hanya pergi untuk menunjukkan bahwa ada tempat untuk itu juga.
mengalir
2
Di mana kita dapat menemukan "perpustakaan emulasi shell Unix" yang sedang Anda bicarakan?
Florian
@Florian maksudnya ShellJS
xst

Jawaban:

153

Node.js (sejak versi 0.12 - untuk sementara waktu) mendukung execSync:

child_process.execSync(command[, options])

Anda sekarang dapat langsung melakukan ini:

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

dan itu akan melakukan apa yang Anda harapkan. (Default untuk menyalurkan hasil i / o ke proses induk). Perhatikan bahwa Anda juga bisa spawnSyncsekarang.

Benjamin Gruenbaum
sumber
6
setelah 10 jam putus asa. TERIMA KASIH DUDE
Tom Dev
Bagaimana saya bisa memutuskan sambungan dari subproses ini?
JulianSoto
@JulianSoto nodejs.org/api/…
ilahi
54

Lihat perpustakaan execSync .

Ini cukup mudah dilakukan dengan node-ffi . Saya tidak akan merekomendasikan untuk proses server, tetapi untuk pengembangan umum utilitas itu menyelesaikan sesuatu. Instal perpustakaan.

npm install node-ffi

Skrip contoh:

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

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

[EDIT Jun 2012: Cara mendapatkan 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'));
mgutz
sumber
2
Bagaimana Anda benar-benar mendapatkan sesuatu yang dikirim stdoutdari ini? Yang bisa saya dapatkan hanyalah kode proses keluar
Mark Kahn
@cwolves: Saya pikir async akan lebih baik ini. ( Jawaban Ivo )
pvorb
@ pvorb - yeah, kecuali ketika Anda tidak dapat menggunakan async :)
Mark Kahn
1
Ada alasan yang sah untuk tidak menggunakan palu async untuk setiap paku. Misalnya, mesin templat async di Express 3 dan fungsi pembantu (lokal) harus sinkron. Bagaimana jika fungsi pembantu itu perlu mengkompilasi file yang kurang secara bersamaan dengan cepat?
mgutz
8
Saya heran mengapa hal sederhana execSyncini bukan bagian dari child_process. Saya pikir, seharusnya begitu.
Michael Härtl
31

Gunakan ShellJS modul .

eksekutiffungsi tanpa memberikan panggilan balik.

Contoh:

var version = exec('node -v').output;
falko
sumber
2
Perhatikan bahwa pada saat penulisan, dokumen menyebutkan bahwa sinkron exec()adalah CPU intensif untuk proses yang panjang.
Aram Kocharyan
1
opsi, {silent: true} adalah kunci
Nick
1
Ini melakukan sesuatu (selain memberikan sintaks yang lebih pendek) ini tidak? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764
23

Ada modul yang sangat baik untuk kontrol aliran di node.js yang disebut asyncblock . Jika membungkus kode dalam suatu fungsi OK untuk kasus Anda, sampel berikut dapat dipertimbangkan:

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...');
});
menangkap
sumber
1
Dia bertanya secara eksplisit tentang versi sinkronisasi, bukan mengontrol aliran perpustakaan.
Alexey Petrushin
22
@AlexeyPetrushin Setiap pertanyaan di sini adalah tentang tujuan, bukan tentang cara tertentu untuk mencapainya. Terima kasih sudah downvoting.
menangkap
1
Juga, ini adalah jawaban yang sangat berguna untuk pengguna Windows; menginstal exec-syncatau ffipada Windows memiliki overhead yang sangat besar (VC ++, SDKs, Python, dll), tetapi ini lebih ringan.
Mendhak
10

Ini tidak mungkin di Node.js, keduanya child_process.spawndanchild_process.exec dibangun dari bawah ke atas menjadi async.

Untuk detailnya lihat: https://github.com/ry/node/blob/master/lib/child_process.js

Jika Anda benar-benar ingin memiliki pemblokiran ini, maka letakkan semua yang perlu terjadi setelah itu dalam panggilan balik, atau buat antrean Anda sendiri untuk menangani hal ini dengan cara pemblokiran, saya kira Anda bisa menggunakan Async.js untuk tugas ini.

Atau, jika Anda memiliki terlalu banyak waktu untuk dihabiskan, coba-coba Node.js sendiri.

Ivo Wetzel
sumber
12
aneh karena modul sistem file memiliki panggilan sinkron. Kenapa tidak juga dieksekusi?
Alfred
4
@ Alfred Panggilan FS sinkronisasi terutama ada di sana untuk memuat konfigurasi pada awal program.
Ivo Wetzel
3
@IvoWetzel - tisk tisk ... belumkah kita belajar untuk tidak pernah mengatakan sesuatu itu mustahil? ;) lihat solusi saya di bawah ini.
Marcus Paus
1
@IvoWetzel "Sinkronisasi panggilan FS ..." - benar, dan kadang-kadang Anda ingin, misalnya, mengeluarkan perintah untuk mengkompilasi sesuatu pada awal program dan melanjutkan penyelesaian. — mengingat bahwa ada panggilan FS sinkronisasi, tidak memiliki sinkronisasi sinkronisasi memang terlihat seperti pengawasan. Saya semua untuk asinkron, tetapi sinkron memang memiliki kelebihan dan kasus penggunaan. Harus menggunakannya dengan cara yang bijaksana, tentu saja.
mengalir
Async baik-baik saja, tetapi jika WidgetB bergantung pada hasil akhir WidgetA, semua async di dunia tidak akan menyelesaikan pekerjaan. Terkadang proses harus sinkron. Cobalah memasak secara tidak sinkron. ;)
Lloyd Sargent
9

Ini adalah cara termudah yang saya temukan:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(Jangan bingung dengan execSync.)
Jalankan perintah shell secara sinkron. Gunakan ini untuk skrip migrasi, program cli, tetapi tidak untuk kode server biasa.

Contoh:

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

Anda dapat mencapai ini menggunakan serat. Misalnya, menggunakan perpustakaan Common Node saya , kode akan terlihat seperti ini:

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

Saya terbiasa menerapkan "synchronous"hal-hal di akhir fungsi panggilan balik. Tidak terlalu bagus, tetapi berhasil. Jika Anda perlu mengimplementasikan urutan eksekusi baris perintah, Anda perlu membungkusnya execdengan beberapa fungsi bernama dan memanggilnya secara rekursif. Pola ini tampaknya dapat digunakan untuk saya:

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);
        }
    });
};
scherka
sumber
3

Saya memiliki masalah yang sama dan akhirnya saya menulis ekstensi node untuk ini. Anda dapat memeriksa repositori git. Ini open source dan gratis dan semuanya bagus!

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

ExecXI adalah ekstensi node yang ditulis dalam C ++ untuk mengeksekusi perintah shell satu per satu, mengeluarkan output perintah ke konsol secara real-time. Tersedia pilihan cara berantai, dan tidak berantai; artinya Anda dapat memilih untuk menghentikan skrip setelah perintah gagal (dirantai), atau Anda dapat melanjutkan seolah-olah tidak ada yang terjadi!

Instruksi penggunaan ada di file ReadMe . Jangan ragu untuk melakukan permintaan tarik atau mengirim masalah!

EDIT: Namun itu belum mengembalikan stdout ... Hanya keluaran mereka secara real-time. Itu sekarang. Yah, saya baru saja merilisnya hari ini. Mungkin kita bisa membangunnya.

Bagaimanapun, saya pikir itu layak untuk disebutkan.

Logan
sumber
Inilah yang sebenarnya saya cari. Terima kasih telah meluangkan waktu untuk membuat ini.
thealfreds
Satu-satunya hal yang belum saya ketahui dan implementasikan adalah konfigurasi build untuk versi nodejs yang berbeda. Saya kira saya menulisnya di node 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) jadi selama npm installberhasil (dengan kata lain sebagai plugin kompilasi), maka baik untuk produksi. Selain menargetkan berbagai versi nodejs, saya akan mengatakan itu ditambal untuk produksi, atau itu cukup stabil. Jika ada bug, Anda dapat mengirim permintaan tarik atau memposting masalah di github :)
Logan
1

Anda dapat melakukan operasi shell sinkron di nodejs seperti:

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 - contoh ini dimaksudkan untuk lingkungan windows, sesuaikan dengan kebutuhan linux Anda sendiri jika perlu

Marcus Pope
sumber
Ya, dan secepat CPU Anda mungkin dapat memanggil ... Tapi hei ketika Anda "perlu" melakukan sesuatu yang jahat, Setan adalah pria Anda, kan?
Marcus Pope
ok, ini benar-benar jahat, tapi luar biasa. Saya memerlukan ini untuk menangani acara filesystem event browserify.on ('register'), yang tidak memiliki panggilan balik. Selamatkan hari saya!
Robert Gould
dapatkah Anda menjelaskan mengapa ini bekerja pada mesin javascript? bukankah while loop dieksekusi secara tak terbatas pada tick yang sama sementara exec mengeksekusi pada tick berikutnya?
badunk
Memaksimalkan CPU dengan menunggu sibuk adalah desain yang buruk.
Louis
@ Louis-DominiqueDubeau Tentu, tetapi sebenarnya tidak ada alternatif lain yang tidak bergantung pada sumber pihak ketiga yang mungkin atau mungkin tidak kompatibel lintas platform. Ini juga bukan maxing out CPU yang sebenarnya karena OS tidak akan memberikan prioritas penuh pada proses nodejs. Saya pikir implementasi sinkronisasi ops shell sudah di cakrawala atau mungkin sudah ada di sini.
Marcus Pope
1

Saya sebenarnya memiliki situasi di mana saya perlu menjalankan beberapa perintah satu per satu dari script package.json preinstall dengan cara yang akan bekerja pada Windows dan Linux / OSX, jadi saya tidak bisa mengandalkan modul non-inti.

Jadi inilah yang saya pikirkan:

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

Anda bisa menggunakannya seperti ini:

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

EDIT: seperti yang ditunjukkan, ini sebenarnya tidak mengembalikan output atau memungkinkan Anda untuk menggunakan hasil dari perintah dalam program Node. Satu ide lain untuk itu adalah menggunakan backcalls LiveScript. http://livescript.net/

Jason Livesay
sumber
Terima kasih, tetapi ini bukan jawaban karena kode Anda menjalankan perintah dalam rangkaian secara asinkron
rusak
Jika Anda perlu menjalankan serangkaian perintah secara sinkron sesuai contoh saya itu akan berhasil. Saya pikir saya salah mengerti pertanyaan Anda, maaf. Saya menambahkan ide lain untuk jawabannya.
Jason Livesay