07 Oct 2025
An Emacs Lisp macro to parse arguments in shell scripts
My Emacs system sometimes creeps out into the shell. This usually happens when I want to provide an additional entry point to information I maintain using Emacs.
This means that in my ~/bin
I have a few scripts, written in Emacs
Lisp, that are meant to be run from the command line, outside of an
Emacs client.
With that, comes the need to parse parameters.
After writing the same code a couple of times, and since back then I
could not find an obvious other choice to do the same thing, I decided
it was a good use case for a macro, which I called with-positional-args
.
Here how it looks like in user-space. This is part of a script I use to search into annotated PDFs in a directory:
#!/usr/local/bin/emacs --script ;; preparing load-path and requiring a couple ;; of features, including the one that implements ;; with-positional-args (defun pdf-search (pattern path) ;; the details are irrelevant in this context ) ;; Here we go! (with-positional-args ((pattern :mandatory "You must provide a PATTERN") (path :default ".")) (pdf-search pattern path))
And here the implementation:
;; Caveman args list parsing (defun arg-resolver (arg-properties idx) (pcase (car arg-properties) (:default `(or (nth ,idx command-line-args) ,(cadr arg-properties))) (:mandatory `(or (nth ,idx command-line-args) (error (or ,(cadr arg-properties) "Undefined error")))) (_ (nth idx command-line-args)))) (defmacro with-positional-args (arglist &rest body) "Bind command-line arguments as per ARGLIST, then evaluate BODY. Each element of ARGLIST has the form: (VAR) for optional argument, (VAR :default VALUE) for specifying a default value when missing, (VAR :mandatory [MSG]) for required arguments with optional error message MSG." (declare (indent 1)) `(let ,(cl-loop for (arg-name . arg-properties) in arglist for idx from 3 collect `(,arg-name ,(arg-resolver arg-properties idx))) ,@body))