檢視原始碼 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 支援位置性引數和選擇性引數。選擇性引數(或簡稱選項)必須以特殊字元作為前綴(- 是所有作業系統上的預設值)。選項和位置性引數都有 1 個或多個相關聯的值。請參閱 引數規格 以瞭解更多關於支援組合的詳細資訊。

在使用者輸入中,簡短選項可以與它們的值串聯。長選項支援以 = 分隔的值。請考慮以下定義

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 原子,或必須傳回字串的使用者定義函數。純字串應以清單包裝,例如 ["string is nested"]

引數名稱用於填入引數映射。

巢狀命令的路徑。第一個元素始終是 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 原子,或必須傳回字串的使用者定義函數。純字串應以清單包裝,例如 ["string is nested"]

連結至此類型

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 的進入點。因此,它會針對偵測到的任何錯誤停止模擬器。請勿透過遠端程序呼叫使用此函式,否則可能會導致遠端節點意外關閉。