node.js выполняет системную команду синхронно


171

Мне нужно в функции node.js

result = execSync('node -v');

это будет синхронно выполнять заданную командную строку и возвращать все stdout'ed по этому тексту команды.

пс. Синхронизация неверна. Я знаю. Просто для личного пользования.

ОБНОВИТЬ

Теперь у нас есть решение mgutz, которое дает нам код выхода, но не стандартный вывод! Все еще жду более точного ответа.

ОБНОВИТЬ

mgutz обновил свой ответ и решение здесь :)
Также, как упоминалось в dgo.a , есть отдельный модуль exec-sync

ОБНОВЛЕНИЕ 2014-07-30

ShellJS lib прибыла. Считайте, что это лучший выбор на данный момент.


ОБНОВЛЕНИЕ 2015-02-10

НАКОНЕЦ! NodeJS 0.12 поддерживает execSyncизначально.
Смотрите официальные документы


26
не дайте себя одурачить, синхронизация не является ошибкой ... ДАЖЕ в NodeJS весь ваш код выполняется синхронно, если вы явно не вызываете асинхронный метод ... если бы все было сделано асинхронно, ничего бы никогда не было сделано. Кроме того, предпочтение асинхронным методам не означает, что ваши длительные вычисления не будут блокировать ваш сервер. это выбор. то, что создатели Node решили предоставить методы синхронной файловой системы наряду с асинхронными, просто показывает, что для них тоже есть место.
поток

2
Где найти «библиотеку эмуляции оболочки Unix», о которой вы говорите?
Флориан

@Florian он имеет в виду ShellJS
xst

Ответы:


153

Node.js (начиная с версии 0.12 и так далее) поддерживает execSync:

child_process.execSync(command[, options])

Теперь вы можете напрямую сделать это:

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

и он будет делать то, что вы ожидаете. (По умолчанию направляет результаты ввода / вывода в родительский процесс). Обратите внимание, что вы можете и spawnSyncсейчас.


6
после 10 часов отчаяния. СПАСИБО ЧУВАК
Том Дев

Как я могу отключиться от этого подпроцесса?
JulianSoto


54

Смотрите библиотеку execSync .

Это довольно легко сделать с помощью node-ffi . Я не рекомендовал бы для серверных процессов, но для общих утилит разработки это делает вещи. Установите библиотеку.

npm install node-ffi

Пример скрипта:

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

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

[РЕДАКТИРОВАТЬ Июнь 2012: Как получить 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
Как бы вы на самом деле получили что-нибудь stdoutот этого? Все, что я могу получить, это код завершения процесса
Марк Кан

@cwolves: я думаю, что async было бы лучше. ( Ответ Иво )
pvorb

@pvorb - да, за исключением случаев, когда вы не можете использовать асинхронность :)
Марк Кан

1
Есть веские причины не использовать асинхронный молоток для каждого гвоздя. Например, движки шаблонов являются асинхронными в Express 3, а вспомогательные функции (локальные) должны быть синхронными. Что если эти вспомогательные функции должны компилировать файлы Less асинхронно на лету?
mgutz

8
Интересно, почему это простое execSyncне является частью child_process. Я думаю, так и должно быть.
Михаэль Хертл

31

Используйте модуль ShellJS .

Exec функция без предоставления обратного вызова.

Пример:

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

2
Обратите внимание, что на момент написания документа упоминалось, что синхронная нагрузка на exec()процессор для длительных процессов.
Арам Кочарян

1
параметры, {silent: true} является ключевым
Ник

1
Это сделать что-то (кроме обеспечения более короткого синтаксиса), это не так? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

В node.js есть отличный модуль для управления потоком, который называется asyncblock . Если перенос кода в функцию подходит для вашего случая, можно рассмотреть следующий пример:

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
Он явно задал вопрос о версии синхронизации, а не о библиотеках потоков управления.
Алексей Петрушин

22
@AlexeyPetrushin Каждый вопрос здесь касается цели, а не конкретного способа ее достижения. Спасибо за снижение хотя.
стащить

1
Кроме того, это очень полезный ответ для пользователей Windows; установка exec-syncили ffiна Windows имеет огромные накладные расходы (VC ++, SDK, Python и т. д.), но это легче.
Мендхак

10

Это невозможно в Node.js, оба child_process.spawnи child_process.execбыли созданы с нуля, чтобы быть асинхронными.

Подробнее см .: https://github.com/ry/node/blob/master/lib/child_process.js.

Если вы действительно хотите иметь эту блокировку, затем поместите все, что должно произойти впоследствии, в обратный вызов, или создайте свою собственную очередь, чтобы обработать это блокирующим способом, я полагаю, вы могли бы использовать Async.js для этой задачи.

Или, если у вас слишком много времени, взломайте Node.js самостоятельно.


12
странно, потому что модуль файловой системы имеет синхронные вызовы. Почему бы не выполнить?
Альфред

4
@Alfred Синхронизирующие вызовы FS в основном используются для загрузки конфигов при запуске программы.
Иво Ветцель

3
@IvoWetzel - тиск тиск ... разве мы не научились никогда не говорить что-то невозможное? ;) см. мое решение ниже.
Маркус Папа

1
@IvoWetzel "Синхронизация вызовов FS ..." - верно, и иногда вы хотите, скажем, выполнить команду для компиляции чего-либо при запуске программы и продолжить ее после завершения - учитывая, что существуют вызовы синхронизации FS, не имеющие функции синхронизации выглядит как недосмотр. Я все для асинхронного, но у синхронного есть свои плюсы и варианты использования. Конечно, нужно использовать это разумно.
поток

Асинхронизация - это хорошо, но если WidgetB зависит от конечных результатов WidgetA, все асинхронные операции в мире не справятся с этой задачей. Иногда процессы должны быть синхронными. Попробуйте готовить в асинхронном режиме. ;)
Ллойд Сарджент

9

Это самый простой способ, который я нашел:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(не путать с execSync.)
Выполнить команду оболочки синхронно. Используйте это для скриптов миграции, программ cli, но не для обычного серверного кода.

Пример:

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

7

Просто чтобы добавить, что, хотя есть несколько вариантов использования, где вы должны их использовать, spawnSync/ execFileSync/ execSyncбыли добавлены в node.js в этих коммитах: https://github.com/joyent/node/compare/d58c206862dc...e8df2676748e


Означает ли это, что мы будем иметь его в v0.12 из коробки?
оскверненный



3

Я привыкла реализовывать "synchronous"вещи в конце функции обратного вызова. Не очень приятно, но это работает. Если вам нужно реализовать последовательность выполнения командной строки, вам нужно заключить execв какую-нибудь именованную функцию и рекурсивно вызвать ее. Этот шаблон, похоже, пригодится мне:

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

У меня была похожая проблема, и я закончил писать расширение узла для этого. Вы можете проверить репозиторий git. Это с открытым исходным кодом и бесплатно, и все такое хорошее!

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

ExecXI - это расширение узла, написанное на C ++ для выполнения команд оболочки, выводящих выходные данные команды на консоль в режиме реального времени. Факультативно прикованные, и раскованные пути присутствуют; Это означает, что вы можете остановить сценарий после сбоя команды (цепочки) или продолжить, как будто ничего не произошло!

Инструкции по использованию находятся в файле ReadMe . Не стесняйтесь подавать заявки или отправлять вопросы!

РЕДАКТИРОВАТЬ: Однако он еще не возвращает стандартный вывод ... Просто выводит их в режиме реального времени. Это сейчас. Ну, я только что выпустил это сегодня. Может быть, мы можем основываться на этом.

Во всяком случае, я думал, что стоит упомянуть об этом.


Это именно то, что я искал. Спасибо, что нашли время, чтобы сделать это.
thealfreds

Единственное, что я не понял и не реализовал, - это конфигурацию сборки для разных версий nodejs. Я думаю, что я написал это на узле 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ), так что пока это npm installудается (другими словами, как компиляция плагина), тогда хорошо идти в производство. Помимо нацеливания на разные версии nodejs, я бы сказал, что он исправлен для производства или что он довольно стабилен. Если есть какие-либо ошибки, вы можете отправить их по запросу или опубликовать вопрос на github :)
Logan

1

Вы можете выполнять синхронные операции оболочки в nodejs следующим образом:

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\\"));

РЕДАКТИРОВАТЬ - этот пример предназначен для сред Windows, при необходимости настройте его под собственные потребности Linux


Да, и так быстро, как ваш процессор может вызвать ... Но эй, когда вам "нужно" сделать что-то злое, сатана ваш человек, верно?
Маркус Папа

хорошо, это чистое зло, но потрясающе. Мне нужно было это для обработки события файловой системы browserify.on ('register'), у которого не было обратного вызова. Спас мой день!
Роберт Гулд

Вы можете объяснить, почему это работает на движках JavaScript? не должен ли цикл while выполняться бесконечно на одном тике, в то время как exec выполняется на следующем?
Badunk

Максимальное использование процессора при занятом ожидании - плохой дизайн.
Луи

@ Louis-DominiqueDubeau Конечно, но на самом деле нет альтернативы, которая не зависит от какого-либо стороннего источника, который может быть или не быть кросс-платформенным. Это также не является максимальным использованием ЦП, потому что ОС не будет отдавать полный приоритет процессу nodejs. Я думаю, что синхронизация реализаций оболочки уже на горизонте или, возможно, уже здесь.
Маркус Папа

1

У меня фактически была ситуация, когда мне нужно было запускать несколько команд одну за другой из сценария предварительной установки package.json таким образом, чтобы он работал как в Windows, так и в Linux / OSX, поэтому я не мог полагаться на неосновной модуль.

Так вот что я придумал:

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

Вы можете использовать это так:

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

РЕДАКТИРОВАТЬ: как указано, это на самом деле не возвращает результат или позволяет использовать результат команд в программе Node. Еще одна идея для этого заключается в использовании обратных вызовов LiveScript. http://livescript.net/


Спасибо, но это не ответ, так как ваш код запускает команды последовательно асинхронно
2013 года

Если вам нужно выполнить серию команд синхронно, как в моем примере, это будет работать. Я думаю, что я неправильно понял ваш вопрос, извините. Я добавил еще одну идею в ответ.
Джейсон Ливисей
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.