Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse command line arguments and flags portably in Common Lisp?

I'd like to get and parse command line arguments in Common Lisp, so that e.g. myscript -a 1 -b 2 will allow me to get the values, and myscript -xyz will work too. How can I do it portably(between compilers)?

like image 643
MatthewRock Avatar asked Aug 31 '16 13:08

MatthewRock


1 Answers

You can try unix-options.

(ql:quickload :unix-options)

(defpackage :test
  (:use :cl)
  (:import-from :unix-options
                :&parameters
                :&free
                :with-cli-options))

You probably want to :use the package, but if you prefer to import symbols from it, don't forget about &free and &parameters. The library defines a getopt function which is similar to the traditional getopt utility. But it also defines with-cli-options, which is a little more lispy.

  • the first set of symbols define options, i.e. flags that are either absent or present;
  • symbols after &parameters define parameters, which must be followed by a value;
  • arguments that are not recognized as either options or parameters are available in a list bound to the variable specified by &free

For example:

(in-package :test)

(defun my-program (&rest cli-args)
  (with-cli-options (cli-args)
      (x y z &parameters a b &free other)
    (list x y z a b other))))

Here I define the entry point of the program. In a real program, you can simply leave the first list empty, like this:

(with-cli-options () <bindings> <body>) 

... and the options will be portably fetched from the actual command line arguments of your Lisp implementation. You can also call (uiop:command-line-arguments) to have to full command line, which seems to support more implementations and includes the name of the program as the first element. The above function allows me to test the behavior of the parser. Note for example that short options can be separated or joined:

(my-program "-xyz" "-a" "2" "-b" "3" "--" "something")
=> (T T T "2" "3" ("something"))

(my-program "-x" "-y" "-z" "-a" "2" "-b" "3" "--" "something")
=> (T T T "2" "3" ("something"))

Be careful about options that are declared as parameters but aren't given actual values (or maybe they are, the case is ambiguous):

(my-program "-a" "-b")
=> (NIL NIL NIL "-b" NIL NIL)

There are warnings for unknown parameters:

(ignore-errors (my-program "-w"))
; WARNING: Invalid option: w
=> (NIL NIL NIL NIL NIL NIL)

See the docs for details.

like image 120
coredump Avatar answered Sep 23 '22 08:09

coredump