Автор оригинала: Hugo Di Francesco.
При запуске скрипта оболочки Python/Ruby/PHP есть случаи случаев. Этот пост смотрит на лучшие практики вокруг используя Child_Process.spawn, чтобы инкапсулировать этот вызов в Node.js/javascript.
Целью здесь состоит в том, чтобы иметь слой взаимодействия между Node.js и внешней оболочкой. Это быстрый обходной путь, если какая-то другая часть вашей системы не разработана в JavaScript.
Мы будем использовать Спон
над exec
Потому что мы говорим о передаче данных и потенциально больших количествах. Чтобы понять разницу между child_process.spawn
и child_process.exec
См. « Разница между порождением и Exec of Node.js Child_Process ».
Длинный и короткий из него используется exec
Для небольших объемов данных (до 200 тыс.) Использование интерфейса буфера и Спон
Для большего количества с использованием интерфейса потока.
Спон
Имеет более многословный синтаксис для некоторых случаев использования, на которые мы посмотрим. Это более обслуживается для интеграции с Ruby/Python/PhP, поскольку мы можем получить больше данных, чем пару строк текста.
Полные примеры github.com/hugodf/node-run-python Отказ
Следующие примеры содержат 2 раздела:
- Часть, которая на самом деле запускает команду оболочки, обычно вызывается функция
Беги
, а также - IIFE («Сразу вызывала выражение функции»), которая на самом деле называет это,
(async () => {a ждать Run ()}
) (). Эта IIFE – хороший узор, включенный в async/await (s ee async js: история, шаблоны и gotc есть), но это просто там для иллюстрации, так как он представляет собой призыв к WRAPPЭд СП
AWN звоните из другой части вашего приложения.
Позвоните в команду оболочки и войдите в систему
Использование Спон
В этой ситуации наполнен, так как эхо будет возвращаться только то, что передано ему.
Пример довольно объяснительный и показывает, как использовать child_process.spawn
«Оболовать» и прочитать эти данные обратно.
Спон
Принимает исполняемый файл для вызова первого параметра и, необязательно, массив параметров/параметров для исполняемого файла в качестве второго параметра.
const { spawn } = require('child_process'); function run() { const process = spawn('echo', ['foo']); process.stdout.on( 'data', (data) => console.log(data.toString()) ); } (() => { try { run() // process.exit(0) } catch (e) { console.error(e.stack); process.exit(1); } })();
Позвоните Python для его версии
Мы будем двигаться довольно быстро, чтобы продемонстрировать, как мы сделаем что-то похожее на вышеизложенное с Python. Обратите внимание, как --version
передается внутри массива.
Мы также создаем хороший регистратор для дифференцировки между Stdout и STDERR и связывают с ними. Так как порождение возвращает экземпляр, который имеет stdout
и Стдерр
Эмиттеры событий, мы можем связать наше logutput
Функция на «Данные»
событие, использующее .on («Данные», () => {/* Наша функция обратного вызова */
}).
Еще одним интересным тидбитом является то, что Python
--version
Выводит версию к Стдерр
Отказ Несоответствия вокруг того, используют ли исполняемые файлы NIX NIX, используют выходные коды, STDERR и STDOUT на успех/ошибках – это крик, который нам придется иметь в виду, одновременно интегрируя Python/Ruby/другой с Node.js.
const { spawn } = require('child_process') const logOutput = (name) => (data) => console.log(`[${name}] ${data.toString()}`) function run() { const process = spawn('python', ['--version']); process.stdout.on( 'data', logOutput('stdout') ); process.stderr.on( 'data', logOutput('stderr') ); } (() => { try { run() process.exit(0) } catch (e) { console.error(e.stack); process.exit(1); } })();
Выход:
$ node run.js
[stderr] Python 2.7.13
Позвоните в сценарий Python из узла
Теперь мы будем запустить полноценный сценарий Python (хотя он может также быть Ruby, PHP, Shell и т. Д.) От Node.js.
Это Script.py
это просто выходит из системы Аргв
(«Вектор аргумента», т. Е. [«Путь/к/исполняемому»,/* Аргументы командной строки]
)
import sys print(sys.argv)
Как и в предыдущем примере, мы просто позвоним с помощью Python
С пути к сценарию Python ( ./script.py
) во втором параметре.
Вот еще один Gotcha интеграции сценариев в этой моде. В этом примере путь к сценарию основан на рабочем каталоге, из которого Узел
называется.
Конечно, используется обходной путь, используя путь
модуль и __dirname
, что, например, мог разрешить Другой-script.py
Коо-находки с помощью модуля файла/узла JavaScript Calling Спон
Использование: требуют («путь»). Разрешить (__ dirname, './OTHER-Script.py')
Отказ
const { spawn } = require('child_process') const logOutput = (name) => (data) => console.log(`[${name}] ${data.toString()}`) function run() { const process = spawn('python', ['./script.py']); process.stdout.on( 'data', logOutput('stdout') ); process.stderr.on( 'data', logOutput('stderr') ); } (() => { try { run() // process.exit(0) } catch (e) { console.error(e.stack); process.exit(1); } })();
Выход:
node run.js \[stdout\] ['./script.py']
Передать аргументы к сценарию Python от Node.js с помощью Child_Process.spawn
Следующим этапом интеграции является возможность передавать данные из узла/JavaScript Code к сценарию Python.
Чтобы сделать это, мы просто передам больше аргументов Shell, используя аргументы Array (второй параметр для Spawn
).
const { spawn } = require('child_process') const logOutput = (name) => (data) => console.log(`[${name}] ${data.toString()}`) function run() { const process = spawn('python', ['./script.py', 'my', 'args']); process.stdout.on( 'data', logOutput('stdout') ); process.stderr.on( 'data', logOutput('stderr') ); } (() => { try { run() // process.exit(0) } catch (e) { console.error(e.stack); process.exit(1); } })();
Наше Script.py
Также просто выйдет из-за Аргв
За исключением первого элемента (который является путь к скрипту).
import sys print(sys.argv)[1:]
Вот вывод:
node run.js \[stdout\] ['my', 'args']
Читайте child_process.spawn вывод из node.js
Приятно иметь возможность передавать данные в сценарий Python. Мы все еще не можем получить данные из сценария Python обратно в формате, который мы можем использовать в нашем приложении Node.js/JavaScript.
Решение этого – обернуть целое Спон
-Каллирующая функция в обещание. Это позволяет нам решать, когда мы хотим решить
или Отклонить
Отказ
Чтобы отслеживать выходные потоки (ы) сценария Python, мы вручную буксируем вывод, используя массивы (один для stdout
и другой для STDERR
).
Мы также добавляем слушатель на «Выход»
Использование Спос (). Вкл. («Выход», (код, сигнал) => {/*, вероятно, разрешение вызова () */
}). Это где мы будем иметь в себе до Reso
л VE/REJ
ECT Value (ы) обещания от Python/Ruby/другой скрипт.
const { spawn } = require('child_process') const logOutput = (name) => (data) => console.log(`[${name}] ${data}`) function run() { return new Promise((resolve, reject) => { const process = spawn('python', ['./script.py', 'my', 'args']); const out = [] process.stdout.on( 'data', (data) => { out.push(data.toString()); logOutput('stdout')(data); } ); const err = [] process.stderr.on( 'data', (data) => { err.push(data.toString()); logOutput('stderr')(data); } ); process.on('exit', (code, signal) => { logOutput('exit')(`${code} (${signal})`) resolve(out); }); }); } (async () => { try { const output = await run() logOutput('main')(output) process.exit(0) } catch (e) { console.error(e.stack); process.exit(1); } })();
Выход:
node run.js \[stdout\] ['my', 'args'] \[main\] ['my', 'args']
Обрабатывать ошибки от child_process.spawn
Следующим UP Нам нужно обрабатывать ошибки из сценария Python/Ruby/Shell на уровне Node.js/JavaScript.
Основной способ, что исполняемые файлы * NIX, которые оно ошибается с использованием 1
Выходной код. Вот почему .on («Выход»
Обработчик теперь делает проверку на фоне Код
Прежде чем решить, разрешать ли или отклонить со значением (ыми).
const { spawn } = require('child_process') const logOutput = (name) => (data) => console.log(`[${name}] ${data}`) function run() { return new Promise((resolve, reject) => { const process = spawn('python', ['./script.py', 'my', 'args']); const out = [] process.stdout.on( 'data', (data) => { out.push(data.toString()); logOutput('stdout')(data); } ); const err = [] process.stderr.on( 'data', (data) => { err.push(data.toString()); logOutput('stderr')(data); } ); process.on('exit', (code, signal) => { logOutput('exit')(`${code} (${signal})`) if (code === 0) { resolve(out); } else { reject(new Error(err.join('\n'))) } }); }); } (async () => { try { const output = await run() logOutput('main')(output) process.exit(0) } catch (e) { console.error('Error during script execution ', e.stack); process.exit(1); } })();
Выход:
node run.js [stderr] Traceback (most recent call last): File "./script.py", line 3, inprint(sy.argv)[1:] NameError: name 'sy' is not defined Error during script execution Error: Traceback (most recent call last): File "./script.py", line 3, in print(sy.argv)[1:] NameError: name 'sy' is not defined at ChildProcess.process.on (/app/run.js:33:16) at ChildProcess.emit (events.js:182:13) at Process.ChildProcess._handle.onexit (internal/child_process.js:240:12)
Передать структурированные данные из Python/Ruby на Node.js/JavaScript
Последний шаг к полной интеграции между скриптами RUBY/PYTHON/PHP/SHOOLL и нашим уровнем приложений Node.js/JavaScript должен иметь возможность передавать структурированные данные обратно из сценария до Node.js/javascript.
Самый простой структурированный формат данных, который имеет тенденцию быть доступен в Python/Ruby/PhP и Node.js/JavaScript, это JSON.
В сценарии Python мы печатаем json.dumps ()
Вывод словаря, см. Script.py
:
import sys import json send_message_back = { 'arguments': sys.argv[1:], 'message': """Hello, This is my message. To the world""" } print(json.dumps(send_message_back))
В узле мы добавляем какую-нибудь логику сбора JSON (используя json.parse
) в «Выход»
обработчик.
A gotcha на данный момент, если, например Json.parse ()
Не удается из-за плохо сформированного JSON, нам нужно распространить эту ошибку. Следовательно, попробуйте/поймать, где поймать
пункт Отклонить
-С потенциальная ошибка: попробуйте (json.parse (out [0]))} catch (e) {reject (e)}
Отказ
const { spawn } = require('child_process') const logOutput = (name) => (message) => console.log(`[${name}] ${message}`) function run() { return new Promise((resolve, reject) => { const process = spawn('python', ['./script.py', 'my', 'args']); const out = [] process.stdout.on( 'data', (data) => { out.push(data.toString()); logOutput('stdout')(data); } ); const err = [] process.stderr.on( 'data', (data) => { err.push(data.toString()); logOutput('stderr')(data); } ); process.on('exit', (code, signal) => { logOutput('exit')(`${code} (${signal})`) if (code !== 0) { reject(new Error(err.join('\n'))) return } try { resolve(JSON.parse(out[0])); } catch(e) { reject(e); } }); }); } (async () => { try { const output = await run() logOutput('main')(output.message) process.exit(0) } catch (e) { console.error('Error during script execution ', e.stack); process.exit(1); } })();
Выход:
node run.js [stdout] {"message": "Hello,\nThis is my message.\n\nTo the world", "arguments": ["my", "args"]} [main] Hello, This is my message. To the world
Это оно! Спасибо за прочтение:)
У меня открыты пятна наставничества на https://mentorcruise.com/dentor/hugodifrancesco/ Отказ Сделайте это, если вы хотите Node.js/javaScript/карьеру наставничества или не стесняйтесь твитнуть на меня @hugo__df.
И читать больше моих статей на codewithhugo.com.