This SRFI describes basic prerequisites for running Scheme programs as Unix scripts in a uniform way. Specifically, it describes:
A user, given a Scheme program, has no standard way of running it, even if it is a single file written in R5RS-conformant Scheme, and if the underlying platform is known to be Unix. Almost every Scheme implementation provides an executable capable of starting up the Scheme system and load a particular file, but few pairs of Scheme implementation exist which share a convention for this.
This lack of de-facto standardization makes it impossible to write even a simple end-user program without also shipping a particular Scheme implementation with it or providing elaborate implementation-specific machinery. This SRFI describes a set of conventions which allow the creation of portable Unix scripts written in Scheme.
Unfortunately, the set of existing conventions among Scheme implementation makes it impossible to formulate these conventions in such a way as to remain compatible with all existing solutions to the problem. The Design Rationale section gives a brief overview.
<script> --> <script prelude>? <program> <script prelude> --> #! <space> <any character that isn't a line break>* <line break>
<script prelude> line may not be longer than
64 characters. (See here for a rationale.)
Systems supporting this SRFI provide a selection of binary executables
called Scheme script interpreters depending on the language dialects they support. They
provide any of
scheme-srfi-7 in the regular path. The invocation syntax for these interpreters
is always as follows:
<executable> <file> <argument> ...
It is recommended that the Scheme script interpreter resides somewhere
in the standard Unix path. Moreover, the recommended way to invoke
the Scheme script interpreter from the script is via a
/usr/bin/env trampoline, like this:
#! /usr/bin/env <executable>
A Scheme script interpreter loads the file specified by
<file>. It ignores the script prelude and interprets the rest of
the file according to the language dialect specified by the name
of the interpreter.
The Scheme script interpreter may also load a different file
after making a reasonable check that loading it is semantically
equivalent to loading
<file>. For example, the script interpreter may assume that
a file with a related name (say, with an additional extension) is a
compiled version of
<file>. (See also below under "Compilability".)
scheme-rnrs expects code written in RnRS Scheme.
scheme-ieee-n-y expects code written in IEEE n-y Scheme. Specifically,
scheme-r4rs expects code written in R4RS Scheme,
scheme-r5rs expects code written in R5RS Scheme, and
scheme-ieee-1178-1990 expects code written in IEEE 1178-1990 Scheme.
scheme-srfi-0 expects code written in R5RS Scheme using the extensions specified in SRFI 0.
scheme-srfi-7 expects code written in R5RS Scheme using the extensions specified in SRFI 7.
Upon invocation of a script, the Scheme system calls a procedure named
main with one argument,
a list of strings containing the Unix command-line arguments to the script, i.e. the
elements of the
argv vector of the Scheme script interpreter process from index 1 on.
Thus, the first element of the list is the name of the script.
main procedure should return an integer which becomes the exit status of
When, during the execution of the script, an error
is signalled (in the sense of R5RS, Section 1.3.2) the script returns immediately with the value of the C
EX_SOFTWARE as its exit status or 70, if
sysexits.h is unavailable.
main return anything other than an integer which would be a valid
exit status, the script also returns
In the above error situations, implementations are encouraged to
display a meaningful error message on
If the script interpreter allows the script access to the environment (via a future SRFI yet to be written), the environment seen by the script must be identical to that of the script interpreter upon its invocation.
A Scheme implementation supporting this SRFI
does not have to provide all of these script interpreters.
implementations are encouraged to provide
scheme-ieee-1178-1990 if they implement IEEE 1178-1990 or R5RS,
scheme-ieee-n-y if they implement another IEEE standard for Scheme,
scheme-rnrs if they implement RnRS for n>=4,
scheme-srfi-0 if they implement SRFI 0, and
scheme-srfi-7 if they implement SRFI 7.
In the case of
scheme-srfi-7 all specifications of filenames (marked by
<filename> in the syntax of SRFI 7) are string literals containing Unix-style filenames
which are absolute or relative to the directory the script resides in.
Scheme implementations with an interactive development environment which support SRFI 22 are encouraged to also support loading Scheme scripts into that environment.
Programmers who want their scripts to be compilable to native code are encouraged to provide an initial invocation line of the format
#! ... <executable>
<executable> is the name of one of the script interpreters from
the above list; it may carry a directory prefix such as in
It is expected that Scheme systems supporting compilation to native executables will use the first such line appearing in a script to determine the language dialect.
Here is a Scheme version of the Unix
#! /usr/bin/env scheme-r5rs (define (main arguments) (for-each display-file (cdr arguments)) 0) (define (display-file filename) (call-with-input-file filename (lambda (port) (let loop () (let ((thing (read-char port))) (if (not (eof-object? thing)) (begin (write-char thing) (loop))))))))
Most Unix Scheme implementation support writing Unix scripts in one form or another. Unfortunately, the invocation syntax as well as the syntax of the script itself vary from one implementation for another. However, the design decisions for this SRFI were made with some care:
Script interpreters adhering to this SRFI must be binary executables. They cannot be shell scripts because that would preclude them from being used directly in the invocation line of the prelude: most Unix variants require the script interpreter in the invocation line to be a binary.
The invocation line is optional to make it possible to write scripts which are standard Scheme files, and by invoking the Scheme script interpreter explicitly. This could make it possible (provided future script SRFIs for other environments follow the example) to write, say, makefiles which are portable among different environments.
This SRFI specifies a name but not an absolute
location for the Scheme script interpreter. Since most Unix
implementations require the interpreter in a script to be an
absolute filename, the only way to portably start
the interpreter is by calling a standard Unix program such as
/usr/bin/env (a so-called trampoline) as
shown in the example.
This is because there is no well-established convention for the location of third-party software on Unix systems. A convention common on one system or in one environment might be unimplementable in another. Moreover, trampolines are generally very cheap on Unix, and it is expected that the cost of Scheme script interpretation and execution will almost always dominate the cost of the trampoline.
Portability is a relative term in the context
of this SRFI: Posix and The Single Unix Specification do not guarantee
any method for automatic script execution, even though most Unix
implementations support the
#! convention. Moreover, neither of the two guarantees the presence
env executable in
/usr/bin. However, a wide range of systems do. The discussion archive contains
logs from a number of systems.
A more pertinent portability issue is the length of the script invocation line. Until recently, a number of Unix variants imposed a 32-character limit on that line. However, this limit seems to have been raised to 64 or disappeared with more recent versions of most of these systems. (In fact, all that we tested.) Again, the discussion archive contains more data. Since descriptive names for the Scheme script interpreters tend to exceed the 32-character limit on invocation lines, the limit it 64.
This SRFI specifies that the Scheme script interpreters will
communicate the command-line arguments to the script as a list argument
Some Scheme implementations use a special global variable that holds
the arguments. It is not clear that one alternative is inherently
preferable to another. Neither is it clear whether a vector or a list
is the more natural data structure.
However, explicitly specifying an entry point has the advantage
that scripts are easier to debug with a REPL-type Scheme implementation -
it is easily possible to call
main explicitly from the REPL,
demonstrably achieving the same effect as loading the script from
There were several discussions on the mailing list
about whether to pass command-line parameters as separate arguments
main procedure or in a data structure. Both offer slight usability
advantages depending on context. However, passing the parameters
as separate arguments opens up a conflict between semantic correctness
("What should be the exit code of a script with an arity error in the
main?") and ease of implementation (see this message and the subsequent discussion).
A previous draft of this SRFI required that language dialect, entry procedure, and script filename be specified via Unix-style command-line switches. This requires additional command-line parsing machinery. Moreover, this precludes starting the script interpreter directly instead of going through a trampoline on Unix systems which allow only one command-line argument to the script interpreter.
It seems that Windows script syntax is fundamentally incompatible with Unix script syntax, so it is impossible to write a single file which will run as a script on both Unix and Windows. (See also Marc Feeley's message on the subject.) It is feasible to also specify an alternate Script syntax which will work on Windows. However, this is not in the scope of this SRFI. However, Windows allows associating an executable with a file extension which might make Unix Scheme scripts runnable on a Windows system. (See Eli Barzilay's message on the subject.)
An implementation is necessarily very implementation-dependent. Moreover, it should be clear that an implementation of this SRFI is not very difficult in any Scheme implementation callable from a Unix shell. Therefore, this SRFI contains no reference implementation.
Copyright (C) Martin Gasbichler and Michael Sperber (2001). All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.