查看源代码 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+C
、Ctrl+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
的宏快捷方式。
在 module
、function
和 arity
中设置一个断点,并设置给定的 stops
数量。
使用指定的 color
返回转义的 string
。
返回 IEx 配置。
配置 IEx。
返回用于检查的选项。
深入进程环境。
如果 IEx 已启动,则返回 true
,否则返回 false
。
返回用于打印的 IEx 宽度。
函数
用于 IEx.break!/4
的宏快捷方式。
@spec break!(module(), atom(), arity(), non_neg_integer()) :: IEx.Pry.id()
在 module
、function
和 arity
中设置一个断点,并设置给定的 stops
数量。
此函数将检测给定的模块,并在内存中加载一个新版本,并在给定函数和元数处设置逐行断点。如果模块被重新编译,所有断点都会丢失。
当遇到断点时,IEx 会询问您是否要 pry
给定函数和元数。换句话说,这与 IEx.pry/0
类似,因为正在运行的进程成为 IEx 命令的评估器,并暂时更改为具有自定义组领导者。但是,与 IEx.pry/0
不同的是,源代码中的别名和导入在 shell 中不可用。
IEx 助手包括许多与断点相关的便利功能。下面列出了它们及其完整的模块,例如 IEx.Helpers.breaks/0
,但请记住,在 IEx 中可以直接调用它,例如 breaks()
。它们是
IEx.Helpers.break!/2
- 为给定的Mod.fun/arity
设置断点IEx.Helpers.break!/4
- 为给定的模块、函数、元数设置断点IEx.Helpers.breaks/0
- 打印所有断点及其 IDIEx.Helpers.continue/0
- 继续执行,直到在同一个 shell 中找到下一个断点IEx.Helpers.n/0
- 跳转到当前断点的下一行IEx.Helpers.next/0
- 与上面相同IEx.Helpers.open/0
- 在当前断点处打开编辑器IEx.Helpers.remove_breaks/0
- 删除所有模块中的所有断点IEx.Helpers.remove_breaks/1
- 删除给定模块中的所有断点IEx.Helpers.reset_break/1
- 将给定 ID 上的停止次数设置为零IEx.Helpers.reset_break/3
- 将给定模块、函数、元数上的停止次数设置为零IEx.Helpers.respawn/0
- 启动一个新的 shell(断点将再次请求权限)IEx.Helpers.whereami/1
- 显示当前位置
默认情况下,断点中的停止次数为 1。任何后续调用都不会停止代码执行,除非设置了另一个断点。
或者,可以通过传递 stops
参数来增加停止次数。可以使用 IEx.Helpers.reset_break/1
和 IEx.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
使用指定的 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 pry
,iex
将设置此函数作为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 个字符。