How to write Unix shell scripts

When MzScheme is installed as part of the standard Unix PLT distribution, the executable plt/bin/mzscheme is a script that selects a binary executable based on the current platform. This provides support for multiple platforms in a single PLT installation.

Unfortunately, launching MzScheme via a script causes problems for writing MzScheme scripts in Unix, because the shell's #! convention normally requires a binary executable.

Fortunately, there is a sneaky workaround. To write a MzScheme script, place the following header at the top of your script:

  #!/bin/sh
  string=? ; exec ${PLTHOME}/bin/mzscheme -r $0 $0 ${1+"$@"}

  ... scheme-program ...

(Depending on your installation, it may be better to hardwire the path to mzscheme instead of using ${PLTHOME}.)

Within scheme-program, (vector-ref argv 0) is the name of the script; arguments passed to the script start at (vector-ref argv 1).

How does this work? "#!/bin/sh" causes the shell to run /bin/sh on the file. The first command, "string=?" is a harmless assignment, and ";" separates this assignment from the "exec" command. The "exec" command replaces the /bin/sh process with mzscheme (so /bin/sh does not see the actual Scheme program). The "-r" flag tells mzscheme to load the next argument as a script and forces the remaining arguments into argv. (For information on ${1+"$@"}, consult your `sh' man page.) Thus, mzscheme will initialize itself and then load the script file. As always, mzscheme ignores "#!" in the first line of a loaded file as a comment. And "string=?" is again a harmless expression (since it's a built-in variable). The ";" starts a Scheme comment, so the "exec" expression is ignored. Finally, mzscheme continues to load the file and evaluates the real Scheme program.

Thanks to J. P. Lewis for the basic idea behind this trick. Many others have suggested alternatives to string=? for shells other than /bin/sh. A similar technique has been applied to Windows batch files.