[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Scheme shellscripts

Hi! Thought this might be of interest.

If you've tried using shellscripts to build your own Un*x commands, felt the
shell language a bit too hairy, and would rather use Scheme instead, here's a
short fix. This assumes that the available Scheme interpiler can be called
at the Un*x prompt with a file-name which is automatically loaded before 
producing the interactive loop. E.g., Chez Scheme (copyright R. Kent Dybvig).

A shellscript written in Scheme is like any other Scheme file, except that its
first line has to be:
			runschemescript "$*" '
and its last line:

The quotes are used to delimit the Scheme code which forms the body of the
script. The script consists of a call of the Un*x command `runschemescript' 
with two arguments, the first being the list of arguments to the Scheme 
script, and the second the text of the Scheme code in the script. Thus, a 
Scheme shellscript is a *true* schellscript, i.e., it is callable at the Un*x 
command-line, though the main part of it is a piece of text which is Scheme.

The command `runschemescript' used above is a shellscript written in the usual
shell language. It is the following relatively simple piece of cshell:

		echo > .tmpfile '(set! $* (quote (' $1 ')))'
		echo >>.tmpfile $2
		echo >>.tmpfile '(exit)'
		scheme .tmpfile

In effect, `runschemescript' creates a temp Scheme file which sets a global 
variable $* to the list of arguments supplied to the Scheme script, runs the 
script which uses $* to refer to its arguments and exits. This file is then 
run with the Scheme interpiler. 

An example Scheme shellscript is the Scheme compiler `sc' which can be invoked
at the Un*x command-line, in makefiles, etc [like `cc']. My `sc' looks like

runschemescript "$*" '

(case (length $*)
  [1 (compile-file (symbol->string (car $*)))]
  [2 (compile-file (symbol->string (car $*)) (symbol->string (cadr $*)))]
  [3 (let ([$1 (car $*)] [$2 (cadr $*)] [$3 (caddr $*)])
       (cond [(eq? $1 `-o)
	      (compile-file (symbol->string $3) (symbol->string $2))]
	     [(eq? $2 `-o)
	      (compile-file (symbol->string $1) (symbol->string $3))]
	     [else (printf "Bad args to sc: ~a~n" $*)]))]
  [else (printf "Bad args to sc: ~a~n" $*)])


$* being a simple Scheme list of symbols, any processing of arguments or
options is straightforward. Some nits are: These Scheme shellscripts 
always have to be enclosed in runschemescript "$*" ' <body> '. The quote-
character ' {a common enough character in Scheme!} cannot be used in the 
script's body as it interferes with the shell's delimiter characters--instead 
one should use the backquote ` or "quote". Typical shellscripts use other
Un*x commands in their bodies: in Ch*z Scheme, there is a function "system" 
which enables us to call Un*x commands; but this may not be available
in other Schemes. Even in this case, calling a Scheme shellscript from 
within a Scheme shellscript will have multiple invocations of the Scheme
interpreter running simultaneously, which doesn't help a whole lot in
maintaining efficiency. Strangely, one doesn't have to bother about the
same file .tmpfile being used for such nesting--once the file is loaded,
it doesn't matter if it is used for something else.

On the whole, I've found that in a Un*x environment which supports Ch*z 
Scheme, the above technique beats writing shellscripts in cshell for sheer 
ease and maneuvrability. One can then, if one chooses, convert a fully 
debugged Scheme script to a cshell script (or a C program) to recover 

If anyone can suggest further improvement I'll be glad to hear of it.