dorai@titan.rice.edu (Dorai Sitaram) (07/16/88)
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
james@ZERMATT.LCS.MIT.EDU (James William O'Toole Jr.) (07/17/88)
Date: 16 Jul 88 08:01:32 GMT From: titan!dorai@rice.edu (Dorai Sitaram) 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: ... If anyone can suggest further improvement I'll be glad to hear of it. Your method, when executed, requires starting one /bin/csh to interpret the shellscript, another /bin/csh to interpret the ``runschemescript'' shellscript, copying the scheme code into a temporary file, and invoking the scheme implementation on that file. I would suggest that you instead place your scheme code in a file whose first line is ``#!/bin/scheme arg''. If your file is called foo, and its first line is as given above, then when you execute foo, your Unix will execute the command ``/bin/scheme arg foo''. You may omit arg, or choose arg to be something which speeds up /bin/scheme, or tells it to load the file whose name follows, or whatever. You will have to convince your scheme to ignore the first line of foo, of course. This method avoids starting up extra shells and copying files. --Jim
dorai@titan.rice.edu (Dorai Sitaram) (07/21/88)
James William O'Toole Jr. <james@ZERMATT.LCS.MIT.EDU> writes: > Date: 16 Jul 88 08:01:32 GMT > From: titan!dorai@rice.edu (Dorai Sitaram) > > 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: > > ... > > If anyone can suggest further improvement I'll be glad to hear of it. > >Your method, when executed, requires starting one /bin/csh to interpret >the shellscript, another /bin/csh to interpret the ``runschemescript'' >shellscript, copying the scheme code into a temporary file, and invoking >the scheme implementation on that file. > >I would suggest that you instead place your scheme code in a file whose >first line is ``#!/bin/scheme arg''. If your file is called foo, and >its first line is as given above, then when you execute foo, your Unix >will execute the command ``/bin/scheme arg foo''. You may omit arg, or >choose arg to be something which speeds up /bin/scheme, or tells it to >load the file whose name follows, or whatever. You will have to >convince your scheme to ignore the first line of foo, of course. This >method avoids starting up extra shells and copying files. > > --Jim A Refresher on #! ----------------- If `#! A A1 ...' is the first line of a shell script file whose name is B, calling B with args B1 ... has the same effect as calling A with args A1 ... B B1 ... In the above, A has to be a full pathname of a *standard* Un*x command (like cat, scheme, etc), i.e., it cannot be a user-fashioned command or shell script (I found this out thru experimentation. Why is this?). !# no rehserfeR A ----------------- Jim [and J A Biep Durieux (private communication)] suggest that #! /usr/local/scheme be the first line of a Scheme script: this has the unfortunate effect that all arguments to a Scheme script will be loaded as Scheme files [even though they are in general, just like any shell script arguments, *not* Scheme files]. The script file itself is loaded into Scheme, which is ok, provided that its first (non-Scheme) line can somehow be removed. However, Jim and Biep are right in stating that #! can lead to a concise and efficient implementation of Scheme scripts. Let the first line of a Scheme script be the line #! /bin/sh runschemescript runschemescript is a Bourne script which looks like: ******************************************** foo=$1 shift echo > .tmp '(set! $* (quote ('$*')))' echo -n >> .tmp ';' cat $foo >> .tmp echo >> .tmp '(exit)' scheme .tmp #this line could be 'exec scheme .tmp' but I don't know if #that saves anything ******************************************** A Scheme script 'ss' looks like: ******************************************** #! /bin/sh runschemescript <a body of Scheme code which refers to the list of arguments of `ss' by the variable $*> ******************************************** This Scheme code doesn't have the restriction of not being able to use quotes in it. It also looks less ugly with the user not having to mention $* and use Bourne quotes in his Scheme scripts. ss, when called with arguments a1 ..., gets converted (by the #!) to the call /bin/sh runschemescript ss a1 ... which is the same as (with one subshell invocation) runschemescript ss a1 ... runschemescript then creates a .tmp file which sets a Scheme global variable $* to a list (a1 ...) of ss's arguments; followed by the contents of the file ss; followed by an (exit). [A judicious ';' is inserted at the right spot to comment out the only non-Scheme portion of the file ss: the first line (with #! ...).] ss can therefore be used as a regular shell script which is written in Scheme rather than in Bourne. I would like the first line of Scheme script to be just #! runschemescript instead of #! /bin/sh runschemescript but as said earlier, only standard Un*x commands seem to be accepted by #!. Improvements are welcome. --dorai
mwm@eris.berkeley.edu (Mike (I'm in love with my car) Meyer) (07/27/88)
In article <various@many.sites> many people write shell scripts with
lines like:
echo > .tmp '(set! $* (quote ('$*')))'
in them. This has one nasty problem - try doing "cd /; <scheme-program>".
You'll find (well, you ought to find) that it dies - you can't create
the scratch file you want. Of course, if two people are in the
directory you're running in and running that command, you'll have
probles too.
Instead, try doing:
echo > /tmp/runscheme.$$ '(set! $* (quote ('$*')))'
This creates a file in /tmp with the process id of the shell executing
the script in it's name. That solves both problems at once.
<mike
--
My feet are set for dancing, Mike Meyer
Won't you turn your music on. mwm@berkeley.edu
My heart is like a loaded gun, ucbvax!mwm
Won't you let the water run. mwm@ucbjade.BITNET
ok@quintus.uucp (Richard A. O'Keefe) (07/28/88)
In article <12601@agate.BERKELEY.EDU> mwm@eris.berkeley.edu (Mike Meyer) writes: ... >Instead, try doing: > echo > /tmp/runscheme.$$ '(set! $* (quote ('$*')))' >This creates a file in /tmp ... Um, /tmp is for system programs. /usr/tmp is the place for user temporary files. A number of system V utilities support the convention of trying to create files in $TMPDIR. It might be better, then, to do scheme_temp=${TMPDIR:-/usr/tmp}/runscheme.$$ echo '(set! $* (quote ('$*')))' >$scheme_temp You'll want to keep track of the file name so that you can delete it.