是的,这种跨语言的 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 处理特有的东西导致它过早地认为它已经到达文件末尾。但是,至少有一个方向有效!