2015-07-28 3 views
1

В других программах, которые я написал, мне понравились асинхронные аспекты node.js, используя promises.Node.js для асинхронных Linux-скриптов

Я хотел бы использовать этот же стиль программирования (используя node.js) для сценариев Linux. Другими словами, я хотел бы одновременно выполнять несколько команд Linux, а затем после завершения этих команд я хочу, чтобы скрипт node.js затем выполнял другую группировку команд асинхронно и так далее (без блокировки).

Я столкнулся с an aritlce, который показывает, как выполнять синхронную команду Linux с помощью node.js, но мне еще предстоит найти аналогичный учебник, который охватывает управление несколькими асинхронными командами Linux с помощью node.js.

Возможно ли это? Если да, можете ли вы направить меня на некоторые конкретные ресурсы, которые могли бы помочь мне начать с этой цели?

+0

Хотя я проголосовал за все ответы, я все еще изучаю их, и сегодня я выберу один из них. Спасибо всем! –

+0

Этих ответов настолько хорошо, я буду чувствовать себя виноватыми, когда я выбираю только один из них :( –

ответ

8

Я не уверен, что я прав, но я думаю, что вы ищете exec и spawn. См. related API documentation. В документации есть примеры для обеих команд.

exec и spawn

exec является "простой" вариант spawn. Первый использует один обратный вызов для отправки отчета пользователю, когда команда завершена, и только когда она полностью завершена/не выполнена.

child = exec('cat *.js bad_file | wc -l', 
    function (error, stdout, stderr) { 
    console.log('stdout: ' + stdout); 
    console.log('stderr: ' + stderr); 
    if (error !== null) { 
     console.log('exec error: ' + error); 
    } 
}); 

Поэтому в основном поставляется обратного вызова вызывается только, когда все, что было написано на стандартный вывод/STDERR доступен, полностью. После завершения процесса (с успехом или неудачей) только тогда вызывается обратный вызов, и пользователи (вы) могут воздействовать на него. Если это не удалось error является правдой.

spawn отличается, потому что вы можете слушать на stdout/stderr события. Сначала вы «запускаете» процесс. Функция spawn возвращает ссылку на дочерний процесс.

var spawn = require('child_process').spawn; 
var ls = spawn('ls', ['-lh', '/usr']); 

ls вот детский процесс, который вы породили. Он имеет два свойства (важно сейчас), stdout и stderr, которые являются излучателями событий. Они испускают событие data. Когда материал написан на обоих потоках, вызываются обратные вызовы, зарегистрированные в событии data.

ls.stdout.on('data', function (data) { 
    console.log('stdout: ' + data); 
}); 

ls.stderr.on('data', function (data) { 
    console.log('stderr: ' + data); 
}); 

Есть и другие важные события, конечно (проверьте документацию на самую актуальную и соответствующую информацию, конечно).

ls.on('close', function (code) { 
    console.log('child process exited with code ' + code); 
}); 

Вы бы использовать spawn, когда вы хотите, чтобы захватить вещи на стандартный вывод, например, в то время как процесс запущен.Хорошим примером может быть, если вы создавали задачу кодирования ffmpeg, для чего требуется несколько минут. Вы можете прослушивать stderr (потому что ffmpeg записывает информацию о ходе в stderr вместо stdout), чтобы разобрать информацию о «прогрессе».

Идя линия за линией с carrier

Существует хорошая дополнительная библиотека вы можете использовать вместе с spawn. It's called carrier. Это помогает читать «строки» из stdout/stderr порожденных процессов. Это полезно, потому что параметр data, переданный обратным вызовам, не обязательно содержит «полные» строки, разделенные \n. carrier помогает с этим. (Однако это не поможет вам зафиксировать прогресс ffmpeg на stderr, потому что в этом случае нет новых строк, написанных ffmpeg, это только возврат каретки, строка всегда переписывается в основном.)

Вы бы использовали его как этого

var carry = require("carrier").carry; 
var child = spawn("command"); 
carry(child.stdout, function(line) { 
    console.log("stdout", line); 
}); 

обещания и Deferreds

Если вы хотели бы использовать обещание/отложенный подход стиль, то вы могли бы сделать что-то вроде следующего using Q, который используется AngularJS - или по крайней мере что-то очень (см. ссылку для полного учебника по обещаниям).

spawn возвращает Emitter объект, который не является обещанием. Таким образом, вы должны обернуть вызов на икру (see Using Deferreds).

var q = require("q"); 
var spawn = require("child_process").spawn; 

var ls = function() { 
    var deferred = q.defer(); 
    var ls = spawn("ls", ["-lh", "/usr"]); 
    ls.stdout.on("data", function(data) { 
    deferred.notify({stdout: true, data: data}); 
    }); 
    ls.stderr.on("data", function(data) { 
    deferred.notify({stderr: true, data: data}); 
    }); 
    ls.on("close", function(code) { 
    if (code === 0) { 
     deferred.resolve(); 
    } else { 
     deferred.reject(code); 
    } 
    }); 
    return deferred.promise; 
}; 

ls() Выполнив теперь обещание возвращается, который вы будете использовать, как и любое другое обещание. Когда он будет полностью разрешен, вызывается первый обратный вызов. Если возникает ошибка (процесс существует с кодом без нуля), вызывается обработчик ошибок. Пока команда прогрессирует, будет вызываться третий обратный вызов (уведомить обратный вызов).

ls().then(function() { 
    console.log("child process exited successfully"); 
}, function(err) { 
    console.log("child process exited with code " + err); 
}, function(args) { 
    if (args.stdout) { 
    console.log("stdout: " + args.data); 
    } else { 
    console.log("stderr: " + args.data); 
    } 
}); 

Когда что-то записывается в stderr, вы можете сразу же отклонить отклонение, то есть конструктивное решение. Возвращаясь к примеру ffmpeg, это не принесет вам пользы, потому что ffmpeg сплевывает общую информацию в stderr. Однако он может работать с другими командами.

Я думаю, что вы получите его :)

Примеры взяты из документации nodejs, так как от них хорошо известны.

+0

Я очень ценю, как ваш ответ также рассматривается мониторинг запущенных процессов (излучатели событий). –

+0

Ваш ответ настолько полезен, что он учит меня многим вещам (мне нужно знать), но забыл спросить. Как бы вы включили в этот ответ способ обетования-не-блокирующий-контроль-поток, output? –

+1

@LonnieBest См. мой обновленный ответ, вот как я буду использовать интерфейс «обещание» в стиле обещания. [Это то, как AngularJS использует его.] (https://docs.angularjs.org/api/ng/service/$ ! д) –

2

В библиотеках основных узлов синхронные функции имеют значение Sync, добавленное к имени функции. Они используют child_process.execFileSync, поэтому вы должны искать child_process.execFile для асинхронной версии функции.

+0

Спасибо за подчеркивая эту модель именования. –

2

Я рекомендую подход, объединяющий генераторы и обещания. Вот предпосылки:

  • Узел 0,11 или выше, с --harmony флагом, или любая версия io.js (без флагов, необходимых).
  • Генератор обработчик. Это мой любимый: https://www.npmjs.com/package/co
  • Способ преобразования методов обратного вызова узла в функции возврата обещаний.Я использую это один: https://www.npmjs.com/package/ugly-adapter

Затем сделать то, что вы хотите:

var co = require('co'); 
var adapt = require('ugly-adapter'); 
var childProcessExec = require('child_process').exec; 
var exec = adapt.part(childProcessExec); 

co(function*() { 

    // run commands in parallel by yielding 
    // a single object or array 
    var result = yield { 
    listing: exec('ls -l'), 
    mkdirResult: exec('mkdir foo'), 
    blah: exec('echo "blah blah"'), 
    }; 

    console.log(result.blah); // "blah blah" 

    // run commands in series by yielding 
    // one thing at a time 
    var listing = yield exec('ls -l'); 
    var mkdirResult = yield exec('mkdir foo2'); 
    var blah = yield exec('echo "blah blah"'); 

    console.log(blah); // "blah blah" 

}).catch(function(err){ 

    // this handles any possible errors 
    // thrown by the above 
    console.error(err.stack); 
    process.exit(1); 
}); 

yield ключевое слово вызывает функцию, чтобы сделать паузу, в то время как co разворачивает обещание и отправляет результат обратно в свой функция. Примечание:. Библиотека co - это действительно временный стенд для async/await, который входит в es7 и работает по существу таким же образом.

+0

Я изучаю ваш ответ прямо сейчас. Кажется здорово! У меня проблема с этой строкой: var exec = adapt.part (child_process.exec); Я получаю: child_process не определен (я использую node.js 0.12.0). –

+0

Я отредактировал ваш ответ немного, чтобы он работал без ошибок (для меня). –

+1

Woops, да, забыл потребовать этот модуль! – greim