[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
efficiency.
If anyone can suggest further improvement I'll be glad to hear of it.
--dorai