Which shell interpreter runs a script with no shebang?
Because the script does not begin with a #!
shebang line indicating which interpreter to use, POSIX says that:
If the
execl()
function fails due to an error equivalent to the [ENOEXEC] error defined in the System Interfaces volume of POSIX.1-2008, the shell shall execute a command equivalent to having a shell invoked with the pathname resulting from the search as its first operand, with any remaining arguments passed to the new shell, except that the value of "$0" in the new shell may be set to the command name. If the executable file is not a text file, the shell may bypass this command execution. In this case, it shall write an error message, and shall return an exit status of 126.
That phrasing is a little ambiguous, and different shells have different interpretations.
In this case, Bash will run the script using itself. On the other hand, if you ran it from zsh instead, zsh would use sh
(whatever that is on your system) instead.
You can verify that behaviour for this case by adding these lines to the script:
echo $BASH_VERSION
echo $ZSH_VERSION
You'll note that, from Bash, the first line outputs your version, while the second never says anything, no matter which shell you use.
- If your
/bin/sh
is, say,dash
, then neither line will output anything when the script is executed from zsh or dash. - If your
/bin/sh
is a link to Bash, you'll see the first line output in all cases. - If
/bin/sh
is a different version of Bash than you were using directly, you'll see different output when you run the script from bash directly and from zsh.
The ps -p $$
command from rools's answer will also show useful information about the command the shell used to execute the script.
Since the file is not of any of the types of executable recognised by the system, and assuming you've got the permission to execute that file, the execve()
system call will typically fail with a ENOEXEC
(not an executable) error.
What happens then is up to the application and/or library function used to execute the command.
That can be for instance a shell, the execlp()
/execvp()
libc function.
Most other applications will use either of those when they run a command. They will invoke a shell for instance by way of the system("command line")
libc function which will typically invoke sh
to parse that command line (the path of which can be determined at compile time (like /bin/sh
vs /usr/xpg4/bin/sh
on Solaris)), or invoke the shell stored in $SHELL
by themselves like vi
with its !
command, or xterm -e 'command line'
and many other commands (su user -c
will invoke the user's login shell instead of $SHELL
).
Generally, a shebang-less text file that doesn't start with #
is considered as a sh
script. Which sh
it is will vary though.
execlp()
/execvp()
, upon execve()
returning ENOEXEC
will typically invoke sh
on it. For systems that have more than one sh
because they can conform to more than one standard, which sh
it is will be typically determined at compilation time (of the application using execvp()
/execlp()
by linking a different blob of code which refers to a different path to sh
). For instance, on Solaris, that will be either /usr/xpg4/bin/sh
(a standard, POSIX sh
) or /bin/sh
(the Bourne shell (an antiquated shell) on Solaris 10 and older, ksh93 in Solaris 11).
When it comes to shells, there's a lot of variation. bash
, AT&T ksh
, the Bourne shell will typically interpret the script themselves (in a child process unless exec
is used) after having simulated a execve()
, that is unset all the unexported variables, closed all the close-on-exec fds, removed all the custom traps, aliases, functions... (bash
will interpret the script in sh
mode). yash
will execute itself (with sh
as argv[0]
so in sh
mode) to interpret it.
zsh
, pdksh
, ash
-based shells will typically invoke sh
(the path of which determined at compilation time).
For csh
and tcsh
(and the sh
of some early BSDs), if the first character of the file is #
, then they will execute themselves to interpret it, and sh
otherwise. That goes back to a pre-shebang time where csh
did recognise #
as comments but not the Bourne shell, so the #
was a hint that it was a csh script.
fish
(at least version 2.4.0), just returns an error if execve()
fails (it doesn't attempt to treat it as a script).
Some shells (like bash
or AT&T ksh
) will first try to heuristically determine whether the file is probably meant to be a script or not. So you may find that some shells will refuse to execute a script if it's got a NUL character in the first few bytes.
Also note that if execve()
fails with ENOEXEC but the file does have a shebang line, some shells do try to interpret that shebang line themselves.
So a few examples:
- When
$SHELL
is/bin/bash
,xterm -e 'myscript with args'
will havemyscript
interpreted bybash
insh
mode. While withxterm -e myscript with args
,xterm
will useexecvp()
so the script will be interpreted bysh
. su -c myscript
on Solaris 10 whereroot
's login shell is/bin/sh
and/bin/sh
is the Bourne shell will havemyscript
interpreted by the Bourne shell./usr/xpg4/bin/awk 'BEGIN{system("myscript")'
on Solaris 10 will have it interpreted by/usr/xpg4/bin/sh
(same for/usr/xpg4/bin/env myscript
).find . -prune -exec myscript {} \;
on Solaris 10 (usingexecvp()
) will have it interpreted by/bin/sh
even with/usr/xpg4/bin/find
, even in a POSIX environment (a conformance bug).csh -c myscript
will have it interpreted bycsh
if it starts with#
, withsh
otherwise.
All in all, you can't be sure what shell will be used to interpret that script if you don't know how and by what it will be invoked.
In any case, read -p
is bash
-only syntax, so you'll want to make sure that script is interpreted by bash
(and avoid that misleading .sh
extension). Either you know the path of the bash
executable and use:
#! /path/to/bash -
read -p ...
Or you can try and rely on a $PATH
lookup of the bash
executable (assuming bash
is installed) by using:
#! /usr/bin/env bash
read -p ...
(env
is almost ubiquitously found in /usr/bin
). Alternatively, you can make it POSIX+Bourne compatible in which case you can use /bin/sh
. All systems will have a /bin/sh
. On most of them it will be (for the most part) POSIX-compatible, but you may still find now and then a Bourne shell there instead.
#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
When you do not have any #!
(called shebang) line, sh is used. To check that, you can run the following script.
ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe
On my computer I get
PID TTY TIME CMD
13718 pts/16 00:00:00 sh
The real shell is: /usr/bin/bash
even if my default shell is zsh. It uses bash since on my machine, the sh command is implemented by bash.