;; Two reader macros to help interaction with the shell, from the REPL.
;; You may put that in your rc files.
;; A dispatching reader macro for #! lines, so that you may load lisp scripts.
;; It may be advisable to guard the entry point of the script; I use:
;; #-testing (main)
;; and I have (push :testing *features*) in my REPL rc files
;; (that are not loaded by my scripts).
;; Thus, I can load the scripts without executing them, and I can try and
;; debug them in the REPL.
(defun executable-reader (stream ch subch)
(declare (ignorable ch subch))
#+clisp (sys::unix-executable-reader stream ch subch)
#-clisp (progn
(read-line stream)
(values)))
(set-dispatch-macro-character #\# #\! (function executable-reader))
;; Then, a reader macro on ! to read and run shell commands.
;; 1- the reader macro reads a form. So you can see what it without
;; executing the command by quoting it:
;; '!ls -la
;; 2- status is stored in *shell-command-status* if you need to test it
;; (eg. in a script).
;; 3- before executing the command, it sources ~/.bashrc, and it pipes the
;; command thru expand -8 to expand the tabs. uiop:run-program doesn't
;; use a pty/tty, so we cannot use stty to deal with tabs.
;; 4- the lines following the ! reader macro may be terminated with a backslash,
;; and continued on the following line. All the lines are copied as-is
;; (with the backslash and newline) to the script, until a line without final
;; backslash is read.
;; Example:
;; ! ls \
;; -l \
;; -a
;;
(defvar *shell-command-status* 0)
(defun shell-escape (stream ch)
(declare (ignore ch))
`(progn
(setf *shell-command-status*
(nth-value 2
(uiop:run-program
,(with-output-to-string (out)
(write-string "source ~/.bashrc ; ( " out)
(loop
:for line := (read-line stream nil nil)
:while (and line (char= #\\ (aref line (1- (length line)))))
:do (write-line line out)
:finally (write-string line out))
(write-string " ) | expand -8" out))
:input nil
:output t
:error-output t
:ignore-error-status t
:force-shell t)))
(values)))
(set-macro-character #\! 'shell-escape)# your code goes here
;; Have fun!