Shell Script Working Fine Without Shebang Line? Why

Shell script working fine without shebang line? Why?

shebang line is needed in the file and only if it's meant to be run as executable (as opposed to sh file.sh invocation. It is not actually needed by script, it is for the system to know how to find interpreter.

EDIT: Sorry for misreading the question. If the shebang line is missing or not recognized, /bin/sh is used. But I prefer being explicit about the interpreter.

Note, that this behavior is not universal, IIRC, only some exec* family function do that (not to mention different platforms), so that's another reason to be explicit here.

Writing a Bash script without the shebang line

Not having a shebang in the beginning of your script will get it executed in whatever shell is currently running when the script was invoked. If you know for sure that the script will be launched from bash, and not from any other shell (ksh, csh, etc.), there is no need for a shebang, because the same interpreter will be launched.

Bash script doesn't work without shebang BUT the shebang results in an error message

You have an encoding problem.

Look at: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8

A text editor or web browser misinterpreting the text as ISO-8859-1 or CP1252 will display the characters  for this.

Save your file as UTF-8 without BOM or use system editor (vi, ...)

Should I use a Shebang with Bash scripts?

On UNIX-like systems, you should always start scripts with a shebang line. The system call execve (which is responsible for starting programs) relies on an executable having either an executable header or a shebang line.

From FreeBSD's execve manual page:

 The execve() system call transforms the calling process into a new
process. The new process is constructed from an ordinary file, whose
name is pointed to by path, called the new process file.
[...]

This file is
either an executable object file, or a file of data for an interpreter.

[...]

An interpreter file begins with a line of the form:

#! interpreter [arg]

When an interpreter file is execve'd, the system actually execve's the
specified interpreter. If the optional arg is specified, it becomes the
first argument to the interpreter, and the name of the originally
execve'd file becomes the second argument

Similarly from the Linux manual page:

execve() executes the program pointed to by filename. filename must be
either a binary executable, or a script starting with a line of the
form:

#! interpreter [optional-arg]

In fact, if a file doesn't have the right "magic number" in it's header, (like an ELF header or #!), execve will fail with the ENOEXEC error (again from FreeBSD's execve manpage):

[ENOEXEC] The new process file has the appropriate access
permission, but has an invalid magic number in its
header.


If the file has executable permissions, but no shebang line but does seem to be a text file, the behaviour depends on the shell that you're running in.

Most shells seem to start a new instance of themselves and feed it the file, see below.

Since there is no guarantee that the script was actually written for that shell, this can work or fail spectacularly.

From tcsh(1):

   On  systems which do not understand the `#!' script interpreter conven‐
tion the shell may be compiled to emulate it; see the version shell
variable. If so, the shell checks the first line of the file to see if
it is of the form `#!interpreter arg ...'. If it is, the shell starts
interpreter with the given args and feeds the file to it on standard
input.

From FreeBSD's sh(1):

If the program is not a normal executable file (i.e., if it
does not begin with the “magic number” whose ASCII representation is
“#!”, resulting in an ENOEXEC return value from execve(2)) but appears to
be a text file, the shell will run a new instance of sh to interpret it.

From bash(1):

   If this execution fails because the file is not in  executable  format,
and the file is not a directory, it is assumed to be a shell script, a
file containing shell commands. A subshell is spawned to execute it.

You cannot always depend on the location of a non-standard program like bash. I've seen bash in /usr/bin, /usr/local/bin, /opt/fsf/bin and /opt/gnu/bin to name a few.

So it is generally a good idea to use env;

#!/usr/bin/env bash

If you want your script to be portable, use sh instead of bash.

#!/bin/sh

While standards like POSIX do not guarantee the absolute paths of standard utilities, most UNIX-like systems seem to have sh in /bin and env in /usr/bin.

Bash script execution with and without shebang in Linux and BSD

Since this happens in dash and dash is simpler, I looked there first.

Seems like exec.c is the place to look, and the relevant functionis are tryexec, which is called from shellexec which is called whenever the shell things a command needs to be executed. And (a simplified version of) the tryexec function is as follows:

STATIC void
tryexec(char *cmd, char **argv, char **envp)
{
char *const path_bshell = _PATH_BSHELL;

repeat:

execve(cmd, argv, envp);

if (cmd != path_bshell && errno == ENOEXEC) {
*argv-- = cmd;
*argv = cmd = path_bshell;
goto repeat;
}
}

So, it simply always replaces the command to execute with the path to itself (_PATH_BSHELL defaults to "/bin/sh") if ENOEXEC occurs. There's really no magic here.

I find that FreeBSD exhibits identical behavior in bash and in its own sh.

The way bash handles this is similar but much more complicated. If you want to look in to it further I recommend reading bash's execute_command.c and looking specifically at execute_shell_script and then shell_execve. The comments are quite descriptive.

Do you need shebang in all bash scripts?

The shebang is only mandatory for those scripts, which shall be executed by the operating system in the same way as binary executables. If you source in another script, then the shebang is ignored.

On the other hand. IF a script is supposed to be sourced, then it is convention to NOT put any shebang at the start.

Use of shebang in shell scripts

There’a subtle distinction here. If the target is a binary or begins with a #! shebang line, then the shell calls execv successfully. If the target is a text file without a shebang, then the call to execv will fail, and the shell is free to try launching it under /bin/sh or something else.

Can I omit the full path of a shebang on the first line of shell script?

Using interpreter without full path is not supported everywhere. In many cases, kernel does the shebang handling and kernel normally does not know about the environment variables, including PATH. So it needs a full path in order to find the interpreter.

That's why #!/usr/bin/env bash is used - bash is located and executed by env, so it allows bash to be anywhere in PATH, while only requiring /usr/bin/env to be in known location. You can't use just #!bash or #!env bash, because kernel does not know, where to find them.

Using plain interpreter may work sometimes - it means that shebang handling is done by something that is aware of the environment, most likely by the active shell itself and not by the kernel.

There are good answers about the similar question also available:

  • https://unix.stackexchange.com/questions/77512/why-not-use-pathless-shebangs
  • https://unix.stackexchange.com/questions/29608/why-is-it-better-to-use-usr-bin-env-name-instead-of-path-to-name-as-my

Why should the shebang line always be the first line?

The shebang must be the first line because it is interpreted by the kernel, which looks at the two bytes at the start of an executable file. If these are #! the rest of the line is interpreted as the executable to run and with the script file available to that program. (Details vary slightly, but that is the picture).

Since the kernel will only look at the first two characters and has no notion of further lines, you must place the hash bang in line 1.

Now what happens if the kernel can't execute a file beginning with #!whatever? The shell, attempting to fork an executable and being informed by the kernel that it can't execute the program, as a last resort attempts to interpret the file contents as a shell script. Since the shell is not perl, you get a bunch of errors, exactly the same as if you attempted to run

 sh temp.pl


Related Topics



Leave a reply



Submit