Elixir/Erlang:与外部进程通信

5
假设我有一个简单的Python脚本,使用subprocess模块执行elixir/erlang脚本。假设Python脚本的操作系统PID为P1,而运行的elixir/erlang脚本的PID为P2
我想知道是否可以在P1P2之间进行通信。更具体地说,P1将一些内容写入P2的标准输入(stdin),P2读取从P1接收到的输入,并将一些相应的输出写入其自己的标准输出(stdout)和P1P2的标准输出(stdout)读取并再次将一些内容写入P2的标准输入(stdin)等等。
我知道另一种方法是可能的,即从内部生成外部进程然后与该进程通信。感谢任何帮助。

2
当然可以。只需使用Elixir的IO.gets(或类似)读取,使用IO.puts(或类似)写入,然后在Python中执行相反的操作(https://dev59.com/tWoy5IYBdhLWcg3wkOsK)。你试过了吗? - Dogbert
谢谢!如果P1也是一个Elixir进程呢? - stark
1
你可以使用 Ports 而不是 subprocess。https://hexdocs.pm/elixir/Port.html - Dogbert
你可以使用两个命名管道,我相当确定。 - Don Branson
2个回答

2
是的,这种跨语言的IPC完全是可能的。绝大多数文档、博客文章和回答(以及到目前为止在StackOverflow上的回答!)都假定与您所询问的相反,即Erlang / Elixir生成Python子进程,而不是Python生成Erlang / Elixir子进程。如果可以接受(即您的Erlang或Elixir应用程序启动Python进程),那太好了!Badu的答案将帮助您实现这一点,您还可以查看Elixir的Port模块的文档进行额外参考。
但这似乎不是你要找的答案,也不那么有趣。世界需要更多关于如何以Python脚本的子进程运行Erlang的文档!首先,我们的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可能有点过头了,但我假设您最终想要做一些更高级的操作。

接下来,您需要在您的mix.exs中添加一个escript选项,像这样:

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需要做一个小调整,指向这个新的Elixir化的ping/pong IPC东西:


#!/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 处理方式导致它过早地认为已经到达文件结尾。但至少有一种方法是起作用的!" 最初的回答。

0

就像Dogbert所说的那样,你可以使用Ports。看看Erlport,这里还有一篇关于Elixir和Python之间通信的博客文章here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接