Elixir / Erlang:与外部流程的沟通

时间:2017-03-16 07:27:29

标签: erlang elixir otp

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

假设python脚本的操作系统PID为P1,并且运行的生成的elixir / erlang脚本的操作系统为P2

我想知道P1P2之间的通信是否可行。更具体地说,P1stdin的{​​{1}}写入内容,P2P2读取收到的输入,并将一些相应的输出写入其自己的P1 stdout 1}}和P1stdout的{​​{1}}读取并再次向P2的{​​{1}}写入内容,依此类推。

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

2 个答案:

答案 0 :(得分:2)

是的,这种跨语言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程序,该程序允许我们编写或多或少的self包含的Erlang脚本,例如eip.escript

#!/usr/bin/env escript

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

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


用Elixir做相同的事情只是稍微复杂一点:我们需要创建一个带有一些额外配置的Mix项目,并告诉Mix将一个脚本文件放在一起。因此,让我们从项目开始:

$ 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可能也有些矫kill过正,但我​​假设您最终会想做比该示例更高级的操作)

接下来,您将要向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 whatamabob:

#!/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处理通常使它过早地认为已到达文件结尾,但是,至少有一个方向可行!

答案 1 :(得分:0)

像Dogbert说的那样,你可以使用Ports代替。查看Erlporthere是关于Elixir和Python之间沟通的博客文章