檢視原始碼 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
支援位置性引數和選擇性引數。選擇性引數(或簡稱選項)必須以特殊字元作為前綴(-
是所有作業系統上的預設值)。選項和位置性引數都有 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
,所有其他欄位都有預設值。
使用者定義的說明範本,用於列印在命令用法中。元組的第一個元素必須是字串。它會作為用法標頭的一部分列印。元組的第二個元素可以是包含字串的清單、type
和 default
原子,或必須傳回字串的使用者定義函數。純字串應以清單包裝,例如 ["string is nested"]
。
引數名稱用於填入引數映射。
巢狀命令的路徑。第一個元素始終是 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
原子,或必須傳回字串的使用者定義函數。純字串應以清單包裝,例如 ["string is nested"]
。
引數名稱用於填入引數映射。
-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
的進入點。因此,它會針對偵測到的任何錯誤停止模擬器。請勿透過遠端程序呼叫使用此函式,否則可能會導致遠端節點意外關閉。