查看源码 argparse (stdlib v6.2)
命令行参数解析器。
此模块实现了命令行解析器。解析器操作的命令和参数表示为树状结构。命令是分支,参数是树的叶子。解析器总是从根命令开始,根命令的名称为 progname
(启动 Erlang 的程序的名称)。
命令规范
可以包含每个命令的处理程序定义和多个参数规范。当解析器成功时,argparse
会调用匹配的处理程序,并传递从命令行提取的参数。参数可以是位置参数(在命令行中占据特定位置)和可选参数(位于任何位置,但以指定的字符为前缀)。
argparse
会自动生成帮助和用法消息。当用户向程序提供无效参数时,它也会发出错误。
快速开始
argparse
旨在与 escript
一起使用。下面的示例是一个功能齐全的 Erlang 程序,它接受两个命令行参数并打印它们的乘积。
#!/usr/bin/env escript
main(Args) ->
argparse:run(Args, cli(), #{progname => mul}).
cli() ->
#{
arguments => [
#{name => left, type => integer},
#{name => right, type => integer}
],
handler =>
fun (#{left := Left, right := Right}) ->
io:format("~b~n", [Left * Right])
end
}.
使用不带参数的脚本运行会导致错误,并附带用法信息。
cli
函数定义了一个带有嵌入式处理程序的命令,该处理程序接受一个映射。映射的键是由命令的 argument
字段定义的参数名称,示例中为 left
和 right
。值取自命令行,并按照类型规范的要求转换为整数。示例中的两个参数都是必需的(因此被定义为位置参数)。
命令层次结构
一个命令可以包含嵌套命令,形成一个层次结构。在上层命令中定义的参数会自动添加到所有嵌套命令中。嵌套命令示例(假设 progname
是 nested
)
cli() ->
#{
%% top level argument applicable to all commands
arguments => [#{name => top}],
commands => #{
"first" => #{
%% argument applicable to "first" command and
%% all commands nested into "first"
arguments => [#{name => mid}],
commands => #{
"second" => #{
%% argument only applicable for "second" command
arguments => [#{name => bottom}],
handler => fun (A) -> io:format("~p~n", [A]) end
}
}
}
}
}.
在上面的示例中,定义了一个 3 级层次结构。第一个是脚本本身 (nested
),它只接受一个参数 top
。由于它没有关联的处理程序,run/3
将不会接受省略嵌套命令选择的用户输入。对于此示例,用户必须在命令行中提供 5 个参数,其中两个是命令名称,另外 3 个是必需的位置参数。
./nested.erl one first second two three
#{top => "one",mid => "two",bottom => "three"}
命令优先于位置参数值。在上面的示例中,命令和位置参数是交错的,并且 argparse
首先匹配命令名称。
参数
argparse
支持位置参数和可选参数。可选参数(简称选项)必须以特殊字符为前缀(在所有操作系统上,-
是默认值)。选项和位置参数都有一个或多个关联的值。请参阅 参数规范
以了解有关支持的组合的更多详细信息。
在用户输入中,短选项可以与其值连接。长选项支持用 =
分隔的值。请考虑以下定义
cli() ->
#{
arguments => [
#{name => long, long => "-long"},
#{name => short, short => $s}
],
handler => fun (Args) -> io:format("~p~n", [Args]) end
}.
运行 ./args --long=VALUE
会打印 #{long => "VALUE"}
,运行 ./args -sVALUE
会打印 #{short => "VALUE"}
argparse
支持布尔标志连接:可以将 -r -f -v
缩短为 -rfv
。
不支持缩短的选项名称:即使可以明确找到这样的选项,也不能使用 --my-argum
代替 --my-argument-name
。
摘要
类型
参数映射是将参数名称映射到从命令行提取的值的映射。它被传递给匹配的命令处理程序。如果省略了一个参数,但指定了默认值,则会将其添加到映射中。如果未指定默认值,并且命令行中不存在该参数,则结果映射中不存在相应的键。
定义应用于从用户输入检索的字符串的类型转换。如果转换成功,则使用可选的 Choices
或最小值和最大值(仅适用于整数和浮点值)来验证结果值。可以使用正则表达式来验证字符串和二进制值。可以定义自定义类型转换函数,该函数接受一个字符串并返回 Erlang 术语。如果此函数引发带有 badarg
原因的错误,则该参数将被视为无效。
参数规范。定义在 参数映射
中返回的单个命名参数。唯一必需的字段是 name
,所有其他字段都有默认值。
用户定义的帮助模板,用于在命令用法中打印。元组的第一个元素必须是一个字符串。它作为用法标题的一部分打印。元组的第二个元素可以是包含字符串的列表、type
和 default
原子,或者必须返回字符串的用户定义函数。纯字符串应作为列表包装,例如 ["字符串是嵌套的"]
。
参数名称用于填充参数映射。
嵌套命令的路径。第一个元素始终是 progname
,后续元素是嵌套的命令名称。
命令规范。可以包含嵌套命令,形成一个层次结构。
用户定义的帮助模板。使用此选项可以混合自定义和预定义的用法文本。帮助模板可以包含 Unicode 字符串和以下原子
从 parse/2,3
返回。包含从命令行提取的参数、嵌套命令的路径(如果有)以及当解析器成功完成时被考虑的(可能是嵌套的)命令规范。预计该命令包含一个处理程序定义,该定义将被调用并传递参数映射。
当用户输入无法根据命令规范进行解析时,从 parse/2,3
返回。
更改解析器行为的选项。
函数
为 解析器错误
生成人类可读的文本。不包括帮助/用法信息,并且不提供本地化。
等效于 help/2
。
为提供的命令或指定 command
选项时为任何嵌套命令生成帮助/用法信息文本。参数按照 Command
中指定的相同顺序显示。不提供本地化。期望设置 progname
,否则默认为 init:get_argument(progname)
的返回值。
根据命令规范解析命令行参数。如果命令规范无效,则引发异常。使用 erl_error:format_exception/3,4
查看更友好的消息。无效的命令行输入不会引发异常,而是使 parse/2,3
返回一个元组 {error, parser_error()}
。
解析命令行参数并调用匹配的命令处理程序。如果命令规范或用户提供的命令行输入中存在任何错误,则打印人类可读的错误、帮助/用法信息,并以代码 1 停止模拟器。
类型
-type arg_map() :: #{argument_name() => term()}.
参数映射是将参数名称映射到从命令行提取的值的映射。它被传递给匹配的命令处理程序。如果省略了一个参数,但指定了默认值,则会将其添加到映射中。如果未指定默认值,并且命令行中不存在该参数,则结果映射中不存在相应的键。
-type arg_type() :: boolean | float | {float, Choice :: [float()]} | {float, [{min, float()} | {max, float()}]} | integer | {integer, Choices :: [integer()]} | {integer, [{min, integer()} | {max, integer()}]} | string | {string, Choices :: [string()]} | {string, Re :: string()} | {string, Re :: string(), ReOptions :: [term()]} | binary | {binary, Choices :: [binary()]} | {binary, Re :: binary()} | {binary, Re :: binary(), ReOptions :: [term()]} | atom | {atom, Choices :: [atom()]} | {atom, unsafe} | {custom, fun((string()) -> term())}.
定义应用于从用户输入检索的字符串的类型转换。如果转换成功,则使用可选的 Choices
或最小值和最大值(仅适用于整数和浮点值)来验证结果值。可以使用正则表达式来验证字符串和二进制值。可以定义自定义类型转换函数,该函数接受一个字符串并返回 Erlang 术语。如果此函数引发带有 badarg
原因的错误,则该参数将被视为无效。
-type argument() :: #{name := argument_name(), short => char(), long => string(), required => boolean(), default => term(), type => arg_type(), action => store | {store, term()} | append | {append, term()} | count | extend, nargs => pos_integer() | 'maybe' | {'maybe', term()} | list | nonempty_list | all, help => hidden | unicode:chardata() | argument_help()}.
参数规范。定义在 参数映射
中返回的单个命名参数。唯一必需的字段是 name
,所有其他字段都有默认值。
如果指定了 short
或 long
字段中的任何一个,则该参数将被视为可选参数。可选参数没有特定的顺序,并且可以出现在命令行中的任何位置。位置参数的顺序与它们在命令规范的参数列表中出现的顺序相同。
默认情况下,所有位置参数都必须出现在命令行中。否则,解析器将返回错误。但是,可以省略选项,在这种情况下,生成的参数映射将包含默认值,或者根本没有该键。
name
- 在解析的参数映射中设置参数名称。如果未定义help
,则该名称也用于生成默认用法消息。short
- 定义可选参数的短形式(单个字符)。%% Define a command accepting argument named myarg, with short form $a: 1> Cmd = #{arguments => [#{name => myarg, short => $a}]}. %% Parse command line "-a str": 2> {ok, ArgMap, _, _} = argparse:parse(["-a", "str"], Cmd), ArgMap. #{myarg => "str"} %% Option value can be concatenated with the switch: "-astr" 3> {ok, ArgMap, _, _} = argparse:parse(["-astr"], Cmd), ArgMap. #{myarg => "str"}
默认情况下,所有选项都期望选项开关后跟单个值。唯一的例外是布尔类型的选项。
long
- 定义可选参数的长形式。1> Cmd = #{arguments => [#{name => myarg, long => "name"}]}. %% Parse command line "-name Erlang": 2> {ok, ArgMap, _, _} = argparse:parse(["-name", "Erlang"], Cmd), ArgMap. #{myarg => "Erlang"} %% Or use "=" to separate the switch and the value: 3> {ok, ArgMap, _, _} = argparse:parse(["-name=Erlang"], Cmd), ArgMap. #{myarg => "Erlang"}
如果未定义
short
或long
,则该参数将被视为位置参数。required
- 强制解析器期望该参数出现在命令行中。默认情况下,所有位置参数都是必需的,而所有选项都不是。default
- 指定如果命令行中未提供该值,则在解析的参数映射中放入的默认值。1> argparse:parse([], #{arguments => [#{name => myarg, short => $m}]}). {ok,#{}, ... 2> argparse:parse([], #{arguments => [#{name => myarg, short => $m, default => "def"}]}). {ok,#{myarg => "def"}, ...
type
- 定义类型转换和验证例程。 默认值为string
,表示不进行转换。nargs
- 定义从命令行消耗的后续参数的数量。默认情况下,解析器会消耗下一个参数,并根据指定的类型将其转换为 Erlang 项。pos_integer/0
- 消耗正好此数量的位置参数,如果数量不足则失败。参数映射中的值包含一个长度恰好为此长度的列表。例如,定义一个期望 3 个整数值的位置参数1> Cmd = #{arguments => [#{name => ints, type => integer, nargs => 3}]}, argparse:parse(["1", "2", "3"], Cmd). {ok, #{ints => [1, 2, 3]}, ...
另一个示例,定义一个接受
-env
选项并期望两个字符串参数1> Cmd = #{arguments => [#{name => env, long => "env", nargs => 2}]}, argparse:parse(["-env", "key", "value"], Cmd). {ok, #{env => ["key", "value"]}, ...
list
- 消耗所有后续参数,直到遇到下一个选项(以选项前缀开头)。可能会在参数映射中添加一个空列表。1> Cmd = #{arguments => [ #{name => nodes, long => "nodes", nargs => list}, #{name => verbose, short => $v, type => boolean} ]}, argparse:parse(["-nodes", "one", "two", "-v"], Cmd). {ok, #{nodes => ["one", "two"], verbose => true}, ...
nonempty_list
- 与list
相同,但期望至少一个参数。如果以下命令行参数是选项开关(以选项前缀开头),则返回错误。'maybe'
- 如果下一个命令行参数不以选项前缀开头,则消耗它。否则,将默认值添加到参数映射中。1> Cmd = #{arguments => [ #{name => level, short => $l, nargs => 'maybe', default => "error"}, #{name => verbose, short => $v, type => boolean} ]}, argparse:parse(["-l", "info", "-v"], Cmd). {ok,#{level => "info",verbose => true}, ... %% When "info" is omitted, argument maps receives the default "error" 2> argparse:parse(["-l", "-v"], Cmd). {ok,#{level => "error",verbose => true}, ...
{'maybe', term()}
- 如果下一个命令行参数不以选项前缀开头,则消耗它。否则,将指定的 Erlang 项添加到参数映射中。all
- 将所有剩余的命令行参数折叠到一个列表中,忽略任何选项前缀或开关。适用于将参数代理到另一个命令行实用程序中。1> Cmd = #{arguments => [ #{name => verbose, short => $v, type => boolean}, #{name => raw, long => "-", nargs => all} ]}, argparse:parse(["-v", "--", "-kernel", "arg", "opt"], Cmd). {ok,#{raw => ["-kernel","arg","opt"],verbose => true}, ...
action
- 定义在命令行中找到参数时要执行的操作。默认操作是store
。store
- 将值存储在参数映射中。覆盖先前写入的值。1> Cmd = #{arguments => [#{name => str, short => $s}]}, argparse:parse(["-s", "one", "-s", "two"], Cmd). {ok, #{str => "two"}, ...
{store, term()}
- 存储指定的项,而不是从命令行读取值。1> Cmd = #{arguments => [#{name => str, short => $s, action => {store, "two"}}]}, argparse:parse(["-s"], Cmd). {ok, #{str => "two"}, ...
append
- 追加重复出现的参数,而不是覆盖。1> Cmd = #{arguments => [#{name => node, short => $n, action => append}]}, argparse:parse(["-n", "one", "-n", "two", "-n", "three"], Cmd). {ok, #{node => ["one", "two", "three"]}, ... %% Always produces a list - even if there is one occurrence 2> argparse:parse(["-n", "one"], Cmd). {ok, #{node => ["one"]}, ...
{append, term()}
- 与append
相同,但不是从命令行消耗参数,而是追加提供的term/0
。count
- 将计数器作为值放入参数映射中。适用于实现详细程度选项1> Cmd = #{arguments => [#{name => verbose, short => $v, action => count}]}, argparse:parse(["-v"], Cmd). {ok, #{verbose => 1}, ... 2> argparse:parse(["-vvvv"], Cmd). {ok, #{verbose => 4}, ...
extend
- 与append
的工作方式相同,但会展平结果列表。仅当nargs
设置为list
、nonempty_list
、all
或pos_integer/0
时有效。1> Cmd = #{arguments => [#{name => duet, short => $d, nargs => 2, action => extend}]}, argparse:parse(["-d", "a", "b", "-d", "c", "d"], Cmd). {ok, #{duet => ["a", "b", "c", "d"]}, ... %% 'append' would result in {ok, #{duet => [["a", "b"],["c", "d"]]},
help
- 为参数指定帮助/用法文本。argparse
根据参数名称、类型和默认值提供自动生成,但为了更好的可用性,建议使用适当的描述。将此字段设置为hidden
会禁止此参数的用法输出。
-type argument_help() :: {unicode:chardata(), [unicode:chardata() | type | default] | fun(() -> unicode:chardata())}.
用户定义的帮助模板,用于在命令用法中打印。元组的第一个元素必须是一个字符串。它作为用法标题的一部分打印。元组的第二个元素可以是包含字符串的列表、type
和 default
原子,或者必须返回字符串的用户定义函数。纯字符串应作为列表包装,例如 ["字符串是嵌套的"]
。
参数名称用于填充参数映射。
-type cmd_path() :: [string()].
嵌套命令的路径。第一个元素始终是 progname
,后续元素是嵌套的命令名称。
-type command() :: #{commands => #{string() => command()}, arguments => [argument()], help => hidden | unicode:chardata() | command_help(), handler => handler()}.
命令规范。可以包含嵌套命令,形成一个层次结构。
commands
- 嵌套命令的映射。键必须是与命令行输入匹配的字符串。基本实用程序不需要指定任何嵌套命令。arguments
- 此命令以及层次结构中所有嵌套命令接受的参数列表。help
- 为此命令指定帮助/用法文本。传递hidden
以从用法输出中删除此命令。handler
- 指定当解析器成功时由run/3
调用的回调函数。
-type command_help() :: [unicode:chardata() | usage | commands | arguments | options].
用户定义的帮助模板。使用此选项可以混合自定义和预定义的用法文本。帮助模板可以包含 Unicode 字符串和以下原子
usage - 格式化的命令行用法文本,例如
rm [-rf] <directory>
。commands - 展开的子命令列表。
arguments - 位置参数的详细描述。
options - 可选参数的详细描述。
-type handler() :: optional | fun((arg_map()) -> term()) | {module(), Fn :: atom()} | {fun(() -> term()), term()} | {module(), atom(), term()}.
命令处理程序规范。由 run/3
在成功解析后调用。
{Module :: module(), Function :: atom()}
- 从Module
导出的名为Function
的函数,接受参数映射
。{fun(() -> term()), Default :: term()}
- 接受与此命令的arguments
列表中一样多的参数的函数。解析的映射中缺少的参数将替换为Default
。这是公开现有函数的便捷方式。1> Cmd = #{arguments => [ #{name => x, type => float}, #{name => y, type => float, short => $p}], handler => {fun math:pow/2, 1}}, argparse:run(["2", "-p", "3"], Cmd, #{}). 8.0 %% default term 1 is passed to math:pow/2 2> argparse:run(["2"], Cmd, #{}). 2.0
{Module :: module(), Function :: atom(), Default :: term()}
- 从Module
导出的名为Function
的函数,接受为此命令定义的尽可能多的参数。解析的映射中缺少的参数将替换为Default
。实际上,它只是与上面代码中演示的功能相同的不同语法。
-type parse_result() :: {ok, arg_map(), Path :: cmd_path(), command()} | {error, parser_error()}.
从 parse/2,3
返回。包含从命令行提取的参数、嵌套命令的路径(如果有)以及当解析器成功完成时被考虑的(可能是嵌套的)命令规范。预计该命令包含一个处理程序定义,该定义将被调用并传递参数映射。
-type parser_error() :: {Path :: cmd_path(), Expected :: argument() | undefined, Actual :: string() | undefined, Details :: unicode:chardata()}.
当用户输入无法根据命令规范进行解析时,从 parse/2,3
返回。
第一个元素是解析器检测到错误时考虑的命令路径。第二个元素 Expected
是导致错误的参数规范。它可能是 undefined
,这意味着 Actual
参数在当前命令的参数列表中没有相应的规范。
当 Actual
设置为 undefined
时,表示命令行中缺少必需的参数。如果 Expected
和 Actual
都有值,则表示验证错误。
使用 format_error/1
生成人类可读的错误描述,除非需要提供本地化的错误消息。
-type parser_options() :: #{prefixes => [char()], default => term(), progname => string() | atom(), command => cmd_path(), columns => pos_integer()}.
更改解析器行为的选项。
prefixes
- 更改选项前缀(默认为-
)。default
- 指定所有可选参数的默认值。设置此字段后,生成的参数映射将包含所有参数名称。方便在处理函数中轻松匹配参数映射。progname
- 指定程序(根命令)名称。作为命令路径的第一个元素返回,并打印在帮助/用法文本中。建议设置此值,否则默认值将通过init:get_argument(progname)
确定,并且通常设置为erl
而不是实际的脚本名称。command
- 指定help/2
的嵌套命令路径。适用于限制具有多个命令的复杂实用程序的输出,并由默认错误处理逻辑使用。columns
- 指定help/2
的帮助/用法文本宽度(字符)。默认值为 80。
函数
-spec format_error(Reason :: parser_error()) -> unicode:chardata().
为 解析器错误
生成人类可读的文本。不包括帮助/用法信息,并且不提供本地化。
等效于 help/2
。
-spec help(command(), parser_options()) -> unicode:chardata().
为提供的命令或指定 command
选项时为任何嵌套命令生成帮助/用法信息文本。参数按照 Command
中指定的相同顺序显示。不提供本地化。期望设置 progname
,否则默认为 init:get_argument(progname)
的返回值。
-spec parse(Args :: [string()], command()) -> parse_result().
等效于 parse/3
。
-spec parse(Args :: [string()], command(), Options :: parser_options()) -> parse_result().
根据命令规范解析命令行参数。如果命令规范无效,则引发异常。使用 erl_error:format_exception/3,4
查看更友好的消息。无效的命令行输入不会引发异常,而是使 parse/2,3
返回一个元组 {error, parser_error()}
。
此函数不调用命令处理程序。
-spec run(Args :: [string()], command(), parser_options()) -> term().
解析命令行参数并调用匹配的命令处理程序。如果命令规范或用户提供的命令行输入中存在任何错误,则打印人类可读的错误、帮助/用法信息,并以代码 1 停止模拟器。
警告
此函数旨在用作独立
escript
的入口点。因此,它会停止任何检测到的错误模拟器。请勿通过远程过程调用使用此函数,否则可能会导致远程节点意外关闭。