Вступление
Системные администраторы и разработчики часто обращаются к автоматизации, чтобы уменьшить свою рабочую нагрузку и улучшить свои процессы. При работе с серверами автоматизированные задачи часто выполняются с помощью сценариев оболочки. Однако разработчик может предпочесть использовать более общий язык более высокого уровня для сложных задач. Многим приложениям также необходимо взаимодействовать с файловой системой и другими компонентами уровня ОС, что часто проще сделать с помощью утилит уровня командной строки.
С помощью Node.js мы можем запускать команды оболочки и обрабатывать их входные и выходные данные с помощью JavaScript. Следовательно, мы можем написать большинство этих сложных операций на JavaScript, а не на языке сценариев оболочки, что потенциально упрощает поддержку программы.
child_process
различные способы выполнения команд оболочки в Node.js с
помощью модуля child_process.
Модуль child_proccess
Node.js выполняет свой основной цикл обработки событий в одном потоке. Однако это не означает, что вся его обработка выполняется в одном потоке. Асинхронные задачи в Node.js выполняются в других внутренних потоках. Когда они завершены, код обратного вызова или ошибка возвращается в основной, единственный поток.
Эти различные потоки выполняются в одном и том же процессе Node.js. Однако иногда желательно создать другой процесс для выполнения кода. Когда создается новый процесс, операционная система определяет, какой процессор он использует и как планировать свои задачи.
Модуль child_process
создает новые дочерние процессы нашего основного
процесса Node.js. Мы можем выполнять команды оболочки с этими дочерними
процессами.
Использование внешних процессов может улучшить производительность вашего приложения при правильном использовании. Например, если функция приложения Node.js интенсивно использует ЦП, поскольку Node.js является однопоточным, он блокирует выполнение других задач во время его работы.
Однако мы можем делегировать этот ресурсоемкий код дочернему процессу, скажем, очень эффективной программе на C ++. Затем наш код Node.js выполнит эту программу на C ++ в новом процессе, не блокируя другие ее действия, а после завершения обработает ее вывод.
Для выполнения команд оболочки мы будем использовать две функции: exec
и spawn
.
Функция exec
Функция exec()
создает новую оболочку и выполняет заданную команду.
Выходные данные выполнения буферизуются, то есть хранятся в памяти и
доступны для использования в обратном вызове.
Давайте воспользуемся exec()
чтобы вывести список всех папок и файлов
в нашем текущем каталоге. В новом файле Node.js с именем lsExec.js
напишите следующий код:
const { exec } = require("child_process");
exec("ls -la", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
Во-первых, нам нужен child_process
в нашей программе, в частности, с
помощью функции exec()
(через деструктуризацию ES6). Затем мы вызываем
exec()
с двумя параметрами:
- Строка с командой оболочки, которую мы хотим выполнить.
- Функция обратного вызова с тремя параметрами:
error
,stdout
,stderr
.
Команда оболочки, которую мы запускаем, называется ls -la
, которая
должна выводить список всех файлов и папок в нашем текущем каталоге
построчно, включая скрытые файлы / папки. Функция обратного вызова
регистрирует, получили ли мы error
при попытке выполнить команду или
вывод в потоках stdout
или stderr
Примечание. Объект
error
отличается отstderr
. Объектerror
не равен нулю, когдаchild_process
не удается выполнить команду. Это может произойти, если вы попытаетесь выполнить другой скрипт Node.js вexec()
но файл не может быть найден, например. С другой стороны, если команда успешно выполняется и записывает сообщение в стандартный поток ошибок, тоstderr
не будет нулевым.
Если вы запустите этот файл Node.js, вы должны увидеть результат, подобный следующему:
$ node lsExec.js
stdout: total 0
[email protected] 9 arpan arpan 0 Dec 7 00:14 .
[email protected] 4 arpan arpan 0 Dec 7 22:09 ..
[email protected] 1 arpan arpan 0 Dec 7 15:10 lsExec.js
child process exited with code 0
Теперь, когда мы поняли, как запускать команды с помощью exec()
,
давайте изучим другой способ выполнения команд с помощью spawn()
.
Функция возрождения
Функция spawn()
выполняет команду в новом процессе . Эта функция
использует Stream API , поэтому ее
вывод команды доступен через слушателей.
Как и раньше, мы будем использовать spawn()
для вывода списка всех
папок и файлов в нашем текущем каталоге. Давайте создадим новый файл
Node.js, lsSpawn.js
, и введем следующее:
const { spawn } = require("child_process");
const ls = spawn("ls", ["-la"]);
ls.stdout.on("data", data => {
console.log(`stdout: ${data}`);
});
ls.stderr.on("data", data => {
console.log(`stderr: ${data}`);
});
ls.on('error', (error) => {
console.log(`error: ${error.message}`);
});
ls.on("close", code => {
console.log(`child process exited with code ${code}`);
});
Начнем с того, что потребуем функцию spawn()
из модуля child_process
Затем мы создаем новый процесс, который выполняет команду ls
,
передавая -la
в качестве аргумента. Обратите внимание, как аргументы
хранятся в массиве и не включаются в командную строку.
Затем мы настраиваем наших слушателей. Объект stdout
ls
запускает
data
когда команда записывает в этот поток. Точно так же stderr
также запускает data
когда команда записывает в этот поток.
Ошибки выявляются путем прослушивания их непосредственно на объекте, в
котором хранится ссылка на команду. Вы получите сообщение об ошибке
только в том случае, если child_process
не сможет выполнить команду.
close
происходит, когда команда завершена.
Если мы запустим этот файл Node.js, мы должны получить вывод, как и
раньше, с помощью exec()
:
$ node lsSpawn.js
stdout: total 0
[email protected] 9 arpan arpan 0 Dec 7 00:14 .
[email protected] 4 arpan arpan 0 Dec 7 22:09 ..
[email protected] 1 arpan arpan 0 Dec 7 15:10 lsExec.js
[email protected] 1 arpan arpan 0 Dec 7 15:40 lsSpawn.js
child process exited with code 0
Когда использовать exec и spawn?
Ключевое различие между exec()
и spawn()
заключается в том, как они
возвращают данные. Поскольку exec()
сохраняет весь вывод в буфере, он
требует больше памяти, чем spawn()
, которая передает вывод по мере
его поступления.
Как правило, если вы не ожидаете возврата больших объемов данных, для
простоты exec()
Хорошими примерами использования являются создание
папки или получение статуса файла. Однако, если вы ожидаете большой
объем вывода от вашей команды, вам следует использовать spawn()
.
Хорошим примером будет использование команды для управления двоичными
данными с последующей загрузкой их в вашу программу Node.js.
Заключение
Node.js может запускать команды оболочки с помощью стандартного модуля
child_process
Если мы используем exec()
, наша команда будет
запущена, и ее вывод будет доступен нам в обратном вызове. Если мы
используем spawn()
, его вывод будет доступен через прослушиватели
событий.
Если наше приложение ожидает много вывода от наших команд, мы должны
предпочесть spawn()
exec()
. Если нет, мы могли бы использовать
exec()
для простоты.
Теперь, когда вы можете запускать задачи, внешние по отношению к Node.js, какие приложения вы бы создали?