SCRIPT(7) BSD Reference Manual SCRIPT(7)
script - interpreter script execution
The system is capable of treating a text file containing commands intend- ed for an interpreter, such as sh(1) or awk(1), as an executable program. An "interpreter script" is a file which has been set executable (see chmod(2)) and which has a first line of the form: #! pathname [argument] The '#!' must appear as the first two characters of the file. A space between the '#!' and pathname is optional. At most one argument may fol- low pathname, and the length of the entire line is limited (see below). If such a file is executed (such as via the execve(2) system call), the interpreter specified by the pathname is executed by the system. (The pathname is executed without regard to the PATH variable, so in general pathname should be an absolute path.) The arguments passed to the interpreter will be as follows. argv will be the path to the interpreter itself, as specified on the first line of the script. If there is an argument following pathname on the first line of the script, it will be passed as argv. The subsequent elements of argv will be the path to the interpreter script file itself (i.e. the original argv) followed by any further arguments passed when execve(2) was invoked to execute the script file. By convention, it is expected that an interpreter will open the script file passed as an argument and process the commands within it. Typical interpreters treat '#' as a comment character, and thus will ignore the initial line of the script because it begins '#!', but there is no re- quirement for this per se. On OpenBSD, the length of the '#!' line is limited to MAXINTERP, as de- fined in <sys/param.h>. Other operating systems impose different limits on the length of the '#!' line (see below). Note that the interpreter may not itself be an interpreter script. If pathname does not point to an executable binary, execution of the inter- preter script will fail.
Different operating systems often have interpreters located in different locations, and the kernel executes the passed interpreter without regard to the setting of environment variables such as PATH. This makes it some- what challenging to set the '#!' line of a script so that it will run identically on different systems. Since the env(1) utility executes a command passed to it on its command line, it is often used as a "trampoline" to render scripts portable. If the leading line of a script reads #! /usr/bin/env interp then the env(1) command will execute the "interp" command it finds in its PATH, passing on to it all subsequent arguments with which it itself was called. Since /usr/bin/env is found on almost all POSIX style systems, this trick is frequently exploited by authors who need a script to exe- cute without change on multiple systems.
Shell scripts predate the invention of the '#!' convention, which is im- plemented in the kernel. In the days of Version 7 AT&T UNIX, there was only one interpreter used on the system, /bin/sh, and the shell treated any file that failed to execute with an ENOEXEC error (see intro(2)) as a shell script. Most shells (such as sh(1)) and certain other facilities (including execlp(3) and execvp(3) but not other types of exec(3) calls) still pass interpreter scripts that do not include the '#!' (and thus fail to exe- cute with ENOEXEC) to /bin/sh. As this behavior is implemented outside the kernel, there is no mechanism that forces it to be respected by all programs that execute other pro- grams. It is thus not completely reliable. It is therefore important to always include #!/bin/sh in front of Bourne shell scripts, and to treat the traditional behavior as obsolete.
Suppose that an executable binary exists in /bin/interp and that the file /tmp/script contains: #!/bin/interp -arg [...] and that /tmp/script is set mode 755. Executing $ /tmp/script one two three at the shell will result in /bin/interp being executed, receiving the following arguments in argv (numbered from 0): "/bin/interp", "-arg", "/tmp/script", "one", "two", "three"
The behavior of multiple arguments on the '#!' line is highly non- portable between different systems. In general, only one argument can be assumed to work consistently. Consider the following variation on the previous example. Suppose that an executable binary exists in /bin/interp and that the file /tmp/script contains: #!/bin/interp -x -y [...] and that /tmp/script is set mode 755. Executing $ /tmp/script one two three at the shell will result in /bin/interp being executed, receiving the following arguments in argv (numbered from 0): "/bin/interp", "-x -y", "/tmp/script", "one", "two", "three" Note that "-x -y" will be passed on OpenBSD as a single argument. Although most POSIX style operating systems will pass only one argument, the behavior when multiple arguments are included is not consistent between platforms. Some, such as OpenBSD, will concatenate multiple argu- ments into a single argument (as above), some will truncate them, and at least one will pass them as multiple arguments. The OpenBSD behavior is common but not universal. Sun's Solaris would present the above argument as "-x", dropping the "-y" entirely. Perhaps uniquely, recent versions of Apple's OS X will actually pass multiple ar- guments properly, i.e.: "/bin/interp", "-x", "-y", "/tmp/script", "one", "two", "three" The behavior of the system in the face of multiple arguments is thus not currently standardized, should not be relied on, and may be changed in future releases. In general, pass at most one argument, and do not rely on multiple arguments being concatenated.
awk(1), csh(1), ksh(1), sh(1), chmod(2), execve(2), intro(2), execlp(3), execvp(3)
The behavior of interpreter scripts is obliquely referred to, but never actually described in, IEEE Std 1003.1-2004 ("POSIX"). The behavior is partially (but not completely) described in the System V Interface Definition, Fourth Edition ("SVID4"). Although it has never been formally standardized, the behavior described is largely portable across POSIX style systems, with two significant ex- ceptions: the maximum length of the '#!' line, and the behavior if multi- ple arguments are passed. Please be aware that the behavior in the face of multiple arguments is not consistent across systems.
The behavior of the kernel when encountering scripts that start in '#!' was not present in Version 7 AT&T UNIX. A Usenet posting to net.unix by Guy Harris on October 16, 1984 claims that the idea for the '#!' behavior was first proposed by Dennis Ritchie but that the first implementation was on BSD. Historical manuals (specifically the exec man page) indicate that the behavior was present in 4BSD at least as early as April, 1981. Informa- tion on precisely when it was first implemented, and in which version of UNIX, is solicited.
Numerous security problems are associated with setuid interpreter scripts. In addition to the fact that many interpreters (and scripts) are simply not designed to be robust in a setuid context, a race condition exists between the moment that the kernel examines the interpreter script file and the moment that the newly invoked interpreter opens the file itself. Subtle techniques can be used to subvert even seemingly well written scripts. Scripts executed by Bourne type shells can be subverted in numerous ways, such as by setting the IFS variable before executing the script. Other interpreters possess their own vulnerabilities. Setting the Set-user-ID on execution (SUID) bit is therefore very dangerous, and should not be done lightly, if at all. MirOS BSD #10-current May 6, 2005 2
Generated on 2014-02-10 02:47:05 by $MirOS: src/scripts/roff2htm,v 1.79 2014/02/10 00:36:11 tg Exp $
These manual pages and other documentation are copyrighted by their respective writers;
their source is available at our CVSweb,
AnonCVS, and other mirrors. The rest is Copyright © 2002‒2014 The MirOS Project, Germany.
This product includes material provided by Thorsten Glaser.
This manual page’s HTML representation is supposed to be valid XHTML/1.1; if not, please send a bug report – diffs preferred.