命令列參數

在本教學中,我們將學習如何直接讀取命令列參數,使用 OCaml 的 Sys.argv 陣列,然後學習如何使用標準函式庫的 Arg 模組更輕鬆地執行此操作。

Sys.argv

與 C 和許多其他語言一樣,在命令列上傳遞給特定程式的參數會儲存在陣列中。依照慣例,此陣列命名為 argv。它位於標準函式庫的 Sys 模組中,因此其完整名稱為 Sys.argv。包括程式名稱本身在內的參數數量只是陣列的長度。它使用 Array.length 函式取得。

以下程式會顯示 Sys.argv 中參數及其位置

let () =
  for i = 0 to Array.length Sys.argv - 1 do
    Printf.printf "[%i] %s\n" i Sys.argv.(i)
  done

如果您將上述程式儲存為 args.ml,並執行 ocaml args.ml arg1 arg2 arg3,您會得到以下結果

$ ocaml args.ml arg1 arg2 arg3
[0] args.ml
[1] arg1
[2] arg2
[3] arg3

請注意,ocaml 啟動了一個子程序,該子程序實際執行程式,其中 argv 為 args.ml arg1 arg2 arg3。您也可以使用 ocamlopt -o args args.ml 編譯程式,然後執行 ./args arg1 arg2 arg3,您會得到

$ ocamlopt -o args args.ml
$ ./args arg1 arg2 arg3
[0] ./args
[1] arg1
[2] arg2
[3] arg3

使用 Arg 模組

OCaml 標準函式庫有一個用於編寫命令列介面的模組,因此我們不必直接使用 Sys.argv。我們將考慮 OCaml 文件中的範例,一個用於附加檔案的程式。

首先,我們設定用法訊息,以便在命令列格式錯誤或要求協助時列印

let usage_msg = "append [-verbose] <file1> [<file2>] ... -o <output>"

現在,我們建立一些參考,以保存從命令列收集的資訊。Arg 模組會在讀取命令列時為我們填寫這些參考。

let verbose = ref false
let input_files = ref []
let output_file = ref ""

我們有一個布林參考用於 -verbose 旗標,預設值為 false。然後,我們有一個列表的參考,其中將保存所有輸入檔案的名稱。最後,我們有一個字串參考,其中將放置由 -o 指定的單一輸出檔案名稱。

我們需要一個函式來處理匿名輸入,也就是說,前面沒有旗標的輸入。在這種情況下,這些是我們的輸入檔案名稱。我們的函式只是將檔案名稱新增至先前定義的參考。

let anon_fun filename = input_files := filename :: !input_files

最後,我們建立命令列旗標規格的清單。每一個都是旗標名稱、遇到旗標時要採取的動作和說明字串的元組。

let speclist =
  [
    ("-verbose", Arg.Set verbose, "Output debug information");
    ("-o", Arg.Set_string output_file, "Set output file name");
  ]

我們在這裡有兩種動作:Arg.Set 動作設定布林參考,Arg.Set_string 動作設定字串參考。我們的 input_files 參考當然會由已定義的 anon_fun 函式更新。

現在我們可以呼叫 Arg.parse,並給予它規格清單、匿名函式和用法訊息。一旦它返回,參考將會填入附加檔案所需的所有資訊。

let () = Arg.parse speclist anon_fun usage_msg

(* Main functionality here *)

讓我們將程式儲存為 append.ml,並使用 ocamlopt -o append append.ml 編譯它並試用看看

$ ocamlopt -o append append.ml
$ ./append -verbose one.txt two.txt -o three.txt
$ ./append one.txt two.txt
$ ./append -quiet
./append: unknown option '-quiet'.
append [-verbose] <file1> [<file2>] ... -o <output>
  -verbose Output debug information
  -o Set output file name
  -help  Display this list of options
  --help  Display this list of options
[2]
$ ./append -help
append [-verbose] <file1> [<file2>] ... -o <output>
  -verbose Output debug information
  -o Set output file name
  -help  Display this list of options
  --help  Display this list of options

這是整個程式

let usage_msg = "append [-verbose] <file1> [<file2>] ... -o <output>"

let verbose = ref false

let input_files = ref []

let output_file = ref ""

let anon_fun filename =
  input_files := filename :: !input_files

let speclist =
  [("-verbose", Arg.Set verbose, "Output debug information");
   ("-o", Arg.Set_string output_file, "Set output file name")]

let () =
  Arg.parse speclist anon_fun usage_msg;
  (* Main functionality here *)

Arg 模組不僅具有 SetSet_string 更多的動作,還具有一些用於剖析更複雜命令列的較低層級函式。

其他剖析命令列選項的工具

有一些函式庫提供與內建 Arg 模組不同或更廣泛的功能

  • Cmdliner 是一個現代化的命令列處理介面,還可以自動產生 UNIX 手冊頁。

  • Clap 是一個命令式命令列剖析器。

  • Minicli 對於拒絕其他可能會靜默接受的格式錯誤的命令列具有良好的支援。

  • Getopt for OCaml 類似於 GNU getopt

仍需要協助嗎?

協助改進我們的文件

所有 OCaml 文件都是開放原始碼。看到錯誤或不清楚的地方嗎?提交提取請求。

OCaml

創新、社群、安全。