檢視原始碼 OptionParser (Elixir v1.16.2)

用於剖析命令列引數的函式。

在呼叫命令時,可以傳遞命令列選項來修改命令執行的動作。在此文件中,這些選項稱為「開關」,在其他情況下,它們可能稱為「標記」或單純的「選項」。可以為開關指定一個值,也稱為「引數」。

此模組中的主要函式為 parse/2,它會將命令列選項和引數清單剖析成關鍵字清單

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

OptionParser 提供了一些現成的便利功能,例如別名和自動處理否定開關。

parse_head/2 函式是 parse/2 的替代函式,它會在找到一個既不是開關也不是先前開關值的資料後立即停止剖析。

此模組也提供低階函式,例如 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()

函式

@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} - 選項 key 已使用 value 成功解析

  • {:invalid, key, value, rest} - 選項 key 使用 value 無效(在無法根據切換類型解析值時傳回)

  • {:undefined, key, value, rest} - 選項 key 未定義(在嚴格模式下,當切換未知或在不存在的原子時傳回)

  • {:error, rest} - 在給定的 argv 開頭沒有切換

@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"], []}
@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 區塊。

此函數會將給定的 字串 分割成一個字串清單,其方式與許多 shell 類似。

範例

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

iex> OptionParser.split("foo \"bar baz\"")
["foo", "bar baz"]
連結到此函式

to_argv(列舉, 選項 \\ [])

檢視原始碼
@spec to_argv(Enumerable.t(), options()) :: argv()

接收一個鍵值列舉並將其轉換為 argv/0

金鑰必須為原子。值為 nil 的金鑰會被捨棄,布林值會轉換成 --key--no-key(如果值分別為 truefalse),而所有其他值會使用 to_string/1 轉換。

建議傳遞給 to_argv/2options 設定集與傳遞給 parse/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"]