【问题标题】:Elixir/Erlang: Communication with external processElixir/Erlang:与外部进程的通信
【发布时间】:2017-08-07 06:04:42
【问题描述】:

假设我有一个简单的 python 脚本,它使用 subprocess 模块执行 elixir/erlang 脚本。

假设 python 脚本的 OS PID 是P1,运行的衍生 elixir/erlang 脚本的 OS PID 是 P2

我想知道P1P2 之间是否可以通信。更具体地说,P1P2stdin 写入一些内容,P2P1 读取接收到的输入,并将一些相应的输出写入它自己的stdoutP1 从@987654335 读取@ 的P2 并再次向P2stdin 写一些东西等等。

我知道另一种方法是可能的,即从 elixir/erlang 内部生成外部进程,然后与该进程通信。任何帮助表示赞赏,谢谢。

【问题讨论】:

  • 当然,为什么不呢?只需使用 IO.gets(或类似的)从 Elixir 读取并使用 IO.puts(或类似的)写入,然后在 Python 中执行相反的操作(stackoverflow.com/questions/8475290/…)?你试过了吗?
  • 谢谢!如果P1 也是一个长生不老的过程呢?
  • 您可以使用端口而不是子进程。 hexdocs.pm/elixir/Port.html
  • 你可以使用两个命名管道,我很确定。

标签: erlang elixir erlang-otp


【解决方案1】:

是的,这种跨语言的 IPC 是完全可能的。绝大多数文档和博客文章等(以及迄今为止在 StackOverflow 上的回复!)假设与您似乎在问的相反 - 也就是说,他们假设 Erlang/Elixir 正在产生 Python 子进程,而不是Python 产生一个 Erlang/Elixir 子进程。如果没问题(即你的 Erlang 或 Elixir 应用程序启动 Python 进程没问题),那就太好了! Badu 的回答将帮助您做到这一点,您也可以通过 the documentation for Elixir's Port module 获得额外参考。

但这似乎不是您寻求的答案,而且不那么有趣。世界需要更多关于如何反其道而行之的文档,所以让我们深入了解将 Erlang 作为 Python 脚本的子进程运行的美妙世界!

首先,我们的 Python 脚本 (eip.py):

#!/usr/bin/env python
from subprocess import Popen, PIPE

erl = Popen(['escript', 'eip.escript'],
            stdin=PIPE, stdout=PIPE, stderr=PIPE)
ping = input('Ping: ')

outs, errs = erl.communicate(input=ping.encode('utf-8'),
                                 timeout=15)

print(outs.decode('utf-8'))

在 Erlang 方面(正如您可能已经在 Python 代码中注意到的那样),一个非常简单的方法是使用 escript 程序,它允许我们编写或多或少自包含的程序Erlang 脚本,就像这里 eip.escript:

#!/usr/bin/env escript

main(_Args) ->
  Ping = io:get_line(""),
  io:format("Pong: ~ts", [Ping]).

现在,当您运行python3 eip.py 并在Ping: 提示符下输入asdf 时,您应该会返回Pong: asdf


用 Elixir 做同样的事情只是稍微复杂一点:我们需要创建一个带有一些额外配置的 Mix 项目,比如告诉 Mix 组合一个 escript 文件。那么让我们从项目开始吧:

$ mix new eip
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/eip.ex
* creating test
* creating test/test_helper.exs
* creating test/eip_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd eip
    mix test

Run "mix help" for more commands.

(在这个简单的例子中使用 Mix 可能有点过头了,但我假设你最终会想要做一些比这个例子更高级的事情)

接下来,您需要将escript 选项添加到您的mix.exs,如下所示:

defmodule Eip.MixProject do
  use Mix.Project

  def project, do: [
    app: :eip,
    version: "0.1.0",
    elixir: "~> 1.9",
    start_permanent: Mix.env() == :prod,
    deps: deps(),
    escript: escript()
  ]

  def application, do: [extra_applications: [:logger]]

  defp deps, do: []
  defp escript, do: [main_module: Eip]
end

最后,你的lib/eip.ex 模块:

defmodule Eip do
  def main(_argv) do
    ping = IO.gets("")
    IO.puts("Pong: #{ping}")
  end
end

现在我们只需要构建它:

$ mix escript.build
Compiling 1 file (.ex)
Generated eip app
Generated escript eip with MIX_ENV=dev

eip.py 需要稍作调整才能指向这个新的 Elixirified ping/pong IPC thingamabob:

#!/usr/bin/env python
from subprocess import Popen, PIPE, TimeoutExpired

erl = Popen(['escript', 'eip/eip'],
            stdin=PIPE, stdout=PIPE, stderr=PIPE)
ping = input('Ping: ')

outs, errs = erl.communicate(input=ping.encode('utf-8'))

print(outs.decode('utf-8'))

不幸的是,这完全行不通:

$ python3 eip.py
Ping: asdf
Pong: eof

即使使用 Erlang 版本的更直接端口也会出现相同的结果(即将IO.gets("") 替换为:io.get_line("")IO.puts("Pong: #{ping}"):io.fwrite("Pong: ~ts", [ping]),这意味着通常是Elixir 的STDIN 处理特有的东西导致它过早地认为它已经到达文件末尾。但是,至少有一个方向有效!

【讨论】:

    【解决方案2】:

    就像 Dogbert 所说,您可以使用 Ports 来代替。查看Erlport here 是一篇关于 Elixir 和 Python 之间通信的博文

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-09
      • 2020-02-24
      • 2013-10-01
      • 2013-08-23
      • 1970-01-01
      • 2016-03-22
      • 2014-06-05
      • 2012-09-16
      相关资源
      最近更新 更多