Как вызвать git, cpp, sh и т. Д. Из скрипта Node.JS
Node.JS - одна из самых мощных платформ для управления ресурсами на нашем компьютере, которая становится все более популярной с тех пор, как была выпущена. Каким бы замечательным он ни был, при всей моей любви и уважении, одного Node.JS недостаточно.
Несмотря на развитую экосистему NPM, существует больше инструментов, которые существуют за ее пределами в течение более длительного времени, поэтому они делают то, что делают лучше, чем любой пакет Node.JS; например opencv - служебная библиотека компьютерного зрения с открытым исходным кодом, разработанная для C ++, Python и Java (не для Node.JS).
Кроме того, Node.JS существует для очень общего назначения, в то время как некоторые инструменты существуют исключительно для одной цели; например, git - который существует для контроля версий.
Соответственно, я решил написать статью о модуле child_process в Node - служебном модуле, который предоставляет вам функции, которые могут создавать и управлять другими процессами.
Как вы, наверное, знаете, в нашей типичной ОС в фоновом режиме работают разные процессы. Каждый процесс управляется одноядерным процессором нашего процессора и будет выполнять серию вычислений каждый раз, когда он установлен. Таким образом, мы не можем в полной мере использовать возможности нашего ЦП с помощью одного процесса, нам потребуется количество процессов, по крайней мере равное количеству ядер в нашем ЦП. Кроме того, каждый процесс может отвечать за выполнение серии вычислений с различной логикой, что даст конечному пользователю лучший контроль над поведением ЦП.
Соответственно, если до этого дня вы писали сценарии Node, которые вообще не связаны с процессами, возможно, вы делали это неправильно, потому что вы ограничивали себя одним ядром, не говоря уже о единый процесс. Модуль узла child_process
существует для решения именно этой проблемы; он предоставит вам служебные функции, которые предоставят вам возможность запускать процессы из основного процесса, в котором вы сейчас находитесь.
Почему этот модуль называется child_process
, а не просто process
? Во-первых, чтобы не путать с экземпляром основного процесса global.process
, а во-вторых, дочерний процесс является производным от основного процесса, что означает, что оба могут взаимодействовать - основной процесс будет удерживать потоки для типов std дочернего процесса, и они оба будут поделиться ipc
каналом (канал «Межпроцессное взаимодействие»; подробнее об этом в этой статье).
API-интерфейс child_process
Модуль child_process
предоставляет нам служебные функции, логика которых накладывается одна на другую. Самая основная функция - spawn()
:
Документы: создать
Функция spawn
порождает новый процесс типа git log
. Первый аргумент функции представляет собой путь к исполняемому файлу, который должен запустить процесс, а второй аргумент - это вектор аргументов, который будет передан исполняемому файлу. Возвращенный объект процесса будет содержать свойство для каждого типа std, представленного как Stream: .stdin
- WriteStream, .stout
- ReadStream и, наконец, .stderr
- ReadStream. Соответственно, если мы хотим запустить git log
через процесс узла и вывести его на консоль, мы сделаем что-то вроде следующего:
Или, если мы воспользуемся преимуществом последнего аргумента options, мы могли бы сделать следующее:
Обратите внимание, что каждая
child_process
функция также будет иметь «синхронизирующую» версию (например,spawnSync()
), но, предполагая, что вы уже знакомы с синхронными и асинхронными функциями в Node, я пропущу это для простоты. .
Следующей функцией в списке будет execFile()
. Как подразумевается, он выполнит заданный путь к файлу, как spawn()
does. Однако разница между 2 заключается в том, что в отличие от spawn()
, который возвращает кучу потоков, execFile()
будет анализировать потоки и возвращать результат непосредственно в виде строки:
Документы: execFile
Вот снимок исходного кода Node, который доказывает, что execFile()
напрямую зависит от spawn()
:
Источник: lib / child_process.js
Поскольку bash
широко используется в качестве оболочки командной строки, Node предоставил нам функцию, которая будет охватывать экземпляр bash
и выполнять заданную командную строку. Эта функция называется exec()
и возвращает стандартный вывод в виде строки, как и execFile()
does:
Документы: exec
Вот снимок исходного кода Node, который доказывает, что exec()
напрямую зависит от execFile()
, что делает его косвенно зависимым от spawn()
Источник: lib / child_process.js
Другими словами, ядро exec()
может быть реализовано так:
Часто мы просто запускали другой процесс Node, который выполнял бы другой файл сценария, поэтому Node предоставил нам функцию, которая привязана к пути исполняемого файла Node и называется fork():
Документы: вилка
Что хорошо в этом методе, так это то, что он открывает канал связи между основным процессом и дочерним процессом (известный как ipc
- Inter Process Communication), поэтому мы можем получать уведомления о статусе дочернего процесса и действовать соответственно:
Источник: lib / child_process.js
А теперь вернемся к тому, что я сказал в начале этой статьи. Каждый процесс использует одно ядро нашего ЦП, следовательно, для того, чтобы наш Node-скрипт мог в полной мере использовать наш ЦП, нам нужно будет запустить несколько экземпляров Node, каждый из которых будет иметь свой собственный процесс. Но как нам управлять работой, распределяемой между ядрами ?! К счастью, ОС делает это за нас, поэтому, вызывая метод fork()
, мы фактически распределяем работу по разным ядрам.
Следуя этому принципу, обычным вариантом использования будет распределение работы скрипта, над которым мы сейчас работаем. Поэтому вместо того, чтобы вызывать метод fork()
с текущим путем к файлу сценария, мы можем просто использовать модуль cluster
, который напрямую связан с child_process
по причине, которую я только что упомянул, и вызвать метод cluster.fork()
:
Документы: кластер
Как вы, наверное, заметили, cluster
API имеет некоторую дополнительную логику в дополнение к обычному process
, но по своей сути это просто еще один процесс, созданный child_process
. Чтобы доказать это, давайте взглянем на снимок, сделанный из исходного кода Node:
Источник: lib / internal / cluster / master.js
Как видите, кластер напрямую зависит от метода fork()
, и если мы посмотрим на реализацию метода fork()
, то увидим, что он напрямую зависит от метода spawn()
:
Источник: lib / child_process.js
Так что в конечном итоге все сводится к spawn()
методу; все, что предоставляет нам node, что связано с процессами, - это просто оболочка вокруг него.
Определенно, предстоит еще кое-что сделать, когда дело доходит до мира процессов в отношении внутреннего устройства Node и вне его по отношению к ОС. Но, прочитав это, вы сможете на практике использовать одну из величайших функций Node и полностью раскрыть ее потенциал. Продолжайте читать документы и исследовать, потому что это определенно может улучшить ваши поддерживаемые навыки, и если у вас есть какие-либо дополнительные вопросы или темы, о которых вы бы хотели, чтобы я писал (в мире JavaScript), сообщите.