7

I was told that the first line shebang (#!/bin/bash) is necessary in the shell script file, and the file won't run accurately without this line. But I tested some scripts. They work just fine without that line.

Is that line simply a comment? Does bash omit this line, as it does with other comments? Or does bash interpret it?

Is that line forced in that format? Or just a helpful readable note? What if I put it in other way, say, without the !?

jasonwryan
  • 73,126
user43312
  • 961
  • It should be noted that all answers so far assume you are running ./script.sh and not bash script.sh as in the latter case the #! is just a comment. – tobyodavies Jul 24 '13 at 05:59

5 Answers5

12

The kernel1 itself is clear on this.  The shebang must be there; otherwise the kernel does not consider such a file to be an executable file (script).

In Linux, the exec system call (a.k.a. execve) calls the following code in linux/fs/binfmt_script.c.

static int load_script(struct linux_binprm *bprm)
{
[...]
    if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
            return -ENOEXEC;

However, if the shebang is missing, the shell may choose to execute the file itself.  See Which shell executes scripts when there's no shebang line (#!/path/to/shell) at the beginning?  So it seems to work for shell scripts, but it's not a feature you should rely on.

However what if your shell script uses bashisms? You must have the #!/bin/bash then so it will be executed in bash and not some other shell. Also so it will be executed at all if the program trying to execute it isn't a shell itself. Then there are scripts in completely different languages, such as Perl or Python. Without shebang, the system will not know which interpreter to use for those.

______________
1 This is a Unix feature that way predates Linux; see Shebang History.

frostschutz
  • 48,978
  • It's not only shells that will execute it in an instance of themselves, it's all the standard utilities and all the functions executing commands in the standard C API (execl(), execp(), system(), popen()). – Stéphane Chazelas Jul 24 '13 at 02:01
9

The shebang tells the kernel which program to invoke when the script is executed directly.

chmod +x myscript.sh
./myscript.sh
3

The shebang is only necessary if one of these conditions is true:

  • your script is non portable, i.e. uses something not supported by the POSIX standard (eg: bashisms, kshisms or whatever) so need to specify what interpreter should be launched. This obviously includes alien scripting languages (csh, perl, python, ...). As your question is about #!/bin/bash, you are likely in that case.

  • you are running your script on a non POSIX environment/mode.

Otherwise, the shebang is not required and is even actually not recommended for strictly conforming shell scripts. The reason being despite popular belief, /bin/sh is not necessarily the default path for a POSIX shell. The only supported way to retrieve the standard shell location is running

PATH=`getconf PATH` command -v sh

The POSIX standard states:

If the first line of a file of shell commands starts with the characters "#!", the results are unspecified.

jlliagre
  • 61,204
  • Interesting citation from the standard. I would read that as "If the first line ... the results are unspecified with respect to this shell section of the standard" because, as noted elsewhere in this question, other things might happen before a POSIX compliant shell gets to read it. The converse of that statement might be "everything else in this spec may behave exactly as documented here even if it starts with #! but we don't assert that". This could be true for every system and yet still be "undefined". – msw Jul 24 '13 at 01:08
  • @msw My reply states the shebang is not required if you are running in a POSIX environment and mode. This rules out the shell script not being processed by a POSIX compliant shell. The main issue with #! and portable scripts is the interpreter should be adjusted to the target platform at installation time as unfortunately, the POSIX committee didn't provide a portable way to specify the POSIX shell here. – jlliagre Jul 24 '13 at 01:26
  • Most scripts these days are not portable, but rely on non-POSIX features such as arrays etc. – tripleee Jul 24 '13 at 06:11
  • @tripleee Or not. That really depends on who is writing them. I guess a lot of scripts starting with #!/bin/bash are portable by accident by not using any bashism or other common extension like arrays. – jlliagre Jul 24 '13 at 06:46
3

If the first line of a file starts with #!, the system treats it as a script whose interpreter follows #! (and most interpreters ignore that line as # is the comment leader in most of them). However the syntax of that shebang line and how it is interpreted varies from system to system and is unspecified by POSIX (though in the case of #! /bin/bash alone, there's no ambiguity).

If you omit the shebang line on a text file, when executed by a POSIX conformant utility like env, xargs, find's -exec, awk's system(), vi, sh... or POSIX conformant C library functions like execl(), execp(), system(), popen()... in a POSIX environment, the file will be interpreted by a POSIX conformant sh.

That's what POSIX specifies and same goes for Unix instead of POSIX.

Now, that leaves some uncertainty for what happens when the conditions described above are not meant.

For instance on all systems, the execve() system call will fail to execute those scripts without a shebang. It's not the kernel that implements that logic of starting a POSIX sh in that case, it's userspace applications/functions upon execve actually returning ENOEXEC upon attempted execution of the file.

What that means is that if an application (typically not one specified by POSIX) doesn't rely on POSIX execl(), system()... to execute a command but call execve() directly, they will fail to execute those scripts. Rare but happens.

Now there's the question of the POSIX environment and what shell non-POSIX applications will call.

POSIX allows systems not to conform based on the environment, they even allow the POSIX environment not to be the default.

For instance, Solaris before Solaris 11, had several implementations of sh (and other standard utilities), one standard in /usr/xpg4/bin or /usr/xpg6/bin and a historical (non-standard) one in /bin. The default $PATH had /bin before /usr/xpg4/bin. Also, what shell things like system() called depended on how the application using them was compiled (with the default being the non-POSIX /bin/sh shell). Even POSIX utilities there (like IIRC /usr/xpg4/bin/awk) failed to start the proper shell in some circumstances (like in print | "some command" in awk IIRC).

What that meant is that on Solaris, when omiting the shebang, you could never be sure whether the script was going to be interpreted by a standard sh or by the old Bourne shell in /bin.

So, for portability, even if POSIX implies that you should not use a shebang if you want your script interpreted by a POSIX sh, in practice, you generally have to and need to adapt the path depending on the system (like #! /usr/xpg4/bin/sh - on Solaris and #! /bin/sh - on most other systems).

Now, if you want your script to be interpreted by bash (for instance because it uses bash extensions over the standard sh syntax), then there's no question, we're out of what POSIX specifies anyway, and you need to use a shebang there with the correct path to the bash interpreter on the system that script is to be run.

1

As an addition to frostschutz's answer, some shells have the capability to interpret hash bang (shebang) lines. Bash normally only enables this feature if the kernel does not support hash bang interpretation.

However, if you wish to play (hack) around with your shebangs lines, it may be easier to play around with them in Bash's source than in the kernel source.

From bash/execute_cmd.c:

#if !defined (HAVE_HASH_BANG_EXEC)
    if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
        return (execute_shell_script (sample, sample_len, command, args, env));
    else
#endif

From bash/config.h:

/* Define if the kernel can exec files beginning with #! */
#define HAVE_HASH_BANG_EXEC 1