查看源码 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 字段定义的参数名称,示例中为 leftright。值取自命令行,并按照类型规范的要求转换为整数。示例中的两个参数都是必需的(因此被定义为位置参数)。

命令层次结构

一个命令可以包含嵌套命令,形成一个层次结构。在上层命令中定义的参数会自动添加到所有嵌套命令中。嵌套命令示例(假设 prognamenested

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,所有其他字段都有默认值。

用户定义的帮助模板,用于在命令用法中打印。元组的第一个元素必须是一个字符串。它作为用法标题的一部分打印。元组的第二个元素可以是包含字符串的列表、typedefault 原子,或者必须返回字符串的用户定义函数。纯字符串应作为列表包装,例如 ["字符串是嵌套的"]

参数名称用于填充参数映射。

嵌套命令的路径。第一个元素始终是 progname,后续元素是嵌套的命令名称。

命令规范。可以包含嵌套命令,形成一个层次结构。

用户定义的帮助模板。使用此选项可以混合自定义和预定义的用法文本。帮助模板可以包含 Unicode 字符串和以下原子

命令处理程序规范。由 run/3 在成功解析后调用。

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 停止模拟器。

类型

此类型的链接

arg_map()

查看源码 (自 OTP 26.0 起)
-type arg_map() :: #{argument_name() => term()}.

参数映射是将参数名称映射到从命令行提取的值的映射。它被传递给匹配的命令处理程序。如果省略了一个参数,但指定了默认值,则会将其添加到映射中。如果未指定默认值,并且命令行中不存在该参数,则结果映射中不存在相应的键。

此类型的链接

arg_type()

查看源码 (自 OTP 26.0 起)
-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 原因的错误,则该参数将被视为无效。

此类型的链接

argument()

查看源码 (自 OTP 26.0 起)
-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,所有其他字段都有默认值。

如果指定了 shortlong 字段中的任何一个,则该参数将被视为可选参数。可选参数没有特定的顺序,并且可以出现在命令行中的任何位置。位置参数的顺序与它们在命令规范的参数列表中出现的顺序相同。

默认情况下,所有位置参数都必须出现在命令行中。否则,解析器将返回错误。但是,可以省略选项,在这种情况下,生成的参数映射将包含默认值,或者根本没有该键。

  • 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"}

    如果未定义 shortlong,则该参数将被视为位置参数。

  • 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 设置为 listnonempty_listallpos_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 会禁止此参数的用法输出。

此类型的链接

argument_help()

查看源代码 (自 OTP 26.0 起)
-type argument_help() ::
          {unicode:chardata(), [unicode:chardata() | type | default] | fun(() -> unicode:chardata())}.

用户定义的帮助模板,用于在命令用法中打印。元组的第一个元素必须是一个字符串。它作为用法标题的一部分打印。元组的第二个元素可以是包含字符串的列表、typedefault 原子,或者必须返回字符串的用户定义函数。纯字符串应作为列表包装,例如 ["字符串是嵌套的"]

此类型的链接

argument_name()

查看源代码 (未导出) (自 OTP 26.0 起)
-type argument_name() :: atom() | string() | binary().

参数名称用于填充参数映射。

此类型的链接

cmd_path()

查看源代码 (自 OTP 26.0 起)
-type cmd_path() :: [string()].

嵌套命令的路径。第一个元素始终是 progname,后续元素是嵌套的命令名称。

此类型的链接

command()

查看源代码 (自 OTP 26.0 起)
-type command() ::
          #{commands => #{string() => command()},
            arguments => [argument()],
            help => hidden | unicode:chardata() | command_help(),
            handler => handler()}.

命令规范。可以包含嵌套命令,形成一个层次结构。

  • commands - 嵌套命令的映射。键必须是与命令行输入匹配的字符串。基本实用程序不需要指定任何嵌套命令。

  • arguments - 此命令以及层次结构中所有嵌套命令接受的参数列表。

  • help - 为此命令指定帮助/用法文本。传递 hidden 以从用法输出中删除此命令。

  • handler - 指定当解析器成功时由 run/3 调用的回调函数。

此类型的链接

command_help()

查看源代码 (未导出) (自 OTP 26.0 起)
-type command_help() :: [unicode:chardata() | usage | commands | arguments | options].

用户定义的帮助模板。使用此选项可以混合自定义和预定义的用法文本。帮助模板可以包含 Unicode 字符串和以下原子

  • usage - 格式化的命令行用法文本,例如 rm [-rf] <directory>

  • commands - 展开的子命令列表。

  • arguments - 位置参数的详细描述。

  • options - 可选参数的详细描述。

此类型的链接

handler()

查看源代码 (自 OTP 26.0 起)
-type handler() ::
          optional |
          fun((arg_map()) -> term()) |
          {module(), Fn :: atom()} |
          {fun(() -> term()), term()} |
          {module(), atom(), term()}.

命令处理程序规范。由 run/3 在成功解析后调用。

  • fun((arg_map()) -> term()) - 接受 参数映射 的函数。请参阅快速入门部分中的基本示例。

  • {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。实际上,它只是与上面代码中演示的功能相同的不同语法。

此类型的链接

parse_result()

查看源代码 (未导出) (自 OTP 26.0 起)
-type parse_result() :: {ok, arg_map(), Path :: cmd_path(), command()} | {error, parser_error()}.

parse/2,3 返回。包含从命令行提取的参数、嵌套命令的路径(如果有)以及当解析器成功完成时被考虑的(可能是嵌套的)命令规范。预计该命令包含一个处理程序定义,该定义将被调用并传递参数映射。

此类型的链接

parser_error()

查看源代码 (未导出) (自 OTP 26.0 起)
-type parser_error() ::
          {Path :: cmd_path(),
           Expected :: argument() | undefined,
           Actual :: string() | undefined,
           Details :: unicode:chardata()}.

当用户输入无法根据命令规范进行解析时,从 parse/2,3 返回。

第一个元素是解析器检测到错误时考虑的命令路径。第二个元素 Expected 是导致错误的参数规范。它可能是 undefined,这意味着 Actual 参数在当前命令的参数列表中没有相应的规范。

Actual 设置为 undefined 时,表示命令行中缺少必需的参数。如果 ExpectedActual 都有值,则表示验证错误。

使用 format_error/1 生成人类可读的错误描述,除非需要提供本地化的错误消息。

此类型的链接

parser_options()

查看源代码 (未导出) (自 OTP 26.0 起)
-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。

函数

链接到此函数

format_error(Reason)

查看源代码 (自 OTP 26.0 起)
-spec format_error(Reason :: parser_error()) -> unicode:chardata().

解析器错误 生成人类可读的文本。不包括帮助/用法信息,并且不提供本地化。

链接到此函数

help(Command)

查看源代码 (自 OTP 26.0 起)
-spec help(command()) -> string().

等效于 help/2

链接到此函数

help(Command, Options)

查看源代码 (自 OTP 26.0 起)
-spec help(command(), parser_options()) -> unicode:chardata().

为提供的命令或指定 command 选项时为任何嵌套命令生成帮助/用法信息文本。参数按照 Command 中指定的相同顺序显示。不提供本地化。期望设置 progname,否则默认为 init:get_argument(progname) 的返回值。

链接到此函数

parse(Args, Command)

查看源代码 (自 OTP 26.0 起)
-spec parse(Args :: [string()], command()) -> parse_result().

等效于 parse/3

链接到此函数

parse(Args, Command, Options)

查看源代码 (自 OTP 26.0 起)
-spec parse(Args :: [string()], command(), Options :: parser_options()) -> parse_result().

根据命令规范解析命令行参数。如果命令规范无效,则引发异常。使用 erl_error:format_exception/3,4 查看更友好的消息。无效的命令行输入不会引发异常,而是使 parse/2,3 返回一个元组 {error, parser_error()}

此函数不调用命令处理程序。

链接到此函数

run(Args, Command, Options)

查看源代码 (自 OTP 26.0 起)
-spec run(Args :: [string()], command(), parser_options()) -> term().

解析命令行参数并调用匹配的命令处理程序。如果命令规范或用户提供的命令行输入中存在任何错误,则打印人类可读的错误、帮助/用法信息,并以代码 1 停止模拟器。

警告

此函数旨在用作独立 escript 的入口点。因此,它会停止任何检测到的错误模拟器。请勿通过远程过程调用使用此函数,否则可能会导致远程节点意外关闭。