44

I am using Bash

$ echo $SHELL
/bin/bash

and starting about a year ago I stopped using Shebangs with my Bash scripts. Can I benefit from using #!/bin/sh or #!/bin/bash?

Update: In certain situations a file is only treated as a script with the Shebang, example

$ cat foo.sh
ls

$ cat bar.sh
#!/bin/sh
ls

$ file foo.sh bar.sh
foo.sh: ASCII text
bar.sh: POSIX shell script, ASCII text executable
Zombo
  • 1
  • 62
  • 391
  • 407
  • 2
    I think you can benefit from using `#!/bin/bash` if you run your script on another machine. If that machine uses a default shell different from Bash, you can be sure that your script will run with the intended shell. – Mariano D'Ascanio Aug 06 '14 at 16:52
  • 1
    I guess the shebang line is good if you're running a non-POSIX shell by default but want to ensure that the script is actually executed in a POSIX compatible shell (or vice versa, you're using features inside your script that aren't POSIX default, you can only ensure that the script will run fine even if started from a POSIX shell like bash). So in your case, there won't be any difference. – ConcurrentHashMap Aug 06 '14 at 16:53
  • 3
    Always, always, always. If you don't use a shebang line, you're not just making your script needlessly unportable, you're preventing it from running at all in a fairly wide array of circumstances. – Charles Duffy Aug 06 '14 at 16:55
  • See http://homepages.cwi.nl/~aeb/std/hashexclam-1.html#ss1.1 and http://www.in-ulm.de/~mascheck/various/shebang/ for more details than you want to know about the history and portability of various shebang line details. – Etan Reisner Aug 08 '14 at 17:28
  • I can't find them at the moment but I know I was involved with at least two questions on this site about errors from shell scripts that resulted from a bash-specific script being executed by `dash` or `sh` instead because of a lack of specification (like a shebang line provides). – Etan Reisner Aug 08 '14 at 17:39
  • The fact that `SHELL` is `/bin/bash` says nothing about which shell you are using. If you execute `csh`, it will not change SHELL. You can `eval ksh`, or `eval zsh`, or `csh -c zsh`, and SHELL will retain its value (unless the startup scripts for a particular shell change it, which they really ought not do). Demonstrating the value of SHELL says *nothing* about which shell you are using. – William Pursell Jun 03 '22 at 11:38

8 Answers8

59

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.

Zombo
  • 1
  • 62
  • 391
  • 407
Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • 1
    There are arguments for and against the `#!/usr/bin/env` hack. See [this question](http://unix.stackexchange.com/q/29608/10454) and [my answer](http://unix.stackexchange.com/a/29620/10454). – Keith Thompson Aug 08 '14 at 17:40
  • 1
    "most" != "all". You are ruling out one of the most deployed commercial Unix by assuming `/bin/sh` is a POSIX compliant shell. – jlliagre Aug 13 '14 at 07:19
  • 1
    Having a `/bin/sh` is pretty common. I'm not assuming `sh` is POSIX compliant. – Roland Smith Aug 13 '14 at 17:55
  • 1
    This somewhat contradict "If you want your script to be portable". The script will indeed start whatever the platform as /bin/sh is there, but the script might fail or behave differently at least on Solaris 10 and olders, as the SVR4 Bourne shell is lacking some features most script developers expects will work. – jlliagre Aug 13 '14 at 22:19
  • 1
    My remark was in the context that these days common assumptions are that UNIX means GNU/Linux and a shell means `bash`. – Roland Smith Aug 13 '14 at 22:43
  • It's worth noting that [shebang is only required for executable scripts](https://stackoverflow.com/a/7615477/9157799). – M Imam Pratama Oct 30 '21 at 12:15
14

Scripts should always begin with a shebang line. If a script doesn't start with this, then it may be executed by the current shell. But that means that if someone who uses your script is running a different shell than you do, the script may behave differently. Also, it means the script can't be run directly from a program (e.g. the C exec() system call, or find -exec), it has to be run from a shell.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 2
    Absent a shebang, most(?) OSes will try to execute a script using the system shell. – chepner Aug 06 '14 at 17:13
  • I guess it's changed over time and between OSes. – Barmar Aug 06 '14 at 18:26
  • 1
    "the system shell" is the dangerous part of @chepner's comment and the exact reason why you should use one, which is the OP's question. – Etan Reisner Aug 08 '14 at 17:27
  • 2
    Without a shebang, scripts will be executed by `/bin/sh`, which could be any of several different Bourne-like shells. Depending on the system, it might be a symlink to `dash`, `bash`, or `ksh`, for example. – Keith Thompson Aug 08 '14 at 17:41
  • 1
    Actually, I'm not sure that they're *guaranteed* to be executed by `/bin/sh`. The `#!` method is not standardized by POSIX (to my surprise). – Keith Thompson Aug 08 '14 at 18:37
  • 1
    Without a shebang, the script will be executed by the POSIX shell (either directly or through `find -exec`) when you are in a POSIX environment running in a POSIX platform. The POSIX shell doesn't need and is not guaranteed to be `/bin/sh`. Specifying this PATH breaks portability in at least one well known and still commonly used commercial Unix platform so shouldn't be hardcoded by a shebang. – jlliagre Aug 12 '14 at 06:05
  • 1
    If the script without a shebang line is started from a shell, most shells start a copy of themselves and feed it the file. See the manpage references in my answer below. – Roland Smith Aug 12 '14 at 22:42
  • @RolandSmith That's what I wrote in my second sentence: _it may be executed by the current shell_. – Barmar Aug 13 '14 at 16:02
4

You might be interested in an early description by Dennis M Ritchie (dmr) who invented the #! :

From uucp Thu Jan 10 01:37:58 1980

.>From dmr Thu Jan 10 04:25:49 1980 remote from research

The system has been changed so that if a file being executed begins with the magic characters #! , the rest of the line is understood to be the name of an interpreter for the executed file. Previously (and in fact still) the shell did much of this job; it automatically executed itself on a text file with executable mode when the text file's name was typed as a command. Putting the facility into the system gives the following benefits.

1) It makes shell scripts more like real executable files, because they can be the subject of 'exec.'

2) If you do a 'ps' while such a command is running, its real name appears instead of 'sh'. Likewise, accounting is done on the basis of the real name.

3) Shell scripts can be set-user-ID.

4) It is simpler to have alternate shells available; e.g. if you like the Berkeley csh there is no question about which shell is to interpret a file.

5) It will allow other interpreters to fit in more smoothly.

To take advantage of this wonderful opportunity, put

   #! /bin/sh

at the left margin of the first line of your shell scripts. Blanks after ! are OK. Use a complete pathname (no search is done). At the moment the whole line is restricted to 16 characters but this limit will be raised.

Hope this helps

jrjc
  • 21,103
  • 9
  • 64
  • 78
  • 2
    I think the line length restriction was also raised a while back. However, I believe most implementations limit it to one argument in the shebang line. – Barmar Aug 12 '14 at 15:48
  • 2
    I should have state more clearly that it was the description from 1980 (despite the link). I edit for that. Note that this answer was aimed at providing some historical perspectives about what was `#!` invented for. – jrjc Aug 12 '14 at 15:59
  • @StevenPenny Unfortunately I don't have other information than what can be found in the other answers. – jrjc Aug 13 '14 at 14:20
3
  • If you write bash scripts, i.e. non portable scripts containing bashisms, you should keep using the #!/bin/bash shebang just to be sure the correct interpreter is used. You should not replace the shebang by #!/bin/sh as bash will run in POSIX mode so some of your scripts might behave differently.

  • If you write portable scripts, i.e. scripts only using POSIX utilities and their supported options, you might keep using #!/bin/sh on your system (i.e. one where /bin/sh is a POSIX shell).

  • It you write stricly conforming POSIX scripts to be distributed in various platforms and you are sure they will only be launched from a POSIX conforming system, you might and probably should remove the shebang as stated in the POSIX standard:

As it stands, a strictly conforming application must not use "#!" as the first two characters of the file.

The rationale is the POSIX standard doesn't mandate /bin/sh to be the POSIX compliant shell so there is no portable way to specify its path in a shebang. In this third case, to be able to use the 'find -exec' syntax on systems unable to run a shebangless still executable script, you can simply specify the interpreter in the find command itself, eg:

find /tmp -name "*.foo" -exec sh -c 'myscript "$@"' sh {} + 

Here, as sh is specified without a path, the POSIX shell will be run.

jlliagre
  • 29,783
  • 6
  • 61
  • 72
  • 1
    As I understand it, POSIX doesn't even specify that `#!` causes it to run an interpreter at all. Everything about shebang is a _de facto_ standard, not part of the official specification. – Barmar Aug 12 '14 at 15:44
  • @StevenPenny In a posix conforming environment, the first `sh` tells `find` to run the POSIX shell as it is guaranteed to be the first one found in the PATH. The second `sh` is setting `$0`, it could be almost anything but `sh` makes sense for a shell. `{} +` is filling the potential remaining arguments ($1, $2, ...) that will be passed to the `myscript` command. – jlliagre Aug 15 '14 at 20:30
2

TL;DR: always in scripts; please not in source'd scripts

  • Always in your parent

    FYI: POSIX compliant is #!/bin/bash, not #!/bin/sh

    You want to clarify this so that nothing else overrides the interpreter your script is made for.

    • You don't want a user at the terminal using zsh to have trouble if your script was written for POSIX bash scripts.
    • You don't want to run source in your #!/bin/bash unrecognized by #!/bin/sh, someone in an sh terminal have it break the script because it is expecting the simple/POSIX . for including source'd files
    • You don't want e.g. zsh features - not available in other interpreters - to make their way into your bash code. So, put #!/bin/bash in all your script headers. Then, any of your zsh habits in your script will break so you know to remove them before your roll-out.

    It's probably best, especially so POSIX-compliant scripts don't break in a terminal like zsh.

  • Not expected for included source scripts

    FYI: POSIX compliant for sourcing text in a BASH script is ., not source

    You can use either for sourcing, but I'll do POSIX.

    Standard "shebanging" for all scripting:

    parent.sh:

    #!/bin/bash
    
    echo "My script here"
    
    . sourced.sh # child/source script, below
    

    sourced.sh:

    echo "I am a sourced child script"
    

    But, you are allowed to do this...

    sourced.sh: (optional)

    #!/bin/bash
    echo "I am a sourced child script"
    

    There, the #!/bin/bash "shebang" will be ignored. The main reason I would use it is for syntax highlighting in my text editor. However, in the "proper" scripting world, it is expected that your rolled-out source'd script will not contain the shebang.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Jesse
  • 750
  • 1
  • 9
  • 25
  • 1
    FYI: bash _also_ has features/syntax that are not baseline POSIX. – jonrsharpe Jun 03 '22 at 11:25
  • @jonrsharpe Oh, for sure, such as `source` as I wrote. – Jesse Jun 03 '22 at 11:27
  • This answer was made in lieu of the comments on the [SO meta](https://meta.stackoverflow.com/questions/418503/what-to-do-about-this-special-dup-bash-script-vs-bash-include#comment913541_418503) – Jesse Jun 03 '22 at 11:29
1

The header is useful since it specifies which shell to use when running the script. For example, #!/bin/zsh would change the shell to zsh instead of bash, where you can use different commands.

For example, this page specifies the following:

Using #!/bin/sh, the default Bourne shell in most commercial variants of UNIX, makes the script portable to non-Linux machines, though you sacrifice Bash-specific features ...

pyrrhic
  • 1,769
  • 2
  • 15
  • 27
0

$SHELL and #!/bin/bash or #!/bin/sh are different.

To start, #!/bin/sh is a symlink to /bin/bash on most Linux systems (on Ubuntu it is now /bin/dash)

But on whether to start with /bin/sh or /bin/bash:

Bash and sh are two different shells. Basically bash is sh, with more features and better syntax. Most commands work the same, but they are different.

Just assume if you're writing a bash script, stick with /bin/bash and not /sh because problems can arise.

$SHELL does not necessarily reflect the currently running shell. Instead, $SHELL is the user's preferred shell, which is typically the one set in /etc/passwd. If you start a different shell after logging in, you can not necessarily expect $SHELL to match the current shell anymore.

This is mine for example, but it could also be /root:/bin/dash or /root:/bin/sh depending on which shell you have input in passwd. So to avoid any problems, keep the passwd file at /bin/bash and then using $SHELL vs. #!/bin/bash wouldn't matter as much.

root@kali:~/Desktop# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash

Sources: http://shebang.mintern.net/bourne-is-not-bash-or-read-echo-and-backslash/ https://unix.stackexchange.com/questions/43499/difference-between-echo-shell-and-which-bash http://man.cx/sh http://man.cx/bash

Brandon
  • 2,367
  • 26
  • 32
Chirality
  • 745
  • 8
  • 22
0

In addition to what the others said, the shebang also enables syntax highlighting in some text editors, for example vim.

Konrad Höffner
  • 11,100
  • 16
  • 60
  • 118