Есть случаи при запуске сценария оболочки Python/Ruby/PHP от node.js. В этом посте рассматриваются лучшие практики в использовании child_process.spawn, чтобы инкапсулировать этот вызов в node.js/javascript.
Цель здесь состоит в том, чтобы иметь слой взаимодействия между node.js и внешней оболочкой. Это быстрый обходной путь, если какая -то другая часть вашей системы не разработана в JavaScript.
Мы будем использовать Spawn
над exec
Потому что мы говорим о передаче данных и потенциально больших количествах. Чтобы понять разницу между child_process.spawn
и child_process.exec
(видеть ” Разница между Spawn и Exec of Node.js Child_process “).
Долго и недолго его использования exec
Для небольших объемов данных (до 200 тысяч) с использованием буферного интерфейса и Spawn
Для больших количеств, используя интерфейс потока.
Spawn
Имеет более многословный синтаксис для некоторых из вариантов использования, на которые мы рассмотрим, но он более удобен для интеграции с Ruby/Python/PHP, поскольку мы могли бы получить больше данных, чем пара линий текста.
Полные примеры github.com/hugodf/node-run-python Анкет
Оглавление:
- Вызовите команду оболочки и войдите в нее
- Позвоните Python для его версии
- Вызовите сценарий Python из узла
- Передайте аргументы в сценарий Python от node.js с использованием child_process.spawn
- Читать child_process.spawn вывод из node.js
- Обрабатывать ошибки от child_process.spawn
- Передайте структурированные данные из Python/Ruby в Node.js/JavaScript
Следующие примеры содержат 2 секции.
Часть, которая фактически запускает команду оболочки, обычно функция, называемая запустить
. Они также содержат IIFE («сразу же вызвано выражение функции»), который на самом деле называет его. (async () => {await run ()}) ()
Анкет Этот IIFE – хороший шаблон, включенный Async/wait (см. Но это просто для иллюстрации, так как он представляет призыв к завернутым Spawn Позвоните из другой части вашего приложения.
Вызовите команду оболочки и войдите в нее
Используя Spawn
в этой ситуации излишне, так как Эхо только вернет то, что передано в нее.
Пример довольно эксплуатационный и показывает, как использовать child_process.spawn
чтобы «вылететь» и прочитать эти данные обратно.
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); } })();
$ node run.js foo
Позвоните Python для его версии
Мы будем двигаться довольно быстро, чтобы продемонстрировать, как мы сделаем что -то похожее на вышеперечисленное с Python. Обратите внимание еще раз, как -Версия
передается внутри массива.
Мы также создаем хороший регистратор, чтобы дифференцироваться между Stdout и Stderr и привязан к ним. Поскольку Spawn возвращает экземпляр, который имеет stdout
и Stderr
Эмиттеры событий, мы можем связать наш Logoutput
функция 'Data'
событие с использованием .on ('data', () => {/* Наша функция обратного вызова */})
.
Еще один интересный кусочек – это Python
-
Версия
выводит версию в Stderr
Анкет Несоответствия, связанные с тем, используют ли *NIX -исполнители коды выхода, STDERR и STDOUT при успехе/ошибке – это причуд, которую нам придется иметь в виду при интеграции Python/Ruby/Other с 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); } })(); Output:
штрих $ node run.js
[Stderr] Python 2.7.13
## Call a Python script from Node We'll now run a fully-fledged Python script (although it could just as well be Ruby, PHP, shell etc.) from Node.js. This is `script.py`, it just logs out `argv` (the "argument vector", ie. `['path/to/executable', /* command line arguments]`)
PY -импорт Sys
Печать (sys.argv)
Like in the previous example, we'll just call spawn with `python` with the path to the Python script (`./script.py`) in the second parameter. Here comes another gotcha of integrating scripts in this fashion. In this example, the path to the script is based on the working directory from which `node` is called. There are workaround of course using the `path` module and `__dirname`, which for example could resolve a `other-script.py` co-located with the JavaScript file/Node module calling `spawn` using: `require('path').resolve(__dirname, './other-script.py')`.
JS const {spawn (‘child_process’)
const logoutput = (name) => (data) => console.log ( [$ {name}] $ {data.tostring ()}
)
function run () {const (‘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);}}) ();
Output:
штрих
$ node run.js
[stdout] [‘./script.py’]
## Pass arguments to a Python script from Node.js using child\_process.spawn The next step of integration is to be able to pass data from the Node/JavaScript code to the Pytonh script. In order to do this, we'll just passed more shell arguments using the arguments array (second parameter to `spawn`).
JS const {spawn (‘child_process’)
const logoutput = (name) => (data) => console.log ( [$ {name}] $ {data.tostring ()}
)
function run () {const (‘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);}}) ();
Our `script.py` will also just log out the `argv` except the first element (which is the path to the script).
PY -импорт Sys
Печать (sys.argv) [1:]
Here's the output:
штрих $ node run.js
[stdout] [‘my’, ‘args’]
## Read child\_process.spawn output from Node.js It's nice to be able to pass data down to the Python script, but we're still not able to get the data from the Python script back in a format that we're able to leverage in our Node.js/JavaScript application. The solution to this is to wrap the whole `spawn` -calling function into a Promise. This allows us to decide when we want to `resolve` or `reject`. To keep track of the Python script's output stream(s), we manually buffer the output using arrays (one for `stdout` and another for `stderr`). We also add a listener for `'exit'` using `spawn().on('exit', (code, signal) => { /* probably call resolve() */ })`. This is where we will tend to `resolve`/`reject` the Promise's value(s) from the Python/Ruby/other script.
JS const {spawn (‘child_process’)
const logoutput = (name) => (data) => console.log ( [$ {name}] $ {data}
)
Функция run () {вернуть новое обещание ((Resolve, dize) = > {const (‘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 run () logoutput (‘main’) (output) process.exit (0)} catch (e) {console.error (e.stack); process.exit (1); }}) ();
Output:
$ node run.js
[stdout] [‘my’, ‘args’] [main] [‘my’, ‘args’]
## Handle errors from child\_process.spawn Next up we need to handle errors from the Python/Ruby/shell script at the Node.js/JavaScript level. The main way that a \*NIX executable signals that it errored is by using a `1` exit code. That's why the `.on('exit'` handler now does a check against `code === 0` before deciding whether to resolve or reject with value(s).
JS const {spawn (‘child_process’)
const logoutput = (name) => (data) => console.log ( [$ {name}] $ {data}
)
Функция run () {вернуть новое обещание ((Resolve, dize) = > {const (‘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 run () logoutput (‘main’) (output) process.exit (0)} catch (e) {console.error (‘ошибка во время выполнения скрипта’, e.stack); process.exit (1);}}) ();
Output:
штрих $ node run.js
[stderr] Traceback (последний вызов последний): файл “./script.py”, строка 3, в печатном (sy.argv) [1:] Nameerror: имя ‘sy’ не определено
Ошибка во время ошибки выполнения скрипта: Traceback (самый последний вызов последний): файл “./script.py”, строка 3, в печатном (sy.argv) [1:] Nameerror: имя ‘sy’ не определено
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)
## Pass structured data from Python/Ruby to Node.js/JavaScript The final step to full integration between Ruby/Python/PHP/shell scripts and our Node.js/JavaScript application layer is to be able to pass structured data back from the script up to Node.js/JavaScript. The simplest structured data format that tends to be available in both Python/Ruby/PHP and Node.js/JavaScript is JSON. In the Python script, we print the `json.dumps()` output of a dictionary, see `script.py`:
Py Import Sys import json
send_message_back = {‘аргументы’: sys.argv [1:], ‘message’: “” “Привет, это мое сообщение.
К миру””” }
print (json.dumps (send_message_back))
In Node, we add some JSON-parsing logic (using `JSON.parse`) in the `'exit'` handler. A gotcha at this point is if, for example `JSON.parse()` fails due to badly-formed JSON, we need to propagate that error up, hence the try/catch where the `catch` clause `reject`-s the potential error: `try { resolve(JSON.parse(out[0])) } catch(e) { reject(e) }`.
JS const {spawn (‘child_process’)
const logoutput = (name) => (message) => console.log ( [$ {name}] $ {message}
)
Функция run () {вернуть новое обещание ((Resolve, dize) = > {const (‘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 run () logoutput (‘main’) (output.message) process.exit (0)} catch (e) {console.error (‘ошибка во время выполнения скрипта’, e.stack ); process.exit (1);}}) ();
Output
$ node run.js
[stdout] {“Сообщение”: “Привет, \ Это мое сообщение. \ и миру”, “Аргументы”: [“my”, “args”]}
[Главный] Здравствуйте, это мое сообщение.
К миру
I've got mentoring spots open at [https://mentorcruise.com/mentor/HugoDiFrancesco/](https://mentorcruise.com/mentor/HugoDiFrancesco/), so do that if you want Node.js/JavaScript/career mentoring or feel free to tweet at me [@hugo\_\_df](https://twitter.com/hugo__df) [Elaine Casap](https://unsplash.com/@ecasap?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge "Download free do whatever you want high-resolution photos from Elaine Casap")
Оригинал: “https://dev.to/hugo__df/the-comprehensive-guide-to-integrating-a-pythonrubyphpshell-script-with-nodejs-using-childprocessspawn-524e”