[Openmcl-devel] Using ccl as a #! scripting language

John McAleely john at mcaleely.com
Sun Oct 26 17:16:52 PDT 2008

> I like the idea of setting the arguments into a variable, although I
> suspect that there are likely to be quoting issues if the arguments
> have quote signs or backslashes.

Thank you. You are right about there being quoting issues, so using  
the (ccl::command-line-arguments) function seems to be superior, if  
undocumented. I suppose I could write an escaping routine in the shell  
script, but I'm trying to use more lisp :-) It seems (ccl::command- 
line-arguments) still has quoting artefacts in the arguments, but a  
determined script could parse these. This seems preferable to my  
original which would error in some cases.

> You might also look at SBCL's doc on this, since they have another
> hook that catches exceptions and just prints them and exits.  CCL can
> do this with '-b' added to it's command, but it does print out a full
> stack trace.

I was pleased to see I'd written the same function as they cite:


Which made me feel I was making progress with lisp.

> Also, technically, the SBCL also gets it wrong as well.  The dispatch
> macro function should end in (values), otherwise, the string of the
> ignored line is the result of reading the line.  It doesn't really
> hurt anything in this case, though.

Thanks. I've fixed mine.

So, taking that all in, here is a new version of the script, which  
uses the internal command line arguments function. I admit there are  
still quoting issues with the variable I 'declare' in the top line of  
the script, if you want the full generality of a lisp symbol, but  
those seem worth ignoring for now.

# ccl-script
# Placed in the public domain by the author
# John McAleely <john at mcaleely.com>
# A front end for ccl to be used to create #! executable text scripts on
# unix like operating systems.
# Start your text script with:
# #!/usr/bin/env /path/to/ccl-script *command-line*
# *command-line* is defined in the script as a list of the
#                command line arguments used to invoke the script
# #! causes the remainder of the line to be ignored.
# eg
##!/usr/bin/env /path/to/ccl-script *command-line*
#(format t "Hello World called as: ~a" (pop *command-line*))
##!/usr/bin/env /path/to/ccl-script *command-line*
#(loop for line = (read-line *standard-input* nil nil)
#  while line do (format t "~a~%" line))

# edit this to be your ccl start script of choice (ccl64, openmcl,  

# stash away the name for the global variable that will hold the
# command line

# Store the name of the script we will load later

# get rid of the script's paramaters
shift 2

# pass a little bit of read-macro magic in first,
# so that the line #!/usr... in your script is ignored.
# declares #! as a read macro that ignores the remainder of the line.
# Then pass in a debug hook that outputs a simple error instead of the
# interactive debugger. You may prefer to omit this.
# Finally pass in code to create the scripts command line on $VAR from
# the one passed to ccl. Script parameters will be those after the --
$CCL \
  -e "(set-dispatch-macro-character #\\# #\\!
       #'(lambda (s c1 c2)
           (declare (ignore c1 c2))
           (read-line s t nil t)
           (values)))" \
  -e "(setf *debugger-hook*
       #'(lambda (condition hook)
           (declare (ignore hook))
           (format *error-output* \"Error: ~a~%\" condition)
           (ccl:quit)))" \
  -e "(progn
        (defvar $VAR nil)
        (let ((script-param
               (do ((args (ccl::command-line-arguments) (cdr args)))
                   ((or (null args)
                        (string= \"--\" (car args)))
                 (cdr args)))))
          (push \"$SCRIPT\" $VAR)
          (dolist (x script-param) (push x $VAR))
          (setf $VAR (reverse $VAR))))" \
  -l "$SCRIPT" -- "$@"

More information about the Openmcl-devel mailing list