Bash — The First Line of a Script: #!/bin/bash and the Shebang

What exactly happens when you put `#!/bin/bash` at the top of a script? Learn what a shebang really is, how the OS uses it, why `sh` isn't always Bash, and the difference between running, sourcing, and explicitly invoking a script.

Bash — The First Line of a Script: #!/bin/bash and the Shebang

Almost every Bash script starts with something like:

#!/bin/bash

or, these days, quite often:

#!/usr/bin/env bash

This special line is called a shebang. It tells the operating system which interpreter should be used to execute the script.

And despite looking very much like Bash code, #!/bin/bash is not a Bash command.

What is a shebang?

The name comes from the two characters at the beginning:

#!

They are followed by the path to the interpreter.

For example:

#!/bin/bash

means:

"Run this script using Bash."

The same mechanism can be used with other interpreters:

#!/usr/bin/env python3
#!/usr/bin/perl
#!/usr/bin/ruby

So the shebang is not specific to Bash. It is a way for an executable text file to tell the operating system which program should interpret it.

What happens when you run a script?

Suppose we have this file:

#!/bin/bash

echo "Hello, world!"

and make it executable:

chmod +x hello.sh

When we run:

./hello.sh

the operating system examines the beginning of the file, sees:

#! /bin/bash

and starts /bin/bash to interpret the script.

Conceptually, the result is similar to running:

/bin/bash ./hello.sh

The important difference is that with:

./hello.sh

the interpreter is determined by the shebang.

Why must the shebang be the first line?

Because the operating system only recognizes the interpreter declaration at the very beginning of the file.

This works:

#!/bin/bash

echo "Hello"

This does not:

# My Bash script

#!/bin/bash

echo "Hello"

In the second case, the operating system does not see #! as the first two characters of the file, so it cannot use the shebang to determine the interpreter.

If you want a comment at the beginning of your script, put it after the shebang:

#!/bin/bash

# My Bash script

echo "Hello"

./script.sh and bash script.sh are not the same thing

There is an important distinction between these two commands:

./script.sh

and:

bash script.sh

When you run:

./script.sh

the operating system uses the script's shebang to determine which interpreter to start.

When you run:

bash script.sh

you have already explicitly selected Bash. The shebang is no longer involved in choosing the interpreter.

This also means that:

sh script.sh

does not mean "run the script according to its shebang."

You explicitly told the system to run it with sh.

That distinction can become very important.

sh is not necessarily Bash

One of the most common shell-related mistakes is assuming that sh and bash are interchangeable.

They are not.

sh refers to the traditional/POSIX shell interface. On many modern Linux distributions, /bin/sh is actually a link to another shell implementation.

For example, on Debian and Ubuntu it commonly points to dash.

You can check this with:

ls -l /bin/sh

A Bash script might contain Bash-specific features such as:

if [[ "$name" == "Pavel" ]]; then
    echo "Hello"
fi

or arrays:

servers=("web01" "web02" "web03")

These are Bash features and should not be assumed to work when the script is executed with sh.

Therefore, if your script is a Bash script, say so:

#!/usr/bin/env bash

and run it as a Bash script.

If you deliberately want a POSIX shell script, use:

#!/bin/sh

and write the script using POSIX shell syntax.

/bin/bash vs /usr/bin/env bash

You will commonly see two different forms of Bash shebang:

#!/bin/bash

and:

#!/usr/bin/env bash

They are similar, but not identical.

#!/bin/bash

This specifies the interpreter using an absolute path:

#!/bin/bash

The system will use exactly /bin/bash.

You can check whether Bash exists there with:

ls -l /bin/bash

#!/usr/bin/env bash

Here, the operating system starts env:

/usr/bin/env

and passes it the name:

bash

env then searches for bash using the current PATH.

You can see which Bash would be found with:

command -v bash

This approach can be useful on systems where Bash may be installed somewhere other than /bin/bash.

However, env is not magic. /usr/bin/env itself must exist, and bash must be available through PATH.

In a minimal container or restricted environment, either assumption can fail.

Why does the executable permission matter?

A shebang becomes relevant when you execute the script directly.

For example:

./hello.sh

requires the file to have the executable bit set.

You can check its permissions with:

ls -l hello.sh

and make it executable with:

chmod +x hello.sh

After that:

./hello.sh

can use the shebang.

But this:

bash hello.sh

does not require the script itself to be executable because you are executing bash, and Bash is reading the file as input.

This distinction is especially useful when troubleshooting scripts that work with:

bash script.sh

but fail with:

./script.sh

The shebang is not Bash syntax

If you open a Bash script and Bash reads it directly, the line:

#!/bin/bash

looks like a comment.

And from Bash's point of view, it essentially is one.

The special meaning comes from the operating system when the file is executed directly.

This is why:

bash script.sh

still works even though Bash does not need the shebang to know which interpreter it is already running under.

Don't put -e in the shebang

You may occasionally encounter:

#!/bin/bash -e

This attempts to start Bash with the -e option.

For normal scripts, it is much clearer and more portable to put shell options inside the script:

#!/usr/bin/env bash

set -e

Or, for a script where you deliberately want the commonly used strict-mode combination:

#!/usr/bin/env bash

set -euo pipefail

This also makes the script's behavior much easier to see and maintain.

A good modern Bash script

A simple modern Bash script might therefore look like this:

#!/usr/bin/env bash

set -euo pipefail

CONFIG_FILE="/etc/myapp/config.conf"

if [[ ! -f $CONFIG_FILE ]]; then
    echo "Configuration file not found: $CONFIG_FILE" >&2
    exit 1
fi

echo "Configuration found"

The first line answers:

Which interpreter should run this file?

The set line answers a different question:

How should that Bash interpreter behave?

Keeping these two things separate makes the script easier to understand.

The shebang can point to almost any interpreter

There is nothing special about Bash in this mechanism.

For example, a Python script can start with:

#!/usr/bin/env python3

A Perl script can use:

#!/usr/bin/env perl

And a Ruby script can use:

#!/usr/bin/env ruby

The important thing is that the selected program understands the rest of the file.

The operating system doesn't care whether the interpreter is Bash, Python, Perl, Ruby, or something else. It simply follows the interpreter specified by the shebang.

What happens with source?

There is another important case:

source script.sh

or:

. script.sh

Here the script is not started as a separate executable process.

Its commands are read and executed by the current shell.

Therefore, the shebang does not select a new interpreter in this situation.

This is particularly important for files intended to define environment variables, functions, aliases, or shell configuration.

If a file is intended to be sourced by Bash, it should still contain appropriate Bash syntax, but the shebang does not control what happens when the file is sourced.

A surprisingly common problem: Windows line endings

Sometimes a perfectly valid Bash script refuses to run and produces an error similar to:

/usr/bin/env: 'bash\r': No such file or directory

The strange \r is the clue.

The file probably uses Windows-style CRLF line endings instead of Unix/Linux LF line endings.

You can inspect the file with:

file script.sh

or:

cat -A script.sh

If necessary, convert the file to Unix line endings.

This problem is particularly common when scripts move between Windows and Linux, or when they are edited using tools configured for Windows line endings.

Another possible problem: UTF-8 BOM

Some editors can add a UTF-8 BOM (Byte Order Mark) at the beginning of a file.

That means the file may not literally begin with:

#!

even though it looks correct in an editor.

You can inspect the first few bytes with:

xxd -l 16 script.sh

A clean shebang should begin with the bytes corresponding to:

23 21

which represent:

#!

A BOM before those bytes can prevent the system from recognizing the shebang correctly.

Which shebang should you use?

For a Bash script, two common choices are:

#!/bin/bash

or:

#!/usr/bin/env bash

For a POSIX shell script:

#!/bin/sh

For Python:

#!/usr/bin/env python3

The important thing is to choose the interpreter that actually matches the language and features used by the script.

Don't write:

#!/bin/sh

just because sh is shorter if your script is full of Bash-specific features.

The operating system will happily start sh for you. It just won't necessarily be happy about what happens next.

Useful commands when troubleshooting a shebang

When a script behaves strangely, these commands can quickly reveal what's going on:

head -n 1 script.sh

Show the first line.

command -v bash

Show where Bash is found in PATH.

file script.sh

Check the file type and often its line-ending format.

xxd -l 16 script.sh

Inspect the first bytes, useful for detecting a BOM.

ls -l script.sh

Check the executable permission.

chmod +x script.sh

Make the script executable.

Then try:

./script.sh

Quick reference

Script typeTypical shebang
Bash#!/usr/bin/env bash
Bash with fixed path#!/bin/bash
POSIX shell#!/bin/sh
Python 3#!/usr/bin/env python3
Perl#!/usr/bin/env perl
Ruby#!/usr/bin/env ruby

The important part

The line:

#!/bin/bash

is not a Bash command.

It is a shebang, and its job is to tell the operating system which interpreter should be used when the file is executed directly.

Keep these three cases in mind:

./script.sh

Use the interpreter specified by the shebang.

bash script.sh

Explicitly use Bash; the shebang is not used to choose the interpreter.

source script.sh

Execute the file inside the current shell; the shebang does not start another interpreter.

Once you understand this distinction, the mysterious #!/bin/bash at the top of every script becomes considerably less mysterious.

And, as with many things in Unix, it turns out that the line that looks like a command isn't actually a command at all.

Read next

Bash !: Negating Command Results in Bash

Master the Bash ! operator to easily reverse exit statuses and trigger actions when a command fails. This quick guide breaks down how the logical NOT operator transforms success into failure (and vice versa) with practical scripting examples.

Bash: Getting Started with the Basic Concepts

Bash may look intimidating, but it is simply a program waiting for commands. Learn how Bash works, finds commands, handles files and patterns, and combines commands using pipes, redirection, variables, conditions, and more.