命令行参数

在本教程中,我们将学习如何直接读取命令行参数(使用 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

创新。社区。安全。