當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Elixir Port用法及代碼示例


Elixir語言中 Port 相關用法介紹如下。

通過端口與外部世界交互的函數。

端口提供了一種機製來啟動 Erlang VM 外部的操作係統進程並通過消息傳遞與它們通信。

示例

iex> port = Port.open({:spawn, "cat"}, [:binary])
iex> send(port, {self(), {:command, "hello"}})
iex> send(port, {self(), {:command, "world"}})
iex> flush()
{#Port<0.1444>, {:data, "hello"}}
{#Port<0.1444>, {:data, "world"}}
iex> send(port, {self(), :close})
:ok
iex> flush()
{#Port<0.1464>, :closed}
:ok

在上麵的示例中,我們創建了一個執行程序 cat 的新端口。 cat 是Unix-like 操作係統上可用的程序,它接收來自多個輸入的數據並將它們連接到輸出中。

創建端口後,我們使用 Kernel.send/2 以消息的形式向它發送兩個命令。第一個命令的二進製有效負載為"hello",第二個命令為"world"。

發送這兩條消息後,我們調用了 IEx 助手 flush() ,它打印了從端口接收到的所有消息,在這種情況下,我們得到了 "hello" 和 "world"。請注意,消息是二進製的,因為我們在 Port.open/2 中打開端口時傳遞了 :binary 選項。如果沒有這樣的選項,它將產生一個字節列表。

一切完成後,我們關閉了端口。

Elixir 為使用端口提供了許多便利,但也有一些缺點。我們將在下麵探討這些。

消息和函數 API

有兩個 API 用於處理端口。它可以通過消息傳遞異步,如上例所示,也可以通過調用此模塊上的函數來實現。

端口支持的消息及其對應的函數API如下:

  • {pid, {:command, binary}} - 將給定的數據發送到端口。見 command/3

  • {pid, :close} - 關閉端口。除非端口已經關閉,否則端口將在刷新其緩衝區並有效關閉後回複{port, :closed} 消息。見 close/1

  • {pid, {:connect, new_pid}} - 將 new_pid 設置為端口的新所有者。打開端口後,該端口將鏈接並連接到調用者進程,並且與端口的通信僅通過連接的進程發生。此消息使new_pid 成為新連接的進程。除非端口已死,否則端口將使用 {port, :connected} 回複舊所有者。見 connect/2

反過來,端口將向連接的進程發送以下消息:

  • {port, {:data, data}} - 端口發送的數據
  • {port, :closed} - 回複 {pid, :close} 消息
  • {port, :connected} - 回複 {pid, {:connect, new_pid}} 消息
  • {:EXIT, port, reason} - 端口崩潰時的退出信號。如果原因不是 :normal ,則僅當所有者進程正在捕獲退出時才會收到此消息

開放機製

端口可以通過四種主要機製打開。

作為一個簡短的總結,最好使用下麵提到的:spawn:spawn_executable 選項。其他兩個選項 :spawn_driver:fd 用於 VM 中的高級用法。如果您隻想執行程序並檢索其返回值,還可以考慮使用 System.cmd/3

產卵

:spawn 元組接收將作為完整調用執行的二進製文件。例如,我們可以使用它直接調用"echo hello":

iex> port = Port.open({:spawn, "echo hello"}, [:binary])
iex> flush()
{#Port<0.1444>, {:data, "hello\n"}}

:spawn 將從參數中檢索程序名稱並遍曆您的操作係統 $PATH 環境變量以尋找匹配的程序。

盡管上述內容很方便,但這意味著不可能調用名稱或任何參數中包含空格的可執行文件。由於這些原因,大多數時候最好執行 :spawn_executable

spawn_executable

Spawn 可執行文件是 spawn 的更受限製和更明確的版本。它需要您要執行的可執行文件的完整文件路徑。如果它們在您的 $PATH 中,則可以通過調用 System.find_executable/1 來檢索它們:

iex> path = System.find_executable("echo")
iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
iex> flush()
{#Port<0.1380>, {:data, "hello world\n"}}

使用 :spawn_executable 時,可以通過 :args 選項傳遞參數列表,如上所述。有關選項的完整列表,請參閱 Erlang 函數 :erlang.open_port/2 的文檔。

fd

:fd 名稱選項允許開發人員訪問 Erlang VM 使用的 inout 文件說明符。隻有在重新實現運行時係統的核心部分(例如 :user:shell 進程)時,才會使用這些。

僵屍操作係統進程

可以通過 close/1 函數或發送{pid, :close} 消息來關閉端口。但是,如果 VM 崩潰,由端口啟動的 long-running 程序將關閉其標準輸入和標準輸出通道,但不會自動終止。

雖然大多數 Unix 命令行工具會在其通信通道關閉後退出,但並非所有命令行應用程序都會這樣做。您可以通過啟動端口然後關閉 VM 並檢查您的操作係統以查看端口進程是否仍在運行來輕鬆檢查這一點。

雖然我們通過檢測標準輸入/標準輸出是否已關閉來鼓勵正常終止,但我們並不總是能夠控製第三方軟件的終止方式。在這些情況下,您可以將應用程序包裝在檢查標準輸入的腳本中。這是經過驗證可以在 bash shell 上運行的腳本:

#!/usr/bin/env bash

# Start the program in the background
exec "$@" &
pid1=$!

# Silence warnings from here on
exec >/dev/null 2>&1

# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
  while read; do :; done
  kill -KILL $pid1
) &
pid2=$!

# Clean up
wait $pid1
ret=$?
kill -KILL $pid2
exit $ret

請注意,上麵的程序劫持了標準輸入,因此您將無法通過標準輸入與底層軟件進行通信(從積極的方麵來說,從標準輸入讀取的軟件通常會在標準輸入關閉時終止)。

現在代替:

Port.open(
  {:spawn_executable, "/path/to/program"},
  args: ["a", "b", "c"]
)

您可以調用:

Port.open(
  {:spawn_executable, "/path/to/wrapper"},
  args: ["/path/to/program", "a", "b", "c"]
)

相關用法


注:本文由純淨天空篩選整理自elixir-lang.org大神的英文原創作品 Port。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。