Pure Danger Tech


Learning Clojure #9: command-line options

25 Feb 2010

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]
    "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"]
    ...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>...
  --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…