查看源代码 OptionParser (Elixir v1.16.2)

用于解析命令行参数的函数。

在调用命令时,可以传递命令行选项来修改命令的行为。在本文档中,这些选项被称为“开关”,在其他情况下,它们可能被称为“标志”或简称“选项”。开关可以被赋予一个值,也称为“参数”。

此模块中的主要函数是 parse/2,它将命令行选项和参数列表解析为关键字列表。

iex> OptionParser.parse(["--debug"], strict: [debug: :boolean])
{[debug: true], [], []}

OptionParser 提供了一些开箱即用的便利功能,例如别名和对否定开关的自动处理。

The parse_head/2 function is an alternative to parse/2 which stops parsing as soon as it finds a value that is not a switch nor a value for a previous switch.

此模块还提供低级函数,例如 next/2,用于手动解析开关,以及 split/1to_argv/1 用于从字符串解析开关并将其转换为字符串。

摘要

函数

解析单个选项的低级函数。

argv 解析为关键字列表。

parse/2 相同,但如果提供任何无效选项,则会引发 OptionParser.ParseError 异常。

parse/2 类似,但只解析 argv 的头部;一旦找到非开关,它就会停止解析。

parse_head/2 相同,但如果提供任何无效选项,则会引发 OptionParser.ParseError 异常。

将字符串拆分为 argv/0 块。

接收键值可枚举并将其转换为 argv/0.

类型

@type argv() :: [String.t()]
@type errors() :: [{String.t(), String.t() | nil}]
@type options() :: [
  switches: keyword(),
  strict: keyword(),
  aliases: keyword(),
  allow_nonexistent_atoms: boolean(),
  return_separator: boolean()
]
@type parsed() :: keyword()

函数

链接到此函数

next(argv, opts \\ [])

查看源代码
@spec next(argv(), options()) ::
  {:ok, key :: atom(), value :: term(), argv()}
  | {:invalid, String.t(), String.t() | nil, argv()}
  | {:undefined, String.t(), String.t() | nil, argv()}
  | {:error, argv()}

解析单个选项的低级函数。

它接受与 parse/2parse_head/2 相同的选项,因为这两个函数都是基于此函数构建的。此函数可能会返回

  • {:ok, key, value, rest} - 成功解析了带有 value 的选项 key

  • {:invalid, key, value, rest} - 选项 key 无效,带有 value(当值无法根据开关类型解析时返回)

  • {:undefined, key, value, rest} - 选项 key 未定义(在严格模式下返回,当开关未知或在不存在的原子上时返回)

  • {:error, rest} - 给定 argv 的头部没有开关

链接到此函数

parse(argv, opts \\ [])

查看源代码
@spec parse(argv(), options()) :: {parsed(), argv(), errors()}

argv 解析为关键字列表。

它返回一个三元素元组,形式为 {parsed, args, invalid},其中

  • parsed 是一个关键字列表,包含解析的开关,其中包含 {switch_name, value} 元组;switch_name 是表示开关名称的原子,而 value 是根据 opts 解析的该开关的值(有关更多信息,请参阅“示例”部分)
  • argsargv 中剩余参数的列表,作为字符串
  • invalid 是一个无效选项的列表,作为 {option_name, value},其中 option_name 是原始选项,而 valuenil(如果选项不受期望)或字符串值(如果值没有相应的选项的期望类型)

Elixir 将开关转换为带下划线的原子,因此 --source-path 变为 :source_path。这样做是为了更好地适应 Elixir 的约定。但是,这意味着开关不能包含下划线,并且包含下划线的开关始终在无效开关列表中返回。

在解析时,通常列出开关及其预期的类型

iex> OptionParser.parse(["--debug"], strict: [debug: :boolean])
{[debug: true], [], []}

iex> OptionParser.parse(["--source", "lib"], strict: [source: :string])
{[source: "lib"], [], []}

iex> OptionParser.parse(
...>   ["--source-path", "lib", "test/enum_test.exs", "--verbose"],
...>   strict: [source_path: :string, verbose: :boolean]
...> )
{[source_path: "lib", verbose: true], ["test/enum_test.exs"], []}

我们将在下面探讨选项解析器的有效开关和操作模式。

选项

支持以下选项

  • :switches:strict - 请参阅下面的“开关定义”部分
  • :allow_nonexistent_atoms - 请参阅下面的“解析未知开关”部分
  • :aliases - 请参阅下面的“别名”部分
  • :return_separator - 请参阅下面的“返回分隔符”部分

开关定义

可以通过以下两种选项之一指定开关

  • :strict - 定义严格的开关及其类型。在 argv 中,任何未在列表中指定的开关都会在无效选项列表中返回。这是解析选项的首选方法。

  • :switches - 定义开关及其类型。此函数仍然尝试解析不在此列表中的开关。

这两个选项都接受关键字列表,其中键是定义开关名称的原子,值是开关的 type(有关更多信息,请参阅下面的“类型”部分)。

请注意,您应该只提供 :switches:strict 选项。如果您同时提供两者,则会引发 ArgumentError 异常。

类型

OptionParser 解析的开关可以接受零个或一个参数。

以下开关类型不接受参数

  • :boolean - 在给出时将值设置为 true(另请参阅下面的“否定开关”部分)
  • :count - 统计开关被给出的次数

以下开关接受一个参数

  • :integer - 将值解析为整数
  • :float - 将值解析为浮点数
  • :string - 将值解析为字符串

如果开关无法根据给定的类型解析,则它将在无效选项列表中返回。

修饰符

开关可以使用修饰符指定,这些修饰符会改变它们的行为。支持以下修饰符

  • :keep - 保留重复元素,而不是覆盖它们;适用于除 :count 之外的所有类型。指定 switch_name: :keep 假设 :switch_name 的类型将为 :string

要将 :keep:string 以外的类型一起使用,请使用列表作为开关的类型。例如:[foo: [:integer, :keep]]

否定开关

如果指定开关 SWITCH 的类型为 :boolean,则也可以将其作为 --no-SWITCH 传递,这将使选项设置为 false

iex> OptionParser.parse(["--no-op", "path/to/file"], switches: [op: :boolean])
{[op: false], ["path/to/file"], []}

解析未知开关

当给出 :switches 选项时,OptionParser 将尝试解析未知开关

iex> OptionParser.parse(["--debug"], switches: [key: :string])
{[debug: true], [], []}

即使我们没有在开关列表中指定 --debug,它也是返回选项的一部分。这也会有效

iex> OptionParser.parse(["--debug", "value"], switches: [key: :string])
{[debug: "value"], [], []}

带有值的开关将被赋予该值,作为字符串。没有参数的开关将自动设置为 true。由于我们无法断言开关值的类型,因此最好使用 :strict 选项,该选项只接受已知开关并始终验证其类型。

如果您确实要解析未知开关,请记住 Elixir 会将开关转换为原子。由于原子不会被垃圾回收,OptionParser 只会解析转换为运行时使用的原子的开关,以避免泄漏原子。例如,下面的代码将丢弃 --option-parser-example 开关,因为 :option_parser_example 原子在任何地方都没有使用

OptionParser.parse(["--option-parser-example"], switches: [debug: :boolean])
# The :option_parser_example atom is not used anywhere below

但是,下面的代码将有效,只要 :option_parser_example 原子在稍后(或更早)在 **同一个模块** 中使用。例如

{opts, _, _} = OptionParser.parse(["--option-parser-example"], switches: [debug: :boolean])
# ... then somewhere in the same module you access it ...
opts[:option_parser_example]

换句话说,Elixir 仅解析运行时使用的选项,忽略所有其他选项。如果您想解析所有开关,无论它们是否存在,都可以通过传递 allow_nonexistent_atoms: true 作为选项来强制创建原子。谨慎使用此选项。它仅在您构建接收动态命名参数的命令行应用程序时有用,必须在长期运行的系统中避免。

别名

可以在 :aliases 选项中指定一组别名

iex> OptionParser.parse(["-d"], aliases: [d: :debug], strict: [debug: :boolean])
{[debug: true], [], []}

示例

以下是一些使用不同类型和修饰符的示例

iex> OptionParser.parse(["--unlock", "path/to/file"], strict: [unlock: :boolean])
{[unlock: true], ["path/to/file"], []}

iex> OptionParser.parse(
...>   ["--unlock", "--limit", "0", "path/to/file"],
...>   strict: [unlock: :boolean, limit: :integer]
...> )
{[unlock: true, limit: 0], ["path/to/file"], []}

iex> OptionParser.parse(["--limit", "3"], strict: [limit: :integer])
{[limit: 3], [], []}

iex> OptionParser.parse(["--limit", "xyz"], strict: [limit: :integer])
{[], [], [{"--limit", "xyz"}]}

iex> OptionParser.parse(["--verbose"], switches: [verbose: :count])
{[verbose: 1], [], []}

iex> OptionParser.parse(["-v", "-v"], aliases: [v: :verbose], strict: [verbose: :count])
{[verbose: 2], [], []}

iex> OptionParser.parse(["--unknown", "xyz"], strict: [])
{[], ["xyz"], [{"--unknown", nil}]}

iex> OptionParser.parse(
...>   ["--limit", "3", "--unknown", "xyz"],
...>   switches: [limit: :integer]
...> )
{[limit: 3, unknown: "xyz"], [], []}

iex> OptionParser.parse(
...>   ["--unlock", "path/to/file", "--unlock", "path/to/another/file"],
...>   strict: [unlock: :keep]
...> )
{[unlock: "path/to/file", unlock: "path/to/another/file"], [], []}

返回分隔符

分隔符 -- 表示不再处理选项。默认情况下,分隔符不会作为参数的一部分返回,但这可以通过 :return_separator 选项更改

iex> OptionParser.parse(["--", "lib"], return_separator: true, strict: [])
{[], ["--", "lib"], []}

iex> OptionParser.parse(["--no-halt", "--", "lib"], return_separator: true, switches: [halt: :boolean])
{[halt: false], ["--", "lib"], []}

iex> OptionParser.parse(["script.exs", "--no-halt", "--", "foo"], return_separator: true, switches: [halt: :boolean])
{[{:halt, false}], ["script.exs", "--", "foo"], []}
链接到此函数

parse!(argv, opts \\ [])

查看源代码
@spec parse!(argv(), options()) :: {parsed(), argv()}

parse/2 相同,但如果提供任何无效选项,则会引发 OptionParser.ParseError 异常。

如果没有错误,则返回一个 {parsed, rest} 元组,其中

  • parsed 是解析的开关列表(与 parse/2 中相同)
  • rest 是参数列表(与 parse/2 中相同)

示例

iex> OptionParser.parse!(["--debug", "path/to/file"], strict: [debug: :boolean])
{[debug: true], ["path/to/file"]}

iex> OptionParser.parse!(["--limit", "xyz"], strict: [limit: :integer])
** (OptionParser.ParseError) 1 error found!
--limit : Expected type integer, got "xyz"

iex> OptionParser.parse!(["--unknown", "xyz"], strict: [])
** (OptionParser.ParseError) 1 error found!
--unknown : Unknown option

iex> OptionParser.parse!(
...>   ["-l", "xyz", "-f", "bar"],
...>   switches: [limit: :integer, foo: :integer],
...>   aliases: [l: :limit, f: :foo]
...> )
** (OptionParser.ParseError) 2 errors found!
-l : Expected type integer, got "xyz"
-f : Expected type integer, got "bar"
链接到此函数

parse_head(argv, opts \\ [])

查看源代码
@spec parse_head(argv(), options()) :: {parsed(), argv(), errors()}

parse/2 类似,但只解析 argv 的头部;一旦找到非开关,它就会停止解析。

有关更多信息,请参阅 parse/2

示例

iex> OptionParser.parse_head(
...>   ["--source", "lib", "test/enum_test.exs", "--verbose"],
...>   switches: [source: :string, verbose: :boolean]
...> )
{[source: "lib"], ["test/enum_test.exs", "--verbose"], []}

iex> OptionParser.parse_head(
...>   ["--verbose", "--source", "lib", "test/enum_test.exs", "--unlock"],
...>   switches: [source: :string, verbose: :boolean, unlock: :boolean]
...> )
{[verbose: true, source: "lib"], ["test/enum_test.exs", "--unlock"], []}
链接到此函数

parse_head!(argv, opts \\ [])

查看源代码
@spec parse_head!(argv(), options()) :: {parsed(), argv()}

parse_head/2 相同,但如果提供任何无效选项,则会引发 OptionParser.ParseError 异常。

如果没有错误,则返回一个 {parsed, rest} 元组,其中

示例

iex> OptionParser.parse_head!(
...>   ["--source", "lib", "path/to/file", "--verbose"],
...>   switches: [source: :string, verbose: :boolean]
...> )
{[source: "lib"], ["path/to/file", "--verbose"]}

iex> OptionParser.parse_head!(
...>   ["--number", "lib", "test/enum_test.exs", "--verbose"],
...>   strict: [number: :integer]
...> )
** (OptionParser.ParseError) 1 error found!
--number : Expected type integer, got "lib"

iex> OptionParser.parse_head!(
...>   ["--verbose", "--source", "lib", "test/enum_test.exs", "--unlock"],
...>   strict: [verbose: :integer, source: :integer]
...> )
** (OptionParser.ParseError) 2 errors found!
--verbose : Missing argument of type integer
--source : Expected type integer, got "lib"
@spec split(String.t()) :: argv()

将字符串拆分为 argv/0 块。

此函数将给定的 string 拆分为字符串列表,方式类似于许多 shell。

示例

iex> OptionParser.split("foo bar")
["foo", "bar"]

iex> OptionParser.split("foo \"bar baz\"")
["foo", "bar baz"]
链接到此函数

to_argv(enum, options \\ [])

查看源代码
@spec to_argv(Enumerable.t(), options()) :: argv()

接收键值可枚举并将其转换为 argv/0.

键必须是原子。具有 nil 值的键将被丢弃,布尔值将转换为 --key--no-key(如果值分别为 truefalse),所有其他值都将使用 to_string/1 进行转换。

建议将与 parse/2 传递的相同 options 集传递给 to_argv/2。某些开关只有在拥有 :switches 信息的情况下才能被正确重建。

示例

iex> OptionParser.to_argv(foo_bar: "baz")
["--foo-bar", "baz"]
iex> OptionParser.to_argv(bool: true, bool: false, discarded: nil)
["--bool", "--no-bool"]

某些开关将根据开关类型输出不同的值

iex> OptionParser.to_argv([number: 2], switches: [])
["--number", "2"]
iex> OptionParser.to_argv([number: 2], switches: [number: :count])
["--number", "--number"]