Using SBCL for Common Lisp shell scripting
Sunday, October 5th, 2008I have previously developed some Commom Lisp shell scripts with Emacs/Slime/SBCL and used Clisp for running the scripts. But after running into a compatibility problem between SBCL and Clisp while developing a script for maintaing an automatic mirror of my music collection where flac files are converted to much smaller ogg files, I decided I might as well do what has to be done for using SBCL directly for running the script.
Prepping SBCL
As was mentioned in a comment in my previous post about using Common Lisp for shell scripting, the SBCL manual outlines a piece of code that must be added to an initialization file (I have added it to my $HOME/.sbclrc file).
After adding that to .sbclrc, the next step is to add a shebang line to my script
#!/usr/bin/sbcl --noinform
Adapting for development
The problem now is that the code that is needed for running the script will also be executed while compiling the file inside Slime. I found that this can be fixed by inspecting the *posix-argv* variable. This is a list, that inside Slime has one entry (“/usr/bin/sbcl”), and when the script is executed from the command line has two entries (path to the SBCL interpreter and path to your script in addition to possible command line arguments to the scripts). So if *posix-argv* is longer than 1, we can execute the script. One caveat here, if you are inside a package, *posix-argv* is not directly available. We must use sb-ext:*posix-argv* instead. This leads us to the following file structure:
#!/usr/bin/sbcl --noinform
;; Lisp code (defuns, defclasses, whatever)
;; If run from command line, the following if-test will succeed
(if (> (length sb-ext:*posix-argv*) 1)
;; Code that is executed from the command line only goes here
)
Now, the script can be developed as usual in Slime and be executed from the command line without changing anything. This also means that the command line arguments to the script is found from the third position and onwards in ext:*posix-argv*.
Script details
This loops through my music directory and creates a parallell directory structure where flac files are converted to ogg files and other file types are just hard linked to the original file. Soft links does not work, because when you try to copy the files to your portable music player, you get a soft link/permission error instead of getting your actual music file.
The flac to ogg conversion is done with sox, so you need to install sox in addition to sbcl and cl-asdf to run this script.
Script usage
General:
./converter.lisp basedir targetdir quality
Example:
./converter.lisp /media/sda4/musikk/ /media/sda4/ogg-musikk/ 5
Note: You need the ending / in the directory paths, it does not work otherwise.
Portability hints for Clisp
The main problem I ran into with Clisp is different behaviour in the directory function. This can probably be fixed by using the complete pathname library from PCL chapter 15. The shebang line must be changed to f.ex.
#!/usr/bin/env clisp
and command line arguments are available in ext:*args*
More hints for porting shell scripts to other CL implementations are available in the Common Lisp Cookbook.