Выполнение команды node.js
Я все еще пытаюсь понять тонкости того, как я могу запустить команду оболочки linux или windows и выводить вывод в node.js; в конечном счете, я хочу сделать что-то вроде этого...
//pseudocode
output = run_command(cmd, args)
Важным моментом является то, что output
должен быть доступен для переменной (или объекта) с глобальной областью. Я попробовал следующую функцию, но по какой-то причине я получаю undefined
для печати на консоли...
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" <------
У меня возникли проблемы с пониманием того, где код сломается выше... очень простой прототип этой модели работает...
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" <----
Может кто-нибудь помочь мне понять, почему работает try_this()
, а run_cmd()
- нет? FWIW, мне нужно использовать child_process.spawn
, потому что child_process.exec
имеет ограничение на 200 КБ.
Окончательное разрешение
Я принимаю ответ Джеймса Уайта, но это точный код, который работал у меня...
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);
Ответы
Ответ 1
Здесь необходимо устранить три проблемы:
Первый заключается в том, что вы ожидаете синхронного поведения при асинхронном использовании stdout. Все вызовы в вашей функции run_cmd
являются асинхронными, поэтому он будет порождать дочерний процесс и немедленно возвращаться независимо от того, были ли считаны некоторые, все или никакие данные из stdout. Таким образом, при запуске
console.log(foo.stdout);
вы получаете все, что происходит в настоящее время в foo.stdout, и нет гарантии, что это будет, потому что ваш дочерний процесс все еще может работать.
Второй - это то, что stdout является читаемым потоком, поэтому 1) событие данных можно вызывать несколько раз, и 2) обратный вызов получает буфер, а не строку. Легко исправить; просто измените
foo = new run_cmd(
'netstat.exe', ['-an'], function (me, data){me.stdout=data;}
);
в
foo = new run_cmd(
'netstat.exe', ['-an'], function (me, buffer){me.stdout+=buffer.toString();}
);
чтобы преобразовать наш буфер в строку и добавить эту строку в нашу переменную stdout.
Третий заключается в том, что вы можете знать только, что вы получили весь вывод, когда получаете событие "end", что означает, что нам нужен еще один прослушиватель и обратный вызов:
function run_cmd(cmd, args, cb, end) {
// ...
child.stdout.on('end', end);
}
Итак, ваш конечный результат таков:
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) }
);
Ответ 2
Упрощенная версия принятого ответа (третий пункт), просто сработала для меня.
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) });
} // ()
Использование:
run_cmd( "ls", ["-l"], function(text) { console.log (text) });
run_cmd( "hostname", [], function(text) { console.log (text) });
Ответ 3
Самый простой способ - просто использовать оболочку ShellJS...
$ npm install [-g] shelljs
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 поддерживает множество общих команд оболочки, отображаемых как функции NodeJS, включая:
- кошка
- кд
- CHMOD
- ф
- каталоги
- эхо
- Exec
- Выход
- найти
- Grep
- пер
- Ls
- MkDir
- мв
- POPD
- Pushd
- PWD
- гт
- СЕПГ
- тест
- , который
Ответ 4
Я использовал это более кратко:
var sys = require('sys')
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
exec("ls -la", puts);
работает отлично.:)
Ответ 5
У меня была аналогичная проблема, и я закончил тем, что написал для нее расширение node. Вы можете проверить репозиторий git. Это с открытым исходным кодом и бесплатно, и все это хорошо!
https://github.com/aponxi/npm-execxi
ExecXI - это расширение node, написанное на С++, для выполнения команд оболочки один за другим, вывод команды на консоль в в режиме реального времени. Необязательные цепные и неразвязанные способы присутствуют; имея в виду что вы можете остановить остановку script после сбоя команды (цепочка), или вы можете продолжать, как будто ничего не произошло!
Инструкции по использованию находятся в файле ReadMe. Не стесняйтесь делать запросы на тягу или отправлять вопросы!
Я подумал, что стоит упомянуть об этом.
Ответ 6
@TonyO'Hagan является компрессионным ответом shelljs
, но я хотел бы выделить синхронную версию его ответа:
var shell = require('shelljs');
var output = shell.exec('netstat -rn', {silent:true}).output;
console.log(output);
Ответ 7
Синхронный однострочный:
require('child_process').execSync("echo 'hi'", function puts(error, stdout, stderr) { console.log(stdout) });
Ответ 8
В вашей функции run_cmd
есть переменный конфликт:
var me = this;
child.stdout.on('data', function(me, data) {
// me is overriden by function argument
cb(me, data);
});
Просто измените его на это:
var me = this;
child.stdout.on('data', function(data) {
// One argument only!
cb(me, data);
});
Чтобы увидеть ошибки, всегда добавляйте это:
child.stderr.on('data', function(data) {
console.log( data );
});
РЕДАКТИРОВАТЬ. Вы не выполняете код, потому что пытаетесь запустить dir
, который не предоставляется как отдельная отдельная программа. Это команда в процессе cmd
. Если вы хотите играть с файловой системой, используйте native require( 'fs' )
.
В качестве альтернативы (который я не рекомендую) вы можете создать командный файл, который затем можно запустить. Обратите внимание, что ОС по умолчанию запускает пакетные файлы через cmd
.
Ответ 9
Вы ничего не возвращаете из своей функции run_cmd.
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...'
Хорошо работает.:)
Ответ 10
Обещаемая версия наиболее выдающегося ответа:
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) })
})
}
Для использования:
runCmd('ls').then(ret => console.log(ret))