Previous: MS-DOS Compatible Scripts, Up: Scripting


3.13.3 Unix Shell Scripts

Scheme-scripts suffer from two 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 :;. 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.

The following example additionally sets *script* to the script argument, making it compatible with the scheme code of the previous example.

     #! /bin/sh
     :;exec scm -e"(set! *script* \"$0\")" -l$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 6
     ⇒ 720