查看源代码 IEx (IEx v1.16.2)

Elixir 的交互式 shell。

这里描述的一些功能可能在您的终端上不可用。特别是,如果您收到一条消息,表明智能终端无法运行,这里描述的一些功能将无法使用。

助手

IEx 提供了许多助手。您可以通过在 shell 中键入 h() 或作为 IEx.Helpers 模块的文档来访问它们。

自动完成

要发现模块的公共函数或其他模块,请键入模块名称后跟一个点,然后按 Tab 键触发自动完成。例如

Enum.

一个模块可能会导出一些不适合直接使用的函数:这些函数不会被 IEx 自动完成。IEx 不会自动完成带有 @doc false@impl true 注解的函数,或者未显式记录且函数名称为 __foo__ 形式的函数。

自动完成默认情况下在 Erlang/OTP 26 中的 Windows shell 上可用。在早期版本中,您可能需要在启动 IEx 时传递 --werl 选项,例如 iex --werl(或者如果使用 PowerShell,则使用 iex.bat --werl)。--werl 可以通过将 IEX_WITH_WERL 环境变量设置为 1 来永久启用。

编码和颜色

IEx 期望输入和输出采用 UTF-8 编码。这是大多数 Unix 终端的默认设置,但 Windows 上可能并非如此。如果您在 Windows 上运行并且看到打印的错误值,您可能需要通过运行 chcp 65001 来更改当前会话的编码,然后再调用 iex(或者如果使用 PowerShell,则在调用 iex.bat 之前)。

同样,ANSI 颜色默认情况下在大多数 Unix 终端上启用。它们也适用于 Windows 10 上的 Windows 控制台以及 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

BREAK 菜单

在 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 还允许您设置断点,以使用 IEx.break!/4 在您无法控制的特定模块、函数和元数上启动 pry 会话。类似于 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 来进入 用户切换命令 菜单。进入后,您可以键入 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。打开 用户切换命令 提示符 (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”表示“远程 shell”。一般来说,Elixir 支持

  • 从 Elixir 节点到 Elixir 节点的 remsh
  • 从普通 Erlang 节点到 Elixir 节点的 remsh(通过 ^G 菜单)
  • 从 Elixir 节点到普通 Erlang 节点的 remsh(并在那里获取 erl shell)

不支持将 Elixir shell 连接到没有 Elixir 的远程节点。

.iex.exs 文件

启动时,IEx 会查找本地 .iex.exs 文件(位于当前工作目录),然后查找位于 IEX_HOME 环境变量指向的目录(默认为 ~)中的全局 .iex.exs 文件,并加载它找到的第一个文件(如果有)。

所选 .iex.exs 文件中的代码在 shell 的上下文中逐行评估,就像每行都在 shell 中键入一样。例如,在 .iex.exs 文件中加载的任何模块或绑定的任何变量,在 shell 启动后都将在 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

可以使用 --dot-iex 选项向 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 的宏快捷方式。

modulefunctionarity 中设置一个断点,并设置给定的 stops 数量。

使用指定的 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()

modulefunctionarity 中设置一个断点,并设置给定的 stops 数量。

此函数将检测给定的模块,并在内存中加载一个新版本,并在给定函数和元数处设置逐行断点。如果模块被重新编译,所有断点都会丢失。

当遇到断点时,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 删除给定模块中的检测,并通过调用 IEx.Helpers.remove_breaks/0 删除所有模块中的检测。

在断点内,您可以调用 n 跳转到下一行。要退出断点,您可以调用 continue,它将阻塞 shell 直到找到下一个断点或进程终止,或者调用 respawn,它会启动一个新的 IEx shell,释放被 pry 的 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,并将 --trace 传递给 mix test 以避免遇到超时

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

使用指定的 color 返回转义的 string

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,释放被探测的 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 中设置变量或导入模块不会影响调用者的环境。但是,发送和接收消息将更改进程状态。

探测和宏

当在由宏定义的代码中设置探测时,例如

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

由于引用表达式中的卫生机制,在探测期间将无法使用在 quote 中定义的变量。卫生机制会更改引用表达式中的变量名称,以防止它们与宏使用者定义的变量冲突。因此,原始名称不可用。

探测和 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 应用程序已启动,但并不意味着它的 CLI 界面正在运行。

@spec width() :: pos_integer()

返回用于打印的 IEx 宽度。

由助手使用,并且具有默认的最大上限 80 个字符。