Рубрики
Без рубрики

Комплексное руководство по интеграции сценария Python/Ruby/Php/Shell с node.js с использованием child_process.spawn

Есть случаи при запуске сценария оболочки Python/Ruby/PHP от node.js. Этот пост … с меткой узла, JavaScript, Python, WebDev.

Есть случаи при запуске сценария оболочки 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)

[unsplash-logo
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”