PREV UP NEXT SCM

3.12.1: Unix Shell Scripts

In reading this section, keep in mind that the first line of a script file has (different) meanings to SCM and the operating system (execve).

file: #! interpreter
file: #! interpreter arg

On unix systems, a Shell-Script is a file (with execute permissions) whose first two characters are `#!'. The interpreter argument must be the pathname of the program to process the rest of the file. The directories named by environment variable PATH are not searched to find interpreter. The arg is an optional argument encapsulating the rest of the first line's contents, if not just whitespace.

When executing a shell-script, the operating system invokes interpreter with (if present) arg, the pathname of the shell script file, and then any arguments which the shell-script was invoked with.

Read syntax: #! ignored
When the first two characters of the file being loaded are #!, the first line of that file will be ignored.

This combination of interpretatons allows SCM source files to be used as POSIX shell-scripts if the first line is:

#!/usr/local/bin/scm

or

#!/usr/local/bin/scm -l

When such a file is invoked, /usr/local/bin/scm is executed with the name of this file as the first argument.

#!/usr/local/bin/scm
(print (program-arguments))
(quit)
=> ("scm" "./script")
#!/usr/local/bin/scm -l
(print (program-arguments))
=> ("scm" "-l" "./script")

The following shell-script will print factorial of its argument:

#!/usr/local/bin/scm -l
(define (fact n) (if (< n 2) 1 (* n (fact (+ -1 n)))))
(print (fact (string->number (cadddr (program-arguments)))))
./fact 6
=> 720 

Shell-scripts suffer from several drawbacks:

The following approach solves these problems at the expense of slower startup. Make `#!/bin/sh' the first line and prepend every subsequent line to be executed by the shell with :; (type; in older versions). The last line to be executed by the shell should contain an exec command; exec tail-calls its argument.

/bin/sh is thus invoked with the name of the script file, which it executes as a *sh script. Usually the second line starts `:;exec scm -f$0', which executes scm, which in turn loads the script file. When SCM loads the script file, it ignores the first and second lines, and evaluates the rest of the file as Scheme source code.

The second line of the script file does not have the length restriction mentioned above. Also, /bin/sh searches the directories listed in the `PATH' environment variable for `scm', eliminating the need to use absolute locations in order to invoke a program.

#!/bin/sh
:;exec scm -l$0 $*
(define (fact n) (if (< n 2) 1 (* n (fact (+ -1 n)))))
(print (fact (string->number (caddr (program-arguments)))))
./fact 6
=> 720