thực thi lệnh shell node.js


113

Tôi vẫn đang cố gắng nắm bắt những điểm tốt hơn về cách tôi có thể chạy lệnh shell linux hoặc windows và nắm bắt đầu ra trong node.js; cuối cùng, tôi muốn làm một cái gì đó như thế này ...

//pseudocode
output = run_command(cmd, args)

Phần quan trọng là outputphải có sẵn cho một biến (hoặc đối tượng) có phạm vi toàn cục. Tôi đã thử chức năng sau, nhưng vì lý do nào đó, tôi được undefinedin ra bảng điều khiển ...

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" <------

Tôi đang gặp khó khăn khi hiểu đoạn mã ở trên bị ngắt ở đâu ... một nguyên mẫu rất đơn giản của mô hình đó hoạt động ...

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" <----

Ai đó có thể giúp tôi hiểu tại sao try_this()hoạt động và run_cmd()không? FWIW, tôi cần sử dụng child_process.spawn, vì child_process.execcó giới hạn bộ đệm 200KB.

Giải quyết cuối cùng

Tôi chấp nhận câu trả lời của James White, nhưng đây là mã chính xác phù hợp với tôi ...

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
Trong Final Resolution bạn nên đặt me.stdout = "";trong cmd_exec()để ngăn chặn concatenating undefinedđến đầu của kết quả.
aorcsik

Này, mã độ phân giải cuối cùng đó hoàn toàn khủng khiếp, điều gì sẽ xảy ra nếu mất hơn 0,25 giây để thực thi netstat của bạn?
Steven Lu,

Ummmm ... có thể sử dụng một trong những câu trả lời mà tôi đã thưởng cho ?????
Mike Pennington

Câu trả lời:


89

Có ba vấn đề ở đây cần được khắc phục:

Đầu tiên là bạn đang mong đợi hành vi đồng bộ trong khi sử dụng stdout không đồng bộ. Tất cả các lệnh gọi trong run_cmdhàm của bạn là không đồng bộ, vì vậy nó sẽ sinh ra tiến trình con và trả về ngay lập tức bất kể một số, tất cả hay không có dữ liệu nào đã được đọc khỏi stdout. Như vậy, khi bạn chạy

console.log(foo.stdout);

bạn nhận được bất cứ điều gì xảy ra được lưu trữ trong foo.stdout vào lúc này và không có gì đảm bảo điều đó sẽ là gì vì quy trình con của bạn có thể vẫn đang chạy.

Thứ hai là stdout là một luồng có thể đọc được , vì vậy 1) sự kiện dữ liệu có thể được gọi nhiều lần và 2) lệnh gọi lại được cung cấp một bộ đệm, không phải một chuỗi. Dễ dàng để khắc phục; Chỉ là sự thay đổi

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

thành

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

để chúng tôi chuyển đổi bộ đệm của mình thành một chuỗi và nối chuỗi đó vào biến stdout của chúng tôi.

Thứ ba là bạn chỉ có thể biết bạn đã nhận được tất cả đầu ra khi bạn nhận được sự kiện 'kết thúc', có nghĩa là chúng tôi cần một trình nghe và gọi lại khác:

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

Vì vậy, kết quả cuối cùng của bạn là:

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) }
);

"không có gì đảm bảo điều đó sẽ xảy ra vì quá trình con của bạn có thể vẫn đang chạy" gần ... nhưng có một đảm bảo rằng nó sẽ không được đặt vào thời điểm đó và sẽ chỉ được đặt khi lệnh gọi lại cuối cùng được gọi, như bạn chỉ ở nơi khác

4
Đây là một câu trả lời tuyệt vời với những giải thích tuyệt vời về một số khái niệm JS rất quan trọng. Đẹp!
L0j1k

1
Bạn sẽ muốn thực hiện this.stdout = "";bên trong run()hàm, nếu không console.log(foo.sdtout);sẽ có tiền tố là undefined.
f1lt3r

78

Một phiên bản đơn giản của câu trả lời được chấp nhận (điểm thứ ba), chỉ phù hợp với tôi.

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) });
} // ()

Sử dụng:

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

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

1
Còn về giá trị trả về, khi tiến trình trả về một số khác không, làm thế nào tôi có thể lấy nó?
Vitim.us

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

52

Tôi đã sử dụng điều này một cách ngắn gọn hơn:

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

nó hoạt động hoàn hảo. :)


1
Điều này hoạt động tốt và không yêu cầu bất kỳ mô-đun nút bổ sung nào. Tôi thích nó!
bearvarine

9
sys.puts()đã không được dùng nữa vào năm 2011 (với Node.js v0.2.3). Bạn nên sử dụng console.log()thay thế.
tfmontague

1
điều đó thực sự hoạt động ... vì vậy tôi tự hỏi, tại sao đây không phải là câu trả lời? nó rất đơn giản
ekkis

Chà !! câu trả lời này là hoàn hảo và thanh lịch. cảm ơn bạn.
Em Ji Madhu

43

Cách đơn giản nhất là chỉ cần sử dụng ShellJS lib ...

$ npm install [-g] shelljs

Ví dụ EXEC:

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 hỗ trợ nhiều lệnh shell phổ biến được ánh xạ dưới dạng các hàm NodeJS bao gồm:

  • con mèo
  • CD
  • chmod
  • cp
  • dirs
  • tiếng vang
  • người thực thi
  • lối ra
  • tìm thấy
  • grep
  • ln
  • ls
  • mkdir
  • mv
  • popd
  • pushd
  • pwd
  • rm
  • quyến rũ
  • kiểm tra
  • cái nào

làm thế nào để thêm một tham số vào tập lệnh shell được gọi bởi shell.exec ("foo.sh")?
pseudozach

1
Bạn chỉ có thể thêm lập luận của bạn đứng đầu chuỗi: shell.exec("foo.sh arg1 arg2 ... "). Bạn foo.shkịch bản có thể tham khảo những cách sử dụng $1, $2... vv
Tony O'Hagan

Nếu lệnh bạn đang cố gắng thực thi cần đầu vào từ người dùng thì ĐỪNG sử dụng ShellJS execute (). Về bản chất, chức năng này không có tính tương tác, vì nó sẽ chỉ nhận lệnh và in đầu ra, Không thể chấp nhận các đầu vào ở giữa. Thay vào đó, hãy sử dụng quy trình con tích hợp sẵn. Ví dụ: https://stackoverflow.com/a/31104898/9749509
MPatel1

4

Tôi đã gặp sự cố tương tự và 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 lưu trữ git. Nó là mã 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 hiện từng lệnh shell một, xuất kết quả đầu ra của lệnh đó tới bảng điều khiển trong thời gian thực. Các cách được xâu chuỗi tùy chọn và không xác định có sẵn; nghĩa là bạn có thể chọn dừng tập lệnh sau khi một lệnh bị lỗi (chuỗi) 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 đưa ra các yêu cầu kéo hoặc gửi các vấn đề!

Tôi nghĩ rằng nó là giá trị để đề cập đến nó.


4

@ TonyO'Hagan là shelljscâu trả lời phức tạp , nhưng, tôi muốn nhấn mạnh phiên bản đồng bộ của câu trả lời của anh ấy:

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

1

Một lớp lót đồng bộ:

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


0

Có một xung đột biến trong run_cmdchức năng của bạn :

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

Chỉ cần thay đổi nó thành thế này:

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

Để xem lỗi, hãy luôn thêm điều này:

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

EDIT Bạn đang đang thất bại vì bạn đang cố gắng chạy dirđược không được cung cấp như một chương trình độc lập riêng biệt. Nó là một lệnh trong cmdquá trình. Nếu bạn muốn chơi với hệ thống tập tin, hãy sử dụng bản địa require( 'fs' ).

Ngoài ra (tôi không khuyến khích) bạn có thể tạo một tệp hàng loạt mà sau đó bạn có thể chạy. Lưu ý rằng hệ điều hành theo mặc định sẽ kích hoạt các tệp hàng loạt qua cmd.


cảm ơn bạn đã giúp đỡ của bạn ... Tuy nhiên, ngay cả khi tôi chạy C:\Windows\System32\netstat.exe, điều này vẫn không mang lại kết quả ... cú pháp chính xác của tôi đã foo = new run_cmd('netstat.exe', ['-an'], function (me, data){me.stdout=data;});... Tôi cũng đã cố gắng đường dẫn đầy đủ với không thành công cho đến nay
Mike Pennington

0

Bạn không thực sự trả lại bất kỳ thứ gì từ hàm run_cmd của mình.

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...'

Hoạt động tốt. :)


Tôi không nghĩ returnlà cần thiết miễn là bạn thiết lập đúng các thuộc tính đối tượng
Mike Pennington

0

Một phiên bản quảng cáo của câu trả lời được trao giải nhiều nhất:

  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) })
    })
  }

Để sử dụng:

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