This post is going to be about a bit of POSIX trivia which is not likely to be very useful in practice. Still, it's an interesting bit of trivia, so if that interests you, read on.
I'm going to assume that you know what shebangs are. If you wanted
to make an executable script that is run with Bash, you would add a
line like #!/bin/bash
to the top.
However, shebangs are not actually portable under POSIX. First of
all, the shebang itself (#!
) is not defined under
POSIX. Second of all, the paths of POSIX utilities are not
specified. sh
could be located at /bin/sh
or /usr/bin/sh
or
/screw/you/this/is/still/posix/sh
. The same goes
for env
, so #!/usr/bin/env bash
is also
not portable.
There is a way to make an actually portable executable Bash script
under POSIX (assuming bash
is somewhere in
your PATH
of course), by taking advantage of a little
known behavior in POSIX. When you execute a file, and the file does
not match a binary executable format, then the OS will execute the
default shell and pass the file as the first argument.
There are two distinct ways in which the contents of the process image file may cause the execution to fail, distinguished by the setting of errno to either [ENOEXEC] or [EINVAL] (see the ERRORS section). In the cases where the other members of the exec family of functions would fail and set errno to [ENOEXEC], the execlp() and execvp() functions shall execute a command interpreter and the environment of the executed command shall be as if the process invoked the sh utility using execl() as follows:
execl(<shell path>, arg0, file, arg1, ..., (char *)0);
where <shell path> is an unspecified pathname for the sh utility, file is the process image file, and for execvp(), where arg0, arg1, and so on correspond to the values passed to execvp() in argv[0], argv[1], and so on.
Note how it specifically points out that the shell path is unspecified.
Anyway, we can use this to write a portable Bash script like so:
# This will be run with the default shell, usually "/bin/sh".
# This stanza is valid in both POSIX shell and Bash.
# If we aren't in Bash, then we exec bash from the PATH.
# In Bash, BASH_VERSION is set so we will skip this.
# On OSX, you can check the version if you're trying to use a newer Bash.
if [ -z "${BASH_VERSION}" ]; then
exec bash "$0" "$@"
fi
# Put the Bash script contents here.
echo "${BASH_VERSION}"
echo "$0" "$@"
Unfortunately, this cannot replace all uses of shebangs, only those for programs that can tolerate the initial POSIX shell stanza. Also, you would never need to do this in practice since all Unix-like OSes in common use support the familiar shebang behavior. Still, it is an interesting bit of trivia to discuss over lunch.