Accessing Environment variables
ASDF comes with a function that’ll allow you to look at Unix/Linux environment variables on a lot of different CL implementations:
* (uiop:getenv "HOME") "/home/edi"
Below is an example implementation:
* (defun my-getenv (name &optional default) "Obtains the current value of the POSIX environment variable NAME." (declare (type (or string symbol) name)) (let ((name (string name))) (or #+abcl (ext:getenv name) #+ccl (ccl:getenv name) #+clisp (ext:getenv name) #+cmu (unix:unix-getenv name) ; since CMUCL 20b #+ecl (si:getenv name) #+gcl (si:getenv name) #+mkcl (mkcl:getenv name) #+sbcl (sb-ext:posix-getenv name) default))) MY-GETENV * (my-getenv "HOME") "/home/edi" * (my-getenv "HOM") NIL * (my-getenv "HOM" "huh?") "huh?"
You should also note that some of these implementations also provide the ability to set these variables. These include ECL (
si:setenv) and AllegroCL, LispWorks, and CLISP where you can use the functions from above together with
setf. This feature might be important if you want to start subprocesses from your Lisp environment.
Also note that the
library has the method
(environment-variable "name"), on POSIX-like
systems including Windows. It is also
Accessing the command line arguments
Accessing command line arguments is implementation-specific but it appears most implementations have a way of getting at them. Roswell or external libraries (see next section) make it portable.
SBCL stores the arguments list in the special variable
$ sbcl my-command-line-arg
* sb-ext:*posix-argv* ("sbcl" "my-command-line-arg") *
More on using this to write standalone Lisp scripts can be found in the SBCL Manual
CL-USER> system:*line-arguments-list* ("/Users/cbrown/Projects/lisptty/tty-lispworks" "-init" "/Users/cbrown/Desktop/lisp/lispworks-init.lisp")
Here’s a quick function to return the argument strings list across multiple implementations:
(defun my-command-line () (or #+SBCL *posix-argv* #+LISPWORKS system:*line-arguments-list* #+CMU extensions:*command-line-words* nil))
Now it would be handy to access them in a portable way and to parse them according to a schema definition.
Parsing command line arguments
We can now refer to it with its
We first declare our arguments with
opts:define-opts, for example
(opts:define-opts (:name :help :description "print this help text" :short #\h :long "help") (:name :level :description "The level of something (integer)." :short #\l :long "level" :arg-parser #'parse-integer))
Everything should be self-explanatory. Note that
a built-in CL function.
Now we can parse and get them with
opts:get-opts, which returns two
values: the first is the list of valid options and the second the
remaining free arguments. We then must use multiple-value-bind to
(multiple-value-bind (options free-args) ;; There is no error handling yet (specially for options not having their argument). (opts:get-opts)
We can explore this by giving a list of strings (as options) to
(multiple-value-bind (options free-args) (opts:get-opts '("hello" "-h" "-l" "1")) (format t "Options: ~a~&" options) (format t "free args: ~a~&" free-args)) Options: (HELP T LEVEL 1) free args: (hello) NIL
If we put an unknown option, we get into the debugger. We refer you to unix-opts’ documentation and code sample to deal with erroneous options and other errors.
We can access the arguments stored in
getf (it is a
property list), and we can exit (in a portable way) with
opts:exit. So, for example:
(multiple-value-bind (options free-args) ;; No error handling. (opts:get-opts) (if (getf options :help) (progn (opts:describe :prefix "My app. Usage:" :args "[keywords]") (exit))) ;; <= exit takes an optional return status. ...
And that’s it for now, you know the essential. See the documentation for a complete example, and the Awesome CL list for useful packages to use in the terminal (ansi colors, printing tables and progress bars, interfaces to readline,…).
Forking with CMUCL
(defparameter *sigchld* 0) (defparameter *compile-files-debug* 2) (defun sigchld-handler (p1 p2 p3) (when (> 0 *compile-files-debug*) (print (list "returned" p1 p2 p3)) (force-output)) (decf *sigchld*)) (defun compile-files (files &key (load nil)) (setq *sigchld* 0) (system:enable-interrupt unix:sigchld #'sigchld-handler) (do ((f files (cdr f))) ((not f)) (format t "~&process ~d diving for ~a" (unix:unix-getpid) `(compile-file ,(car f))) (force-output) (let ((pid (unix:unix-fork))) (if (/= 0 pid) ;; parent (incf *sigchld*) ;; child (progn (compile-file (car f) :verbose nil :print nil) (unix:unix-exit 0))))) (do () ((= 0 *sigchld*)) (sleep 1) (when (> 0 *compile-files-debug*) (format t "~&process ~d still waiting for ~d childs" (unix:unix-getpid) *sigchld*))) (when (> 0 *compile-files-debug*) (format t "~&finished")) (when load (do ((f files (cdr f))) ((not f)) (load (compile-file-pathname (car f))))))