Skip to content
DevOps devops shell 6 min read

Shebang & Making Scripts Executable

When you write a shell script, you save it as a plain text file full of commands. But a text file does not know how to run itself. Linux needs to be told which program should read and execute those commands. That is the job of the shebang — the special first line at the top of almost every script. Getting it right (and understanding how scripts are launched) is the difference between a script that “just works” and one that fails with a confusing error.

What the shebang is

The shebang (also written as hashbang) is the very first line of a script. It always starts with the two characters #! followed by the full path to an interpreter (the program that reads and runs your script line by line). For a Bash script it usually looks like this:

#!/bin/bash

The name comes from the two symbols: # is called hash (or sharp), and ! is called bang. Put them together and you get “shebang”.

When you run a script as a program, the Linux kernel (the core of the operating system) looks at the first two bytes of the file. If it sees #!, it reads the rest of that line, finds the interpreter listed there, and hands your script to it. So #!/bin/bash literally means: “run the rest of this file using the program located at /bin/bash”.

Important: The shebang must be the first line of the file, with no blank lines, spaces, or comments above it. If even a single empty line comes first, the kernel will not recognise it as a shebang and will fall back to running the file with /bin/sh, which can behave differently.

Why the shebang matters

Without a shebang, the system has to guess which interpreter to use, and that guess depends on how you launch the script. With a shebang, your script is self-describing: anyone can run it directly and the correct interpreter is always used, regardless of which shell they happen to be using.

This matters because Ubuntu does not use Bash for everything. On Ubuntu (22.04 and 24.04 LTS), /bin/sh is actually a link to dash (Debian Almquist Shell — a smaller, faster shell). Dash does not support many Bash features like arrays or [[ ... ]] tests. If you write a Bash script but it accidentally runs under dash, you will get errors like Syntax error: "[[" unexpected. A correct shebang prevents this.

The two ways to run a script

There are two common ways to execute a script, and they behave differently.

1. Pass the script to an interpreter

You can name the interpreter explicitly and give it the script as an argument:

bash hello.sh

Here, bash is launched first, and it opens hello.sh and runs the commands. In this case the shebang line is ignored — you already told the system to use Bash. The file does not need to be executable for this to work, because you are running bash (which is executable) and merely feeding it a text file.

2. Run the script directly

The second way treats the script itself like a program:

./hello.sh

For this to work, two things must be true:

  1. The file must have the execute permission set (more on this below).
  2. The shebang line must point to a valid interpreter, because now the shebang is what decides which program runs the file.

The leading ./ is required and often confuses beginners. It means “the file named hello.sh in the current directory”. You need it because, for security, Linux does not look in your current folder for commands by default.

Way to runCommandNeeds execute permission?Uses shebang?
Pass to interpreterbash hello.shNoNo (ignored)
Run directly./hello.shYesYes

When to use which: Use bash hello.sh for quick one-off testing or when you do not want to change file permissions. Use ./hello.sh (after making the file executable) for scripts you run regularly, install, or share — it is cleaner and lets the shebang guarantee the right interpreter.

Making a script executable

To run a script directly with ./, you grant it the execute permission using chmod (short for change mode — the command that sets file permissions). Create a quick script first:

nano hello.sh

Put this inside:

#!/bin/bash
echo "Hello from a real script!"

Save and exit (in nano: Ctrl+O, Enter, then Ctrl+X). Now make it executable and run it:

chmod +x hello.sh
./hello.sh

Output:

Hello from a real script!

The +x means “add the execute permission”. You can confirm it with ls -l:

ls -l hello.sh

Output:

-rwxr-xr-x 1 ubuntu ubuntu 47 Jun 15 10:22 hello.sh

The x characters in -rwxr-xr-x show the file is now executable.

PATH placement — running from anywhere

So far you must type ./hello.sh from inside the script’s folder. The reason is the PATH — an environment variable holding a list of folders the shell searches when you type a command. Your current directory is deliberately not on it. To run a script by name from anywhere (like a real command), move it into a folder that is on the PATH:

sudo cp hello.sh /usr/local/bin/hello
hello

Output:

Hello from a real script!

Now hello works from any directory, no ./ and no .sh needed. /usr/local/bin is the standard Ubuntu location for scripts and programs you install yourself, and it is on the PATH by default. You can see your PATH with echo "$PATH".

#!/usr/bin/env bash and when to prefer it

You will often see this alternative shebang:

#!/usr/bin/env bash

Instead of hard-coding the path /bin/bash, this uses env (a small utility) to search the PATH and find whichever bash it sees first. The benefit is portability — the script runs correctly even on systems where Bash lives somewhere unusual (for example macOS with Homebrew installs a newer Bash in /opt/homebrew/bin, and many BSD systems keep Bash in /usr/local/bin).

ShebangBehaviourBest for
#!/bin/bashAlways uses Bash at the fixed path /bin/bashScripts that only ever run on Ubuntu/Debian servers
#!/usr/bin/env bashUses the first bash found on the PATHCross-platform scripts, or when a newer Bash is installed elsewhere

Gotcha: #!/usr/bin/env bash does not let you pass options to Bash on the same line in a portable way (for example #!/usr/bin/env bash -e may fail), because env treats the whole rest as one argument. If you need strict mode, set it inside the script with set -euo pipefail instead.

When to use which: On a controlled Ubuntu server you administer, #!/bin/bash is perfectly fine and explicit. For scripts you publish, share with teammates on different operating systems, or keep in a Git repository, prefer #!/usr/bin/env bash.

Best Practices

  • Always put a shebang on line 1 with nothing above it — no blank lines, no comments.
  • Use #!/usr/bin/env bash for portable or shared scripts; #!/bin/bash for Ubuntu-only server scripts.
  • Never rely on /bin/sh if you use Bash features — on Ubuntu it is dash, not Bash.
  • Run chmod +x script.sh once, then launch with ./script.sh for everyday use.
  • Install reusable scripts into /usr/local/bin so they run from anywhere by name.
  • Add set -euo pipefail near the top of serious scripts to fail fast on errors and undefined variables.
  • Test with bash script.sh while developing, since it does not require the execute bit.
Last updated June 15, 2026
Was this helpful?