查看原始碼 IEx (IEx v1.16.2)

Elixir 的互動式 shell。

這裡描述的某些功能取決於您的終端。特別是,如果您收到一條消息說無法運行智能終端,這裡描述的一些功能將無法使用。

助手

IEx 提供了一系列助手。它們可以通過在 shell 中輸入 h() 或作為 IEx.Helpers 模組的文檔來訪問。

自動完成

要發現模組的公共函數或其他模組,請輸入模組名後跟一個句點,然後按 tab 觸發自動完成。例如

Enum.

一個模組可能導出不打算直接使用的函數:這些函數不會被 IEx 自動完成。IEx 不會自動完成帶有 @doc false@impl true 註釋的函數,或者未明確記錄並且函數名字以 __foo__ 形式的函數。

自動完成在 Windows shells 的 Erlang/OTP 26 中默認可用。在較早的版本中,您可能需要在啟動 IEx 時傳遞 --werl 選項,例如 iex --werl(如果使用 PowerShell,則為 iex.bat --werl)。可以通過將 IEX_WITH_WERL 環境變量設置為 1 來永久啟用 --werl

編碼和著色

IEx 預期輸入和輸出以 UTF-8 編碼。這是大多數 Unix 終端的默認值,但在 Windows 上可能不是這種情況。如果您在 Windows 上運行並且看到打印的值不正確,您可能需要在調用 iex(或者如果使用 PowerShell,則在調用 iex.bat 之前)運行 chcp 65001 以更改當前會話的編碼。

同樣,大多數 Unix 終端默認啟用 ANSI 着色。它們也可在 Windows 10 上的控制台和 Erlang/OTP 26 或更高版本上使用。對於早期的 Erlang/OTP 版本,您可以通過運行以下命令在註冊表中為當前用戶顯式啟用它

$ reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1

在運行上述命令之後,您必須重新啟動當前的控制台。

Shell 歷史記錄

可以通過傳遞一些選項來啟用 VM 中的 shell 歷史記錄。這可以在啟動 IEx 時根據需要進行。

$ iex --erl "-kernel shell_history enabled"

如果您寧願將其在系統中全局啟用,則可以使用 ERL_AFLAGS 環境變量,並確保在您的終端/ shell 配置中設置相應的值。

在類 Unix / Bash 上

$ export ERL_AFLAGS="-kernel shell_history enabled"

在 Windows 上

$ set ERL_AFLAGS "-kernel shell_history enabled"

在 Windows 10 / PowerShell 上

$ $env:ERL_AFLAGS = "-kernel shell_history enabled"

IEx 中的表達式

作為交互式 shell,IEx 評估表達式。這有一些值得討論的有趣結果。

第一個是代碼是真正評估的,而不是編譯的。這意味著在 shell 中進行的任何基準測試都將產生偏差的結果。因此,永遠不要在 shell 中運行任何分析或基準測試。

其次,IEx 允許您將一個表達式分成多行,因為這在 Elixir 中很常見。例如

iex(1)> "ab
...(1)> c"
"ab\nc"

在上面的例子中,shell 將等待更多輸入,直到找到結束引號。有時候不明顯 shell 預期哪個字符,用戶可能會發現自己陷入未完成表達式狀態,無法終止它,除非退出 shell。

對於這種情況,有一個特殊的斷點觸發器(#iex:break),當它在一行中被單獨遇到時,將強制 shell 中斷任何待處理的表達式並返回正常狀態

iex(1)> ["ab
...(1)> c"
...(1)> "
...(1)> ]
...(1)> #iex:break
** (TokenMissingError) iex:1: incomplete expression

將多行表達式粘貼到 IEx 中

IEx 以急切方式逐行評估其輸入。如果在一行的結尾處,到目前為止看到的代碼是完整的表達式,那麼 IEx 將在該點評估它。

iex(1)> [1, [2], 3]
[1, [2], 3]

為了防止這種行為破壞有效的代碼,其中後續行以二進制運算符開始,例如|>/2++/2,IEx會自動將這些行視為前置了IEx.Helpers.v/0的操作,該操作返回前一個表達式的值(如果有)。

iex(1)> [1, [2], 3]
[1, [2], 3]
iex(2)> |> List.flatten()
[1, 2, 3]

上述等同於

iex(1)> [1, [2], 3]
[1, [2], 3]
iex(2)> v() |> List.flatten()
[1, 2, 3]

如果歷史記錄中沒有前一個表達式,則管道運算符將失敗

iex(1)> |> List.flatten()
** (RuntimeError) v(-1) is out of bounds

如果前一個表達式是匹配操作,則管道運算符也將失敗,以防止未經請求的匹配中斷

iex(1)> x = 42
iex(2)> |> IO.puts()
** (SyntaxError) iex:2:1: pipe shorthand is not allowed immediately after a match expression in IEx. To make it work, surround the whole pipeline with parentheses ('|>')
    |
  2 | |> IO.puts()
    | ^

請注意,上述對於+/2-/2不起作用,因為它們與一元+/1-/1具有二義性

iex(1)> 1
1
iex(2)> + 2
2

中斷菜單

在IEx中,按下Ctrl+C將打開BREAK菜單。在此菜單中,您可以退出shell,查看進程和ETS表信息等等。

退出shell

有幾種方法可以退出IEx shell

  • 通過BREAK菜單(通過Ctrl+C可用),輸入q,然後按enter
  • 通過按下Ctrl+CCtrl+C
  • 通過按下Ctrl+\

如果您連接到遠程shell,則在斷開連接後它仍然保持活動狀態。

dbg和斷點

IEx與Kernel.dbg/2集成,並引入了一個可以暫停代碼執行的後端。要啟用它,您必須傳遞--dbg pry

$ iex --dbg pry

例如,請參考以下函數

def my_fun(arg1, arg2) do
  dbg(arg1 + arg2)
  ... implementation ...
end

當代碼以 iex 執行時(通常通過調用 iex --dbg pry -S mix),它將請求您允許使用 "pry"。如果您同意,它將在上述函數的上下文中啟動一個 IEx shell,可以訪問其變量、導入和別名。但是,您只能訪問現有值,無法訪問私有函數,也無法更改執行本身(因此命名為 "pry")。

當在管道的末尾使用 |> dbg() 時,您可以在管道的每一步進行檢查。您可以在需要時輸入 n 以跳轉到下一個管道。當您想要執行所有步驟但仍保持在檢查過程中時,請輸入 continue。當您想要退出檢查過程並啟動新的 shell 時,請輸入 respawn

或者,您可以直接開始一個 pry 會話,而不使用 dbg/2,通過調用 IEx.pry/0

IEx 還允許您設置斷點,以便在給定模塊、函數和您無法控制的參數上開始 pry 會話,通過 IEx.break!/4。與 dbg() 中的管道類似,IEx.break!/4 允許您逐行調試函數並訪問其變量。但斷點不包含來自源代碼的導入和別名的信息。

在使用測試時使用 dbg 或斷點時,請記得將 --trace 傳遞給 mix test,以避免遇到超時

$ iex -S mix test --trace
$ iex -S mix test path/to/file:line --trace

用戶切換命令

除了 BREAK 菜單外,您還可以輸入 Ctrl+G 來進入 User switch command 菜單。到達後,您可以輸入 h 以獲取更多信息。

在此菜單中,開發人員可以開始新的 shell 並在它們之間切換。讓我們試試看

User switch command
 --> s 'Elixir.IEx'
 --> c

上面的命令將啟動一個新的 shell 並連接到它。創建一個名為 hello 的新變量並為其賦值

hello = :world

現在,讓我們回到第一個 shell

User switch command
 --> c 1

現在,再次嘗試訪問 hello 變量

hello
** (CompileError) undefined variable "hello"

上面的命令失敗了,因為我們切換了shell。由於各個shell是相互隔離的,所以無法從一個shell中訪問在另一個shell中定義的變量。

用戶切換命令 也可以用於終止現有的會話,例如當評估器陷入無限循環時,或者當您在輸入表達式時卡住時。

User switch command
 --> i
 --> c

用戶切換命令 菜單還允許開發人員使用 r 命令連接到遠程shell。這是我們接下來要討論的話題。

遠程shell

IEx 允許您以兩種方式連接到另一個節點。首先,我們只能在給出當前shell和要連接的shell的名稱的情況下連接到shell。

讓我們試一下。首先,啟動一個新的shell

$ iex --sname foo
iex(foo@HOST)1>

提示符括號中的字符串是您節點的名稱。我們可以通過調用 node/0 函數來檢索它

iex(foo@HOST)1> node()
:"foo@HOST"
iex(foo@HOST)2> Node.alive?()
true

為了好玩,讓我們在這個shell中定義一個簡單的模塊

iex(foo@HOST)3> defmodule Hello do
...(foo@HOST)3>   def world, do: "it works!"
...(foo@HOST)3> end

現在,讓我們再啟動另一個shell,同樣給它一個名字

$ iex --sname bar
iex(bar@HOST)1>

如果我們嘗試調用 Hello.world/0,它將不可用,因為它僅在另一個shell中定義

iex(bar@HOST)1> Hello.world()
** (UndefinedFunctionError) undefined function Hello.world/0

但是,我們可以遠程連接到另一個shell。打開 User switch command 提示符(Ctrl+G)並輸入

User switch command
 --> r 'foo@HOST' 'Elixir.IEx'
 --> c

現在我們已經連接到遠程節點了,因為提示符向我們顯示,我們可以訪問在那裡定義的信息和模塊

iex(foo@HOST)1> Hello.world()
"it works!"

事實上,連接到遠程shell是如此常見,我們也通過命令行提供了一個快捷方式

$ iex --sname baz --remsh foo@HOST

其中 "remsh" 意思是 "remote shell"。通常,Elixir 支持

  • 從一個Elixir節點到另一個Elixir節點的remsh
  • 從一個普通的Erlang節點到一個Elixir節點的remsh(通過^G菜單)
  • 從一個Elixir節點到一個普通的Erlang節點的remsh(並在那裡獲得一個 erl shell)

不支持將一個Elixir shell連接到沒有Elixir的遠程節點。

.iex.exs 文件

開始時,IEx 會尋找本地的 .iex.exs 檔案(位於目前的工作目錄中),然後尋找全局的 .iex.exs 檔案,該檔案位於由 IEX_HOME 環境變數指定的目錄內(預設為 ~),並加載找到的第一個(如果有的話)。

所選擇的 .iex.exs 檔案中的代碼會在 shell 的上下文中逐行評估,就好像每行都在 shell 中輸入一樣。例如,.iex.exs 檔案中加載的任何模塊或綁定的變量將在啟動後的 shell 中可用。

請參閱以下 .iex.exs 檔案

# Load another ".iex.exs" file
import_file("~/.iex.exs")

# Import some module from lib that may not yet have been defined
import_if_available(MyApp.Mod)

# Print something before the shell starts
IO.puts("hello world")

# Bind a variable that'll be accessible in the shell
value = 13

在上述 .iex.exs 檔案所在的目錄中運行 IEx 將會產生

$ iex
Erlang/OTP 24 [...]

hello world
Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> value
13

可以通過為 IEx 提供 --dot-iex 選項來加載另一個檔案。請參閱 iex --help

對於遠程節點,.iex.exs 檔案的位置是相對於啟動應用程序的用戶,而不是相對於遠程 IEx 連接的用戶。

配置 shell

IEx 提供了一些自定義選項。通過輸入 h IEx.configure/1 來查看 IEx.configure/1 函數的文檔。

這些選項可以在您的項目配置文件中配置,也可以通過從您的 ~/.iex.exs 檔案中調用 IEx.configure/1 進行全局配置。例如

# .iex.exs
IEx.configure(inspect: [limit: 3])

現在運行 shell

$ iex
Erlang/OTP 24 [...]

Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> [1, 2, 3, 4, 5]
[1, 2, 3, ...]

總結

功能

基於宏的快捷方式,用於 IEx.break!/4

設置在給定數量的 stops 中的 modulefunctionarity 中的斷點。

返回使用指定 color 轉義的 string

返回 IEx 配置。

配置 IEx。

返回用於檢查的選項。

查看進程環境。

如果 IEx 已啟動則返回 true,否則返回 false

返回用於打印的 IEx 寬度。

函數

連結到此宏

break!(ast, stops \\ 1)

查看源碼 (自 1.5.0 版本起) (宏)

基於宏的快捷方式,用於 IEx.break!/4

連結到此函數

break!(module, function, arity, stops \\ 1)

查看源碼 (自 1.5.0 版本起)
@spec break!(module(), atom(), arity(), non_neg_integer()) :: IEx.Pry.id()

設置在給定數量的 stops 中的 modulefunctionarity 中的斷點。

此函數將對給定的模組進行儀器化,並在記憶體中加載新版本,並在給定的函數和參數性上進行逐行斷點。如果重新編譯模組,則所有斷點都將丟失。

當達到斷點時,IEx 將詢問您是否要 pry 給定的函數和參數性。換句話說,這與 IEx.pry/0 類似,因為運行過程變成了 IEx 命令的評估器,並且暫時更改為具有自定義組領導者。但是,與 IEx.pry/0 不同,來源代碼中的別名和導入在 shell 中不可用。

IEx 助手包括與斷點相關的許多方便功能。下面列出了完整的模塊,例如 IEx.Helpers.breaks/0,但請記住,它可以在 IEx 中直接調用為 breaks()。它們是

默認情況下,斷點中的停止次數為 1。除非設置另一個斷點,否則任何後續調用都不會停止代碼執行。

或者,可以通過傳遞 stops 參數來增加停止次數。可以使用 IEx.Helpers.reset_break/1IEx.Helpers.reset_break/3 來將停止次數重置為零。請注意,即使在所有斷點上的所有停止都被消耗後,模組仍然保持“儀器化”。您可以通過調用 IEx.Helpers.remove_breaks/1 在給定模組上刪除儀器化,在所有模組上刪除斷點。

在斷點內部,可以調用 n 跳轉到下一行。要退出斷點,可以調用 continue,它將阻塞 shell,直到找到下一個斷點或進程終止,或者調用 respawn,它將啟動一個新的 IEx shell,釋放原始 shell。

示例

以下示例將使用 break!,假設您直接從您的 IEx shell 設置斷點。但您可以通過使用完全限定名稱 IEx.break! 在任何地方設置斷點。

以下將在 URI.parse/1 上設置斷點

break! URI, :parse, 1

此調用將設置一個停止一次的斷點。要設置一個停止 10 次的斷點

break! URI, :parse, 1, 10

IEx.break!/2 是一個方便的宏,允許以 Mod.fun/arity 格式給出斷點

break! URI.parse/1

或者設置一個停止 10 次的斷點

break! URI.parse/1, 10

此函數返回斷點 ID,如果設置斷點時出錯,則會引發異常。

模式和守衛

IEx.break!/2 允許給出模式,只在某些情況下觸發斷點。例如,只有當第一個參數以“https”字符串開頭時才觸發斷點

break! URI.parse("https" <> _, _)

每個函數只能設置一個斷點。因此,如果多次使用不同模式調用 IEx.break!,則只保留最後一個模式。

巨集

儘管可以在巨集中設置斷點,但請記住,巨集通常在編譯時擴展,因此它們在運行時可能永遠不會被調用。同樣,儘管可以給巨集設置模式,但巨集接收的是 AST,而不是值。例如,如果您嘗試在以下模式上中斷巨集

break! MyModule.some_macro(pid) when pid == self()

這個斷點永遠不會被觸發,因為巨集永遠不會收到 PID。即使您將巨集呼叫為 MyModule.some_macro(self()),巨集將收到代表 self() 調用的 AST,而不是 PID 本身。

斷點和 mix test

在測試期間使用 IEx.break!/4,您需要在 iex 命令中運行 mix,並在 mix test 中傳遞 --trace 以避免遇到超時問題

$ iex -S mix test --trace
$ iex -S mix test path/to/file:line --trace
@spec color(atom(), iodata()) :: iodata()

返回使用指定 color 轉義的 string

字符串中的 ANSI 转义并不会以任何方式进行处理。

@spec configuration() :: keyword()

返回 IEx 配置。

@spec configure(keyword()) :: :ok

配置 IEx。

支持的選項包括

  • :colors
  • :inspect
  • :width
  • :history_size
  • :default_prompt
  • :continuation_prompt
  • :alive_prompt
  • :alive_continuation_prompt
  • :parser

這些選項在下面的各節中分別討論。

顏色

封裝了 shell 使用的所有顏色設置的關鍵字列表。請參閱 IO.ANSI 模塊的文檔以查看支持的顏色和屬性列表。

關鍵字列表中支持的鍵列表

  • :enabled - 允許開啟或關閉著色的布林值
  • :eval_result - 表達式結果值的顏色
  • :eval_info - ... 各種信息消息
  • :eval_error - ... 錯誤消息
  • :eval_interrupt - ... 中斷消息
  • :stack_info - ... 堆棧跟踪的顏色
  • :blame_diff - ... 當源代碼無匹配時歸咎於的選項
  • :ls_directory - ... 用於目錄條目 (ls 助手)
  • :ls_device - ... 用於設備條目 (ls 助手)

在打印文檔時,IEx 也會將 Markdown 文檔轉換為 ANSI。這些顏色可以通過配置進行設置

  • :doc_code - 代碼塊的屬性 (青色,明亮)
  • :doc_inline_code - 內聯代碼 (青色)
  • :doc_headings - h1 和 h2 (黃色,明亮)
  • :doc_title - 輸出的整體標題 (反轉,黃色,明亮)
  • :doc_bold - (明亮)
  • :doc_underline - (下劃線)

IEx 還會使用 :syntax_colors 選項對檢查表達式進行顏色設置。可以通過以下方式禁用該功能

IEx.configure(colors: [syntax_colors: false])

您也可以按需配置語法顏色。以下示例將原子格式化為紅色,並將其他數據類型的顏色設置為無

IEx.configure(colors: [syntax_colors: [atom: :red]])

默認值可以在 IO.ANSI.syntax_colors/0 中找到。

檢查

包含 shell 在打印表達式評估結果時使用的檢查選項的關鍵字列表。默認為漂亮格式化,限制為 50 條目。

要顯示所有條目,請將限制配置為 :infinity

IEx.configure(inspect: [limit: :infinity])

請參閱 Inspect.Opts 以獲取完整的選項列表。

寬度

一個整數,表示輸出中要使用的最大列數。默認值為 80 列。實際輸出寬度是此數字和 :io.columns 結果的最小值。這樣您可以將 IEx 配置為您的最大屏幕尺寸,並始終佔用當前終端屏幕的全寬。

歷史記錄大小

要保留的表達式及其結果的數量。該值是一個整數。當它為負數時,歷史記錄是無限的。

提示

這是決定等待輸入時顯示給使用者的提示選項。

該值是一個關鍵字列表,包含兩個可能的鍵,表示提示類型

  • :default_prompt - 當 Node.alive?/0 返回 false 時使用

  • :continuation_prompt - 當 Node.alive?/0 返回 false 且需要更多輸入時使用

  • :alive_prompt - 當 Node.alive?/0 返回 true 時使用

  • :alive_continuation_prompt - 當 Node.alive?/0 返回 true 且需要更多輸入時使用

提示字串中的以下值將被適當地替換

  • %counter - 歷史索引
  • %prefix - 由 IEx.Server 給定的前綴
  • %node - 本地節點的名稱

解析器

這是決定用於 IEx 的解析器的選項。

解析器是一個 "mfargs",它是一個具有三個元素的元組:模塊名稱、函數名稱和要附加的額外參數。解析器至少接收三個參數,當前輸入作為字符串,解析選項作為關鍵字列表,以及緩衝區作為字符串。它必須返回 {:ok, expr, buffer}{:incomplete, buffer}

如果解析器引發異常,則緩衝區將重置為空字符串。

@spec inspect_opts() :: keyword()

返回用於檢查的選項。

查看進程環境。

此函數用於調試特定塊的代碼時,由特定進程執行。該進程成為 IEx 命令的評估器,並暫時更改為具有自定義群組領導者。通過調用 IEx.Helpers.respawn/0 來恢復這些值,它開始一個新的 IEx shell,釋放被窺視的那個。

當進程被窺視時,所有代碼都在 IEx 內運行,並且可以訪問原始代碼的所有導入和別名。但是,您無法更改代碼的執行方式,也無法訪問被窺視模塊的私有函數。模塊函數仍然需要通過 Mod.fun(args) 訪問。

另請參見 break!/4 以了解其他窺視方式。

dbg/0 整合

透過呼叫 iex --dbg pryiex 將此函式設定為 dbg/0 呼叫的預設後端。

範例

假設您想要調查某個特定函式正在發生的情況。通過從該函式調用 IEx.pry/0,IEx 將允許您訪問其綁定(變數),驗證其詞法資訊並訪問進程資訊。讓我們看一個例子

import Enum, only: [map: 2]

defmodule Adder do
  def add(a, b) do
    c = a + b
    require IEx; IEx.pry()
  end
end

當調用 Adder.add(1, 2) 時,您將在您的 shell 中收到一條消息,要求進入給定的環境。通過允許它,shell 將被重置,您將可以訪問從上面獲得的所有變數和詞法作用域

iex(1)> map([a, b, c], &IO.inspect(&1))
1
2
3

請注意,IEx.pry/0 在調用進程中運行,會在評估循環期間阻塞該調用者。調用者進程可以通過調用 respawn/0 來釋放,該函數開始一個新的 IEx 評估循環,讓此進程繼續進行

iex(2)> respawn()
true

Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help)

在 IEx 中設定變數或匯入模塊不會影響調用者的環境。然而,發送和接收訊息將改變進程狀態。

Pry 和巨集

當在由巨集定義的程式碼中設置 Pry 時,例如

defmacro __using__(_) do
  quote do
    def add(a, b) do
      c = a + b
      require IEx; IEx.pry()
    end
  end
end

由於引用表達式中的衛生機制,quote 內定義的變數在進行 prying 時將不可用。衛生機制會更改引用表達式中的變數名稱,以使其不與巨集的使用者定義的變數發生衝突。因此,原始名稱不可用。

Pry 和 mix test

在測試期間使用IEx.pry/0,您需要在iex命令中運行mix,並將--trace傳遞給mix test,以避免超時

$ iex -S mix test --trace
$ iex -S mix test path/to/file:line --trace
@spec started?() :: boolean()

如果 IEx 已啟動則返回 true,否則返回 false

這意味著IEx應用程式已啟動,但其命令列介面未運行。

@spec width() :: pos_integer()

返回用於打印的 IEx 寬度。

被助手使用,並且具有默認的最大字符上限為80個字符。