Command-line Arguments
In this tutorial we learn how to read command line arguments directly, using
OCaml's Sys.argv
array, and then how to do so more easily using the standard
library's Arg
module.
Sys.argv
Like in C and many other languages, the arguments that are passed to a given
program on the command line are stored in an array. Following tradition, this
array is named argv
. It is found in the Sys
module of the standard library,
therefore its full name is Sys.argv
. The number of arguments including the
name of the program itself is simply the length of the array. It is obtained
using the Array.length
function.
The following program displays the arguments with their position in Sys.argv
:
let () =
for i = 0 to Array.length Sys.argv - 1 do
Printf.printf "[%i] %s\n" i Sys.argv.(i)
done
If you save the program above as args.ml
, and run ocaml args.ml arg1 arg2 arg3
, here is what you get:
$ ocaml args.ml arg1 arg2 arg3
[0] args.ml
[1] arg1
[2] arg2
[3] arg3
Note that ocaml
launched a subprocess that actually runs the program where
argv is args.ml arg1 arg2 arg3
. You can also compile your program using
ocamlopt -o args args.ml
, and then running ./args arg1 arg2 arg3
and you
will get:
$ ocamlopt -o args args.ml
$ ./args arg1 arg2 arg3
[0] ./args
[1] arg1
[2] arg2
[3] arg3
Arg
Module
Using the The OCaml standard library has a module for writing command line interfaces, so
we do not have to use Sys.argv
directly. We shall consider the example from
the OCaml documentation, a program for appending files.
First, we set up the usage message to be printed in the case of a malformed command line, or when help is requested:
let usage_msg = "append [-verbose] <file1> [<file2>] ... -o <output>"
Now, we create some references to hold the information gathered from the
command line. The Arg
module will fill these in for us as the command line is
read.
let verbose = ref false
let input_files = ref []
let output_file = ref ""
We have a boolean reference for the -verbose
flag with a default value of
false
. Then we have a reference to a list which will hold the names of all
the input files. Finally, we have a string reference into which the single
output file name specified by -o
will be placed.
We will need a function to handle the anonymous inputs, that is to say the ones with no flag before them. In this case these are our input file names. Our function simply adds the file name to the reference defined earlier.
let anon_fun filename = input_files := filename :: !input_files
Finally we build the list of command line flag specifcations. Each is a tuple of the flag name, the action to be taken when it is encountered, and the help string.
let speclist =
[
("-verbose", Arg.Set verbose, "Output debug information");
("-o", Arg.Set_string output_file, "Set output file name");
]
We have two kinds of action here: the Arg.Set
action which sets a boolean
reference, and the Arg.Set_string
action which sets a string reference. Our
input_files
reference will of course be updated by the anon_fun
function
already defined.
We can now call Arg.parse
, giving it our specification list, anonymous
function, and usage message. Once it returns, the references will be filled
with all the information required to append our files.
let () = Arg.parse speclist anon_fun usage_msg
(* Main functionality here *)
Let's save our program as append.ml
and compile it with ocamlopt -o append append.ml
and try it out:
$ 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
Here is the whole program:
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 *)
The Arg
module has many more actions than just Set
and Set_string
, and
some lower-level function for parsing more complicated command lines.
Other Tools for Parsing Command-Line Options
There are libraries with facilities different from or more extensive than the
built-in Arg
module:
-
Cmdliner is a modern interface for command line processing, which also generates UNIX man pages automatically.
-
Clap is an imperative command line parser.
-
Minicli has good support for rejecting malformed command lines which others might sliently accept.
-
Getopt for OCaml is similar to GNU getopt.
Help Improve Our Documentation
All OCaml docs are open source. See something that's wrong or unclear? Submit a pull request.