Writing Common Lisp code that executes from the command line, but not inside the interpreter

孤街浪徒 提交于 2019-11-30 07:04:45

You can use the following trick:

  1. Define a dispatch function for shebang:

    (set-dispatch-macro-character #\# #\!
        (lambda (stream c n)
          (declare (ignore c n))
          (read-line stream)
          `(eval-when (:compile-toplevel :load-toplevel :execute)
             (pushnew :noscript *features*))))
    
  2. In your script file use #-:noscript:

    #!/usr/local/bin/sbcl --script
    
    (defun test (a) (print a))
    
    (test 1)
    #-:noscript (test 2)
    #+:noscript (test 3)
    

    Executing ./test.lisp will print 1 and 2, while C-c C-k will output 1 and 3.

EDITS

This trick should work, because the shebang line is removed altogether by sbcl --script, but not removed, when the file is loaded through SLIME or other mechanisms.

The drawback of this approach is that we condition on absence of :noscript in features, and not presence of :script. To amend it, pushing of the appropriate feature should be done in sbcl --script processing itself.

Fare Rideau wrote a nifty unix utility CL-Launch, which enables executing lisp software from the command line. It has integrated Quicklisp support and works on most of the Common Lisp implementations.

Example script may look like this:

#!/usr/bin/cl -sp "hunchentoot" -Q -E main

(defun main (argv)
  (format t "~A: ~{~A ~}~%" (truename ".") argv)
  (hunchentoot:start
   (make-instance 'hunchentoot:acceptor
                  :document-root (truename ".")
                  :port 8080))
  (do ((x #\s (read-char)))
      ((char-equal x #\q) nil)))

After adding +x permissions to script and invoking it, it will start http server in the current directory. "-sp" flag indicates package you want to load, so it's fairly clean way of abstracting shell script from the package.

For more details refer: http://cliki.net/CL-Launch

I had the same question and I have just stumbled upon this discussion. At least with sbcl it seems I can use (sb-ext:posix-getenv "_"). When run in slime it returns /usr/bin/emacs (or whatever the path to emacs is) and otherwise the command I use to invoke the script. So it is always possible to differentiate between slime and script invocation until you're an emacs contributor :)

If you want to get the full pathname of the script you are invoking, you can use (truename (sb-ext:posix-getenv "_")). However, when run from slime it will return the effective emacs pathname, e.g. /usr/bin/emacs-24.5, so this might be less convenient.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!