node.js виконує системну команду синхронно


171

Мені потрібна функція node.js

result = execSync('node -v');

що синхронно виконає заданий командний рядок і поверне всі stdout'ed цим командним текстом.

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

ОНОВЛЕННЯ

Тепер у нас є рішення mgutz, яке дає нам вихідний код, але не stdout! Ще чекаю точнішої відповіді.

ОНОВЛЕННЯ

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

[EDIT Червень 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: Я думаю, що асинхронний варіант був би кращим. (Відповідь Іво )
pvorb

@pvorb - так, за винятком випадків, коли ви не можете використовувати async :)
Марк Кан

1
Є поважні причини не використовувати молоток для асинхронізації для кожного цвяха. Наприклад, двигуни шаблонів асинхронізовані в Express 3, а допоміжні функції (місцеві жителі) повинні бути синхронні. Що робити, якщо ці допоміжні функції повинні збирати Менш файлів асинхронно на ходу?
mgutz

8
Цікаво, чому це просто execSyncне є частиною child_process. Я думаю, так і має бути.
Michael Härtl

31

Використовуйте модуль ShellJS .

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

Приклад:

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

2
Зауважте, що під час написання документи документи зазначають, що синхронний exec()процесор інтенсивний для тривалих процесів.
Арам Кочарян

1
параметри, {silent: true} є ключовим
Nick

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 - tisk tisk ... хіба ми не навчилися ніколи говорити щось неможливо? ;) дивіться моє рішення нижче.
Маркус Папа

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

Асинхронізація - це добре, але якщо 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 . Не соромтеся робити запити на виклик або надсилати проблеми!

EDIT: Однак він ще не повертає stdout ... Просто виводить їх у режимі реального часу. Це робить і зараз. Ну, я щойно випустив його сьогодні. Можливо, ми можемо на цьому побудувати.

У всякому разі, я думав, що це варто згадати.


Це саме те, що я шукав. Дякуємо, що вклали час, щоб це зробити.
thealfreds

Єдине, що я не з'ясував і не реалізував - це конфігурація збірки для різних версій nodejs. Я думаю, що я написав це на вузлі 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ), поки це npm installвдається (іншими словами, як плагін збирається), тоді добре піти на виробництво. Окрім націлювання на різні версії nodejs, я б сказав, що він виправлений у виробництво, або що він досить стабільний. Якщо є якісь помилки, ви можете надіслати запит на виклик або опублікувати проблему на github :)
Логан

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

EDIT - цей приклад призначений для середовищ Windows, якщо необхідно, відрегулюйте для власних потреб Linux


Так, і так швидко, як ваш процесор може викликати ... Але ей, коли вам "потрібно" зробити щось зло, Сатана ваш чоловік прав?
Маркус Папа

ок, це чисте зло, але приголомшливо. Мені це було потрібно для обробки події файлової системи browserify.on ('register'), у якої не було зворотного дзвінка. Врятував мій день!
Роберт Гулд

Ви можете пояснити, чому це працює на JavaScript? не повинно цикл while виконуватись нескінченно на тому самому галочці, тоді як виконавець виконує наступний?
badunk

Максимальне збільшення процесора зайнятим очікуванням - це поганий дизайн.
Луї

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

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

EDIT: як зазначалося, це насправді не повертає висновок або не дозволяє використовувати результат команд у програмі Node. Ще одна ідея для цього - використовувати зворотні дзвінки LiveScript. http://livescript.net/


Дякую, але це не відповідь, оскільки ваш код виконує команди послідовно асинхронно
оскаржено

Якщо вам потрібно виконати серію команд синхронно, як на моєму прикладі, вона буде працювати. Я думаю, що я неправильно зрозумів ваше запитання, вибачте. Я додав ще одну ідею до відповіді.
Jason Livesay
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.