I needed to process some command-line options today in a Clojure app and I dug into the mostly undocumented <a href="http://richhickey.github.com/clojure-contrib/command-line-api.html">with-command-line</a>
in clojure.contrib.
About the only documentation available is:
Usage: (with-command-line args desc cmdspec & body) Bind locals to command-line args.
That’s great but a little light on examples. :) Fortunately I stumbled on this excellent answer on Stack Overflow which got me 98% of the way there.
Breaking the above params down we have:
- args – the list of incoming args we want to process, such as from a main() call
- desc – the usage description to print with –help
- cmdspec – a list of options and for each a help description and default value
- body – the body to execute in the context of the command-line values
These are all pretty straightforward except for cmdspec which is most easily demonstrated in an example. Just for grins, I implemented something like the classic wc
(word count) command. Here’s the relevant part for this example:
(defn -main [& args] (with-command-line args "Usage: wc [-l|-w|-c] [-out out.txt] <file>..." [[lines? l? "Count lines" false] [words? w? "Count words" false] [chars? c? "Count chars" false] [out "The output file"] files] ...implementation... ))
Taking those args again we see the args
passed from the command-line, the usage string, and a big vector that is the cmdspec
, and finally the body. The cmdspec
is a vector of option vectors where each option vector is of the form:
[<em>option</em> [<em>alternate-option*</em>] <em>description</em> [<em>default</em>] ]
The first option defines the local variable that will be set as well. The ? suffix indicates that the option is a boolean option (no value should be passed on the command line). Taking the first option for lines?
as an example, the command line will now accept “–lines”, “-lines”, “–l”, and “-l”. The out
option is not a boolean and will expect to receive a value on the command line after it. And finally all remaining command line values are put into the files list.
The usage and option strings are used to automatically provide a --help/-h
option:
$ java -cp wcc-standalone.jar wcc -h Usage: wc [-l|-w|-c] [-out out.txt] <file>... Options --lines, -l Count lines [default true] --words, -w Count words --chars, -c Count chars --out <arg> The output file
Here we run it in line mode with an output file:
$ java -cp wcc-standalone.jar wcc -l -out lines.txt src/wcc.clj $ cat lines.txt Counted src/wcc.clj: 38
Hope this helps someone down the line…