Next: , Previous: Scripting, Up: Scripting


3.13.1 Unix Scheme 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 \ ...

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.

When executing a shell-script, the operating system invokes interpreter with a single argument encapsulating the rest of the first line's contents (if not just whitespace), the pathname of the Scheme Script file, and then any arguments which the shell-script was invoked with.

Put one space character between ‘#!’ and the first character of interpreter (‘/’). The interpreter name is followed by ‘ \’; SCM substitutes the second line of file for ‘\’ (and the rest of the line), then appends any arguments given on the command line invoking this Scheme-Script.

When SCM executes the script, the Scheme variable *script* will be set to the script pathname. The last argument before ‘!#’ on the second line should be ‘-’; SCM will load the script file, preserve the unprocessed arguments, and set *argv* to a list of the script pathname and the unprocessed arguments.

Note that the interpreter, not the operating system, provides the ‘\’ substitution; this will only take place if interpreter is a SCM or SCSH interpreter.

— Read syntax: #! ignored !#

When the first two characters of the file being loaded are #! and a ‘\’ is present before a newline in the file, all characters up to ‘!#’ will be ignored by SCM read.

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

     #! /usr/local/bin/scm \

The following Scheme-Script prints factorial of its argument:

     #! /usr/local/bin/scm \ %0 %*
     - !#
     
     (define (fact.script args)
       (cond ((and (= 1 (length args))
                   (string->number (car args)))
              => (lambda (n) (print (fact n)) #t))
             (else (fact.usage))))
     
     (define (fact.usage)
       (print *argv*)
       (display "\
     Usage: fact N
       Returns the factorial of N.
     "
                (current-error-port))
       #f)
     
     (define (fact n) (if (< n 2) 1 (* n (fact (+ -1 n)))))
     
     (if *script* (exit (fact.script (list-tail *argv* *optind*))))
     ./fact 32
     ⇒
     263130836933693530167218012160000000

If the wrong number of arguments is given, fact prints its argv with usage information.

     ./fact 3 2
     -|
     ("./fact" "3" "2")
     Usage: fact N
       Returns the factorial of N.