Как вызвать 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), сообщите.