Suppose the default shell for my account is zsh but I opened the terminal and fired up bash and executed a script named prac002.sh, which shell interpreter would be used to execute the script, zsh or bash? Consider the following example:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
# Which interpreter did it just use?

**EDIT : ** Here's the content of the script

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

3 Answers 3


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:


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.

  • I have even seen csh be used when #! was missing. I certainly consider the absence of #! in the beginning of a script to be an error. And having this workaround in shells probably does more harm than good. But it's good that you point out what POSIX says about it, I certainly wasn't aware of that.
    – kasperd
    Commented Dec 15, 2018 at 13:48
  • @kasperd It’s certainly not an error, it’s the only standard kind of script. If it’s a POSIX sh script no-shebang is even the most portable way to write it (you don’t know where a compatible shell lives). Running it with csh would be non-conforming, though. Commented Dec 15, 2018 at 17:20
  • the ZSH_VERSION does not always return something, the solution proposed below (i.e. to use realpath /proc/$$/exe is the most portable solution.
    – efx
    Commented Mar 30, 2020 at 8:39
  • 1
    @efx $ZSH_VERSION should always be undefined in this context. /proc is non-standard, as is realpath, so realpath /proc/$$/exe is not portable at all, though it will work on e.g. many (but not all) systems with GNU coreutils. The answer to the question at hand is that there is no specific implementation you can generally rely upon. Commented Mar 30, 2020 at 8:48

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 have myscript interpreted by bash in sh mode. While with xterm -e myscript with args, xterm will use execvp() so the script will be interpreted by sh.
  • su -c myscript on Solaris 10 where root's login shell is /bin/sh and /bin/sh is the Bourne shell will have myscript 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 (using execvp()) 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 by csh if it starts with #, with sh 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"

To check which shell is used, 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, sh command is implemented by bash and zsh picks sh if no shebang is specified.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .