HomeLập trìnhPhpCách tích hợp...

Cách tích hợp tập lệnh shell Python/Ruby/PHP với Node.js bằng cách sử dụng child_ process.spawn


Đôi khi cần phải chạy tập lệnh trình bao Python/Ruby/PHP từ Node.js. Bài đăng này xem xét các phương pháp hay nhất xung quanh việc tận dụng child_ process.spawn để đóng gói lệnh gọi này trong Node.js/JavaScript.

Mục tiêu ở đây là có một lớp khả năng tương tác giữa Node.js và lớp vỏ bên ngoài. Đây là một giải pháp thay thế nhanh nếu một số phần khác trong hệ thống của bạn không được phát triển bằng JavaScript.

chúng tôi sẽ sử dụng spawn trên exec bởi vì chúng ta đang nói về việc truyền dữ liệu và lượng lớn dữ liệu tiềm năng. Để hiểu sự khác biệt giữa child_process.spawnchild_process.exec xem “Sự khác biệt giữa spawn và exec của Node.js child_ process”.

Dài và ngắn của nó là sử dụng exec đối với lượng dữ liệu nhỏ (dưới 200k) bằng giao diện Bộ đệm và spawn cho số lượng lớn hơn bằng cách sử dụng giao diện luồng.

spawn có cú pháp dài dòng hơn cho một số trường hợp sử dụng mà chúng ta sẽ xem xét. Việc tích hợp với Ruby/Python/PHP sẽ thuận tiện hơn vì chúng tôi có thể nhận được nhiều dữ liệu hơn một vài dòng văn bản.

Ví dụ đầy đủ github.com/HugoDF/node-run-python.

Các ví dụ sau có 2 phần:

  • Phần thực sự chạy lệnh shell, thường là một chức năng được gọi là run
  • một IIFE (“biểu thức hàm được gọi ngay lập tức”) thực sự gọi nó, (async () => { await run() })(). IIFE này là một mẫu đẹp được kích hoạt bởi async/await (xem Async JS: history, patterns and gotchas) nhưng nó chỉ ở đó cho mục đích minh họa vì nó đại diện cho lời gọi đến góied spcuộc gọi awn từ một phần khác của ứng dụng của bạn.

Gọi một lệnh shell và đăng nhập nó

sử dụng spawn là quá mức cần thiết trong tình huống này vì echo sẽ chỉ trả lại những gì được truyền cho nó.

Ví dụ này khá dễ hiểu và chỉ ra cách sử dụng child_process.spawn để “bóc vỏ” và đọc lại dữ liệu đó.

spawn lấy tệp thực thi để gọi làm tham số đầu tiên và tùy chọn một mảng các tùy chọn/tham số cho tệp thực thi làm tham số thứ hai.

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);
  }
})();

Gọi Python cho phiên bản của nó

Chúng tôi sẽ di chuyển khá nhanh để giới thiệu cách chúng tôi sẽ làm điều gì đó tương tự như trên với Python. Lưu ý lại cách làm --version được truyền vào bên trong một mảng.

Đọc thêm  Thêm tập lệnh vào header.php - Diễn đàn freeCodeCamp

Chúng tôi cũng tạo một trình ghi nhật ký đẹp mắt để phân biệt giữa thiết bị xuất chuẩn và thiết bị xuất chuẩn và liên kết với chúng. Vì spawn trả về một thể hiện có stdoutstderr trình phát sự kiện, chúng ta có thể liên kết logOutput chức năng để 'data' sự kiện sử dụng .on('data', () => { /* our callback function */ }).

Một mẩu tin thú vị khác là python --version xuất phiên bản thành stderr. Sự không nhất quán xung quanh việc liệu các tệp thực thi *NIX có sử dụng mã thoát, thiết bị xuất chuẩn và thiết bị xuất chuẩn khi thành công/lỗi hay không là một vấn đề khó hiểu mà chúng ta sẽ phải lưu ý khi tích hợp Python/Ruby/khác với 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);
  }
})();

Đầu ra:

$ node run.js
[stderr] Python 2.7.13

Gọi tập lệnh Python từ Node

Bây giờ chúng ta sẽ chạy một tập lệnh Python chính thức (mặc dù nó cũng có thể là Ruby, PHP, shell, v.v.) từ Node.js.

Đây là script.pynó chỉ đăng xuất argv (“vectơ đối số”, tức là. ['path/to/executable', /* command line arguments ])

import sys
print(sys.argv)

Giống như trong ví dụ trước, chúng ta sẽ chỉ gọi spawn với python với đường dẫn đến tập lệnh Python (./script.py) trong tham số thứ hai.

Đây là một vấn đề khác về việc tích hợp các tập lệnh theo cách này. Trong ví dụ này, đường dẫn đến tập lệnh dựa trên thư mục làm việc mà từ đó node được gọi là.

Tất nhiên, có một cách giải quyết khác là sử dụng path mô-đun và __dirnameví dụ như có thể giải quyết một other-script.py cùng vị trí với lệnh gọi tệp JavaScript/mô-đun Node spawn sử dụng: require('path').resolve(__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);
  }
})();

Đầu ra:

node run.js
\[stdout\] ['./script.py']

Truyền đối số cho tập lệnh Python từ Node.js bằng cách sử dụng child_ process.spawn

Bước tiếp theo của quá trình tích hợp là có thể chuyển dữ liệu từ mã Node/JavaScript sang tập lệnh Python.

Đọc thêm  Biến siêu toàn cầu PHP,$_POST - Diễn đàn freeCodeCamp

Để làm được điều này, chúng ta sẽ chuyển thêm các đối số shell bằng cách sử dụng mảng đối số (tham số thứ hai cho 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);
  }
})();

Của chúng ta script.py cũng sẽ chỉ đăng xuất argv ngoại trừ phần tử đầu tiên (là đường dẫn đến tập lệnh).

import sys
print(sys.argv)[1:]

Đây là đầu ra:

node run.js
\[stdout\] ['my', 'args']

Đọc đầu ra child_ process.spawn từ Node.js

Thật tuyệt khi có thể chuyển dữ liệu xuống tập lệnh Python. Chúng tôi vẫn không thể lấy lại dữ liệu từ tập lệnh Python ở định dạng mà chúng tôi có thể tận dụng trong ứng dụng Node.js/JavaScript của mình.

Giải pháp cho vấn đề này là bọc toàn bộ spawn -calling chức năng thành một Promise. Điều này cho phép chúng tôi quyết định khi nào chúng tôi muốn resolve hoặc reject.

Để theo dõi (các) luồng đầu ra của tập lệnh Python, chúng tôi đệm đầu ra theo cách thủ công bằng cách sử dụng các mảng (một cho stdout và một cái khác cho stderr).

Chúng tôi cũng thêm một người nghe cho 'exit' sử dụng spawn().on('exit', (code, signal) => { /* probably call resolve() */ }). Đây là nơi chúng ta sẽ có xu hướng to resotôive/reject (các) giá trị của Lời hứa từ Python/Ruby/tập lệnh khác.

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);
  }
})();

Đầu ra:

node run.js
\[stdout\] ['my', 'args']
\[main\] ['my', 'args']

Xử lý lỗi từ child_ process.spawn

Tiếp theo, chúng ta cần xử lý các lỗi từ tập lệnh Python/Ruby/shell ở cấp độ Node.js/JavaScript.

Cách chính mà tệp thực thi *NIX báo hiệu rằng nó bị lỗi là sử dụng 1 mã thoát. Đó là lý do tại sao .on('exit' trình xử lý bây giờ thực hiện kiểm tra đối với code === 0 trước khi quyết định giải quyết hay từ chối với (các) giá trị.

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);
  }
})();

Đầu ra:

node run.js
[stderr] Traceback (most recent call last):
    File "./script.py", line 3, in <module>
    print(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 <module>
    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)

Truyền dữ liệu có cấu trúc từ Python/Ruby sang Node.js/JavaScript

Bước cuối cùng để tích hợp đầy đủ giữa các tập lệnh Ruby/Python/PHP/shell và lớp ứng dụng Node.js/JavaScript của chúng tôi là có thể chuyển dữ liệu có cấu trúc trở lại từ tập lệnh cho đến Node.js/JavaScript.

Đọc thêm  Hướng dẫn PHP – Cách thiết lập PHP và XAMPP cho dự án của bạn

Định dạng dữ liệu có cấu trúc đơn giản nhất có xu hướng khả dụng trong cả Python/Ruby/PHP và Node.js/JavaScript là JSON.

Trong tập lệnh Python, chúng tôi in json.dumps() đầu ra của một từ điển, xem 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))

Trong Node, chúng tôi thêm một số logic phân tích cú pháp JSON (sử dụng JSON.parse) bên trong 'exit' người xử lý.

Một vấn đề ở điểm này là nếu, ví dụ JSON.parse() không thành công do JSON được định dạng sai, chúng tôi cần phải khắc phục lỗi đó. Do đó, thử/bắt nơi catch mệnh đề reject-s lỗi tiềm ẩn: try { resolve(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);
  }
})();

Đầu ra:

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

Đó là nó! Cảm ơn vì đã đọc 🙂

Tôi đã mở các điểm cố vấn tại https://mentorcruise.com/mentor/HugoDiFrancesco/. Làm điều đó nếu bạn muốn Node.js/JavaScript/cố vấn nghề nghiệp hoặc cảm thấy tự do để tweet cho tôi @hugo__df

Và đọc thêm các bài viết của tôi trên codewithhugo.com





Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status