node.js thực thi lệnh hệ thống một cách đồng bộ


171

Tôi cần trong hàm node.js

result = execSync('node -v');

điều đó sẽ thực thi đồng bộ dòng lệnh đã cho và trả về tất cả các thiết bị xuất chuẩn của văn bản lệnh đó.

ps. Đồng bộ hóa là sai. Tôi biết. Chỉ để sử dụng cá nhân.

CẬP NHẬT

Bây giờ chúng tôi có giải pháp của mgutz cung cấp cho chúng tôi mã thoát, nhưng không xuất sắc! Vẫn đang chờ câu trả lời chính xác hơn.

CẬP NHẬT

mgutz cập nhật câu trả lời của mình và giải pháp là đây :)
Ngoài ra, như dgo.a đề cập, có độc lập mô-đun exec-sync

CẬP NHẬT 2014-07-30

ShellJS lib đã đến. Hãy coi đây là sự lựa chọn tốt nhất cho bây giờ.


CẬP NHẬT 2015/02/10

CUỐI CÙNG! NodeJS 0.12 hỗ trợ execSynchữu cơ.
Xem tài liệu chính thức


26
đừng để bản thân bị lừa, đồng bộ hóa không sai ... NGAY CẢ trong NodeJS, tất cả mã của bạn được thực thi đồng bộ trừ khi bạn gọi một phương thức không đồng bộ một cách rõ ràng ... nếu mọi thứ được thực hiện theo cách không đồng bộ thì sẽ không có gì được thực hiện. Ngoài ra, việc ưu tiên các phương thức không đồng bộ không có nghĩa là phép tính dài của bạn sẽ không chặn máy chủ của bạn. đó là một sự lựa chọn rằng các nhà sản xuất của Node đã chọn cung cấp các phương thức hệ thống tệp đồng bộ cùng với các phương thức không đồng bộ chỉ để cho thấy rằng cũng có một nơi dành cho chúng.
chảy

2
Chúng ta có thể tìm thấy "thư viện mô phỏng shell Unix" mà bạn đang nói đến ở đâu?
Florian

@Florian anh ấy có nghĩa là ShellJS
xst

Câu trả lời:


153

Node.js (kể từ phiên bản 0.12 - vì vậy trong một thời gian) hỗ trợ execSync:

child_process.execSync(command[, options])

Bây giờ bạn có thể trực tiếp làm điều này:

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

và nó sẽ làm những gì bạn mong đợi. (Mặc định để chuyển các kết quả i / o sang quy trình cha). Lưu ý rằng bạn cũng có thể spawnSyncbây giờ.


6
sau 10 giờ tuyệt vọng. THANKS DUDE
Tom Dev

Làm thế nào tôi có thể ngắt kết nối từ quy trình con này?
JulianSoto


54

Xem thư viện execSync .

Nó khá dễ thực hiện với nút-ffi . Tôi sẽ không đề xuất cho các quy trình máy chủ, nhưng đối với các tiện ích phát triển chung, nó sẽ hoàn thành công việc. Cài đặt thư viện.

npm install node-ffi

Kịch bản ví dụ:

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

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

[EDIT tháng 6 năm 2012: Làm thế nào để mắc 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
Làm thế nào bạn sẽ đi về thực sự nhận được bất cứ điều gì gửi stdouttừ này? Tất cả những gì tôi có thể nhận được là mã thoát quy trình
Mark Kahn

@cwolves: Tôi nghĩ async sẽ tốt hơn thế này. ( Câu trả lời của Ivo )
pvorb

@pvorb - yeah, ngoại trừ khi bạn không thể sử dụng async :)
Mark Kahn

1
Có những lý do hợp lệ cho việc không sử dụng búa async cho mọi móng tay. Ví dụ, các công cụ mẫu không đồng bộ trong Express 3 và các hàm trợ giúp (cục bộ) cần phải được đồng bộ hóa. Điều gì sẽ xảy ra nếu các hàm trợ giúp này cần biên dịch ít tệp không đồng bộ khi đang di chuyển?
mgutz

8
Tôi tự hỏi tại sao đơn giản execSyncnày không phải là một phần của child_process. Tôi nghĩ nó nên như vậy.
Michael Härtl

31

Sử dụng ShellJS mô-đun .

thực hiệnHàm mà không cung cấp gọi lại.

Thí dụ:

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

2
Lưu ý rằng tại thời điểm viết, các tài liệu đề cập rằng tính đồng bộ exec()cần nhiều CPU cho các quy trình dài.
Aram Kocharyan

1
tùy chọn, {im lặng: đúng} là chính
Nick

1
Điều này làm một cái gì đó (ngoài việc cung cấp cú pháp ngắn hơn) này không? const execSync = require('child_process').execSync; code = execSync('node -v');
dùng2503764

23

Có một mô-đun tuyệt vời để kiểm soát luồng trong node.js được gọi là asyncblock . Nếu gói mã trong một hàm là OK cho trường hợp của bạn, mẫu sau có thể được xem xét:

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
Ông hỏi rõ ràng về phiên bản đồng bộ hóa, không kiểm soát các thư viện dòng chảy.
Alexey Petrushin

22
@AlexeyPetrushin Mỗi câu hỏi ở đây là về một mục tiêu, không phải về cách cụ thể để đạt được nó. Cảm ơn cho downvote mặc dù.
nab

1
Ngoài ra, đây là một câu trả lời rất hữu ích cho người dùng Windows; cài đặt exec-synchoặc ffitrên Windows có một chi phí rất lớn (VC ++, SDK, Python, v.v.), nhưng cái này nhẹ hơn.
Mendhak

10

Điều này là không thể có trong Node.js, cả child_process.spawnchild_process.exec được xây dựng từ đầu để trở thành không đồng bộ.

Để biết chi tiết, hãy xem: https://github.com/ry/node/blob/master/lib/child_ Process.js

Nếu bạn thực sự muốn có tính năng chặn này, sau đó đặt mọi thứ cần xảy ra sau đó trong một cuộc gọi lại hoặc xây dựng hàng đợi của riêng bạn để xử lý việc này theo kiểu chặn, tôi cho rằng bạn có thể sử dụng Async.js cho nhiệm vụ này.

Hoặc, trong trường hợp bạn có quá nhiều thời gian để chi tiêu, hãy tự hack trong Node.js.


12
lạ vì mô-đun hệ thống tập tin có các cuộc gọi đồng bộ. Tại sao không thực hiện?
Alfred

4
@Alfred Các cuộc gọi FS đồng bộ chủ yếu ở đó để tải các cấu hình khi bắt đầu chương trình.
Ivo Wetzel

3
@IvoWetzel - tisk tisk ... chúng ta đã học được cách không bao giờ nói điều gì đó là không thể? ;) xem giải pháp của tôi dưới đây.
Giáo hoàng Marcus

1
@IvoWetzel "Cuộc gọi FS đồng bộ hóa ..." - đúng, và đôi khi bạn muốn, giả sử, ra lệnh biên dịch một cái gì đó khi bắt đầu chương trình và tiếp tục hoàn thành. Đã cho rằng có các cuộc gọi FS đồng bộ hóa, không có lệnh thực thi đồng bộ hóa trông giống như một giám sát. Tôi là tất cả cho không đồng bộ, nhưng đồng bộ có trường hợp ưu và sử dụng của nó. tất nhiên phải sử dụng nó một cách thận trọng
chảy

Async vẫn ổn, nhưng nếu WidgetB phụ thuộc vào kết quả cuối cùng của WidgetA, tất cả các async trên thế giới sẽ không hoàn thành công việc. Đôi khi các quy trình phải được đồng bộ. Hãy thử nấu không đồng bộ. ;)
Lloyd Sargent

9

Đây là cách dễ nhất tôi tìm thấy:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(Không bị nhầm lẫn với execSync.)
Thực thi lệnh shell một cách đồng bộ. Sử dụng điều này cho các tập lệnh di chuyển, chương trình cli, nhưng không phải cho mã máy chủ thông thường.

Thí dụ:

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


5

Bạn có thể đạt được điều này bằng cách sử dụng sợi. Ví dụ: sử dụng thư viện Nút chung của tôi , mã sẽ trông như thế này:

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

3

Tôi được sử dụng để thực hiện "synchronous"công cụ ở cuối chức năng gọi lại. Không đẹp lắm, nhưng nó hoạt động. Nếu bạn cần thực hiện một chuỗi các lệnh thực thi dòng lệnh, bạn cần gói execvào một số hàm được đặt tên và gọi đệ quy nó. Mẫu này dường như có thể sử dụng được cho tôi:

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

Tôi đã có một vấn đề tương tự và cuối cùng tôi đã viết một phần mở rộng nút cho việc này. Bạn có thể kiểm tra kho git. Đó là nguồn mở và miễn phí và tất cả những thứ tốt!

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

ExecXI là một phần mở rộng nút được viết bằng C ++ để thực thi từng lệnh shell, xuất ra đầu ra của lệnh cho bàn điều khiển trong thời gian thực. Tùy chọn xiềng xích, và cách không rõ ràng có mặt; có nghĩa là bạn có thể chọn dừng tập lệnh sau khi lệnh bị lỗi (bị xiềng xích) hoặc bạn có thể tiếp tục như thể không có gì xảy ra!

Hướng dẫn sử dụng có trong tệp ReadMe . Hãy thoải mái thực hiện các yêu cầu kéo hoặc gửi vấn đề!

EDIT: Tuy nhiên, nó vẫn chưa trả về thiết bị xuất chuẩn ... Chỉ cần xuất chúng theo thời gian thực. Nó làm ngay bây giờ.Vâng, tôi vừa phát hành nó ngày hôm nay. Có lẽ chúng ta có thể xây dựng trên nó.

Dù sao, tôi nghĩ rằng nó là giá trị để đề cập đến nó.


Đây chính xác là những gì tôi đã tìm kiếm. Cảm ơn bạn đã dành thời gian để thực hiện điều này.
thealfreds

Điều duy nhất tôi chưa tìm ra và thực hiện là cấu hình xây dựng cho các phiên bản nodejs khác nhau. Tôi đoán tôi đã viết nó vào nút 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) để càng lâu càng npm installthành công (hay nói cách khác là biên dịch cắm), sau đó nó là tốt để đi phục vụ sản xuất. Bên cạnh việc nhắm mục tiêu các phiên bản nodejs khác nhau, tôi muốn nói rằng nó đã được vá để sản xuất hoặc nó khá ổn định. Nếu có bất kỳ lỗi nào bạn có thể gửi yêu cầu kéo hoặc gửi một vấn đề tại github :)
Logan

1

bạn có thể thực hiện các thao tác shell đồng bộ trong nodejs như vậy:

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 - ví dụ này có nghĩa là cho các môi trường windows, điều chỉnh cho nhu cầu linux của riêng bạn nếu cần thiết


Phải, và nhanh như CPU ​​của bạn có thể triệu tập ... Nhưng này, khi bạn "cần" làm điều gì đó xấu xa, Satan là người đàn ông của bạn phải không?
Marcus Pope

ok, đây là ác hoàn toàn, nhưng tuyệt vời. Tôi cần điều này để xử lý một sự kiện hệ thống tập tin browserify.on ('đăng ký'), không có cuộc gọi lại. Cứu ngày của tôi!
Robert Gould

Bạn có thể giải thích tại sao điều này hoạt động trên các công cụ javascript? vòng lặp while có nên được thực hiện vô hạn trên cùng một dấu tích trong khi thực thi trên vòng tiếp theo không?
badunk

Tối đa hóa CPU bằng cách chờ đợi bận rộn là thiết kế tồi.
Louis

@ Louis-DominiqueDubeau Chắc chắn, nhưng thực sự không có bất kỳ sự thay thế nào không phụ thuộc vào một số nguồn bên thứ ba có thể hoặc không thể tương thích đa nền tảng. Đây cũng không phải là một tối đa thực sự của CPU vì HĐH sẽ không ưu tiên hoàn toàn cho quá trình nodejs. Tôi nghĩ việc triển khai đồng bộ của các ops shell đang ở phía chân trời hoặc có lẽ ở đây rồi.
Giáo hoàng Marcus

1

Tôi thực sự đã gặp phải tình huống cần chạy nhiều lệnh lần lượt từ tập lệnh cài đặt trước pack.json theo cách có thể hoạt động trên cả Windows và Linux / OSX, vì vậy tôi không thể dựa vào mô-đun không lõi.

Vì vậy, đây là những gì tôi đã đưa ra:

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

Bạn có thể sử dụng nó như thế này:

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

EDIT: như đã chỉ ra, điều này không thực sự trả về đầu ra hoặc cho phép bạn sử dụng kết quả của các lệnh trong chương trình Nút. Một ý tưởng khác cho điều đó là sử dụng backcalls LiveScript. http://livescript.net/


Cảm ơn, nhưng đây không phải là câu trả lời vì mã của bạn chạy các lệnh theo chuỗi không đồng bộ
vô hiệu hóa vào

Nếu bạn cần thực thi một loạt các lệnh một cách đồng bộ theo ví dụ của tôi, nó sẽ hoạt động. Tôi nghĩ rằng tôi đã hiểu nhầm câu hỏi của bạn, xin lỗi. Tôi đã thêm một ý tưởng cho câu trả lời.
Jason Livesay
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.