Skip to content
VLSI Mentor

Linux & Bash · Part I · Module 5 · Permissions, Ownership and Execution

Making a Script Executable: the Execute Bit and the Shebang

This chapter finishes something started a long way back. [Chapter 1.7](/linux-bash/builtins-vs-external-commands) explained that a word containing a slash is a path rather than a command name, and said that making the file actually run was a separate question for later. [Chapter 2.3](/linux-bash/working-directory-and-path-shorthands) showed you why the two extra characters in ./run.sh are necessary. [Chapter 4.7](/linux-bash/file-types-and-line-endings) produced a baffling error about an interpreter whose name ended in a character nobody can see. All three were describing different stages of the same sequence, and this chapter assembles it: three independent things must be true before a script runs, each one produces a different message when it is not, and reading which message you got is the whole of the diagnosis.

Foundation16 min readLinuxBashPart IFundamentalsPermissionsScripts

Part I · Module 5 · Chapter 5.5 · Making a Script Executable

1. A Script Is an Ordinary Text File

Nothing about a shell script is special to the filesystem. It is a text file containing commands — the same commands you have been typing since Module 1.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cat run.sh
#!/bin/bash
echo "it ran"

$ file run.sh
run.sh: Bourne-Again shell script, ASCII text executable

Chapter 4.7's file recognises it as a script, and that recognition comes entirely from reading the first line. There is no flag marking the file as a program and no special extension required — .sh is a convention for humans, exactly as Chapter 4.7 said of every extension.

What turns this text file into something you can run is three separate conditions, and they are genuinely independent.

Four stages. One: the shell must find the file, which needs a path containing a slash, otherwise command not found. Two: the execute permission must be set, otherwise permission denied. Three: the first line must name an interpreter that really exists, otherwise bad interpreter. Four: only then does the script run.Three separate things must be true before a script runs01THE SHELL MUST FIND ITa word with a slash is a path — else: command not found02EXECUTE MUST BE ALLOWEDthe x character must be there — else: permission denied03THE INTERPRETER MUST EXISTthe first line must name a real one — else: bad interpreter04NOW IT RUNSthree different checks, three different messages
Figure 1 — the three checks, in the order they happen, and the message each one produces when it fails. They are independent: satisfying one tells you nothing about the others, which is why a fix that addresses the wrong stage appears to do nothing at all.

2. Gate One: the Shell Must Find It

Chapter 2.3 covered this in full, so one paragraph suffices.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ run.sh
bash: run.sh: command not found

$ ./run.sh

A bare word is a command name, looked up in a list of directories that does not include the one you are in. A word containing a slash is a path, used directly. ./ is the cheapest way to put a slash in.

The prefix is bash:, so per Chapter 1.3 nothing ran at all — the shell never found a file to attempt.

3. Gate Two: the Execute Bit

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ls -l run.sh
-rw-r--r-- 1 engineer engineer 32 Mar 26 09:10 run.sh

$ ./run.sh
bash: ./run.sh: Permission denied

A different message, and the difference is the diagnosis. The shell found the file this time — ./run.sh is a path, and there is a file there. It was not allowed to run it.

Read the mode with Chapter 5.2: rw-r--r--, 644. No x anywhere. The owner may read and write it and nobody may execute it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ chmod u+x run.sh

$ ls -l run.sh
-rwxr--r-- 1 engineer engineer 32 Mar 26 09:10 run.sh

$ ./run.sh
it ran

Chapter 5.3's symbolic form, adding one bit and leaving the other eight alone.

Who should be allowed to run it?

Chapter 5.3's choice, applied:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ chmod u+x run.sh     # only you           -rwxr--r--
$ chmod a+x run.sh     # everybody          -rwxr-xr-x
$ chmod 700 run.sh     # only you, nothing else at all
$ chmod 755 run.sh     # the usual "shared tool" mode

The default worth adopting is u+x. It answers the question you actually have — let me run this — and says nothing about anybody else. Widen it deliberately when a colleague needs it, which on a shared machine also means checking the group is right, per Chapter 5.4.

A note from Chapter 5.3 that matters here: a bare chmod +x is affected by the umask, so it may or may not grant execute to the group and others depending on a setting you have not met yet. u+x and a+x are unambiguous. Chapter 5.6 explains why.

4. Gate Three: the Interpreter

The execute bit is set and the file still may not run.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cat bad.sh
#!/bin/nosuchshell
echo hi

$ chmod u+x bad.sh

$ ./bad.sh
bash: ./bad.sh: /bin/nosuchshell: bad interpreter: No such file or directory

A third message, and a third stage. The shell found the file and was allowed to run it — so gates one and two both passed. What failed is what happened next.

Chapter 1.7 introduced the #! line, usually called the shebang. When the system is asked to run a text file, it reads that first line to find out which program should interpret the contents, and then runs that program with your file as its input.

Azvya Education Pvt. Ltd.VLSI Mentor
What the first line is for
#!/bin/bash
││└────────── the program that will read this file
│└─────────── the two characters that mark the line as a shebang
└──────────── the line must be the FIRST line, with #! at position 1

So ./bad.sh asked the system to run /bin/nosuchshell, which does not exist. The message says precisely that, naming the interpreter rather than your script.

The tell: the error names a path you did not type. ./bad.sh is what you ran; /bin/nosuchshell came from inside the file. When an error about running a script mentions a path that is not the script's, look at the first line.

5. The Case That Surprises People: No Shebang at All

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cat noshebang.sh
echo "no shebang here"

$ chmod u+x noshebang.sh

$ ./noshebang.sh
no shebang here

It ran.

A missing shebang is not an error. When a file has no #! line, the shell that was asked to run it falls back to running the contents itself.

That sounds convenient and is the argument for always writing one, because what happens is unspecified rather than reliable:

  • it worked here because Bash was asked to run it and Bash handled it;
  • a different shell might do something different;
  • another program starting your script might not do the fallback at all;
  • and if the file is actually Python or some other language, the fallback runs it as shell commands, producing errors that have nothing to do with the real problem.

Write the shebang. It costs one line and converts "whatever the surrounding context happens to do" into "this file states what it needs".

6. Putting the Three Together

A complete session, from a file that does nothing to one that runs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cat > hello.sh
#!/bin/bash
echo "hello from the script"
^D

$ ls -l hello.sh
-rw-r--r-- 1 engineer engineer 45 Mar 26 10:00 hello.sh

$ hello.sh
bash: hello.sh: command not found

$ ./hello.sh
bash: ./hello.sh: Permission denied

$ chmod u+x hello.sh

$ ./hello.sh
hello from the script

Three attempts, three different messages, each one a different gate. Nothing about the file changed between attempts one and two — only how it was named.

That sequence is worth being able to reproduce from memory, because it is the diagnostic ladder: whichever message you get tells you which gate you are standing at.

MessageGateFix
command not found1 — not locateduse ./ or a real path
Permission denied2 — not allowedchmod u+x
bad interpreter3 — no such interpreterfix the first line
runs, but errors oddly—possibly no shebang, §5

7. Other Ways to Run It

Two alternatives, each with a specific use.

Hand the file to an interpreter directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ bash hello.sh
hello from the script

This needs no execute bit and no shebang — you are running bash, which is already executable, and handing it a file to read. Both gates two and three are bypassed because you have answered their questions explicitly.

Useful for a quick test, and useful when you cannot change the permissions on a file somebody else owns.

Run it in your current shell with source, which Chapter 1.7 introduced and Module 10 teaches properly. Also needs no execute bit, and is a genuinely different operation rather than a convenience — it does not start a child process.

Which to use:

SituationUse
your own script, run normallychmod u+x, then ./script.sh
a quick test, or a file you cannot changebash script.sh
it must change your current shellsource script.sh — Module 10

8. The Failures

Setting the bit on something that is not a program. chmod +x notes.txt succeeds — chmod does not inspect the contents. Running it then fails in whatever way a file of prose fails when a shell tries to interpret it. The execute bit is permission, not capability.

Fixing the wrong gate. Running chmod +x at a command not found does nothing, because the shell was never looking at your file. Read the message first.

Editing the shebang on a system where the path differs. #!/bin/bash assumes Bash is at that path, which is overwhelmingly true on Linux and not universal. A more portable form exists and belongs to Module 21, where portability is the subject.

A shebang that is not on line one. A blank line or a comment above it means it is not a shebang at all — the #! must be the first two characters of the file. The symptom is §5's silent fallback.

Losing the execute bit in a copy. Chapter 3.2's cp may not preserve the mode depending on how it is invoked, and Chapter 3.6 noted that an archive stores permissions. A script that ran before a copy and not after is usually this; ls -l confirms it in one command.

9. Why This Matters in Engineering Work

Scripts arriving from elsewhere routinely fail at gate two or three. A colleague sends one, you retrieve one from a shared location, or you extract one from an archive, and it will not run. Two commands settle it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ls -l script.sh      # is the x there?         — gate two
$ head -n 1 script.sh  # what does it ask for?   — gate three

The gate-three failures are the confusing ones, because the message names something you did not type. Both the missing-interpreter and the invisible-carriage-return versions look like the system is broken, and both are one line of a text file.

On shared machines, who may run it is a real decision. A tool the whole team uses needs the group settings from Chapter 5.4 as well as the execute bit — a correctly executable script in a directory the group cannot traverse is still unusable, per Chapter 5.2.

Part II works with team-shared flow scripts throughout, and all three gates are assumed.

10. Practice

Question 1. Name the three conditions that must hold before a script runs, and the error message each produces when it does not.

Question 2. run.sh gives command not found but ls clearly shows the file. Which gate is this, and what is the fix?

Question 3. ./run.sh gives Permission denied. Which gate is this, and how does the message prove the previous gate passed?

Question 4. ./run.sh reports bad interpreter: /bin/bsah. What has gone wrong, and how can you tell the problem is inside the file rather than in what you typed?

Question 5. A script has the execute bit and no #! line at all. Does it run? Explain what happens and why you should still write the shebang.

Question 6. Give a command that runs hello.sh without needing either the execute bit or a shebang, and say why neither is required.

Question 7. chmod +x notes.txt succeeds on a file of plain prose. Why does chmod allow it, and what happens if you then try to run it?

Question 8. A script ran yesterday and today reports Permission denied after being copied from a colleague. Give the command you would run first and what you would look for.

Question 9. Why is u+x a better default than 777 for a script you have just written?

11. Remember

  • A script is an ordinary text file. Nothing marks it as a program except the two things this chapter is about.
  • Three independent gates: the shell must find it, you must be allowed to run it, and the first line must name a real interpreter.
  • Each failure has its own message — command not found, Permission denied, bad interpreter — and the message tells you which gate you are at.
  • command not found means nothing was located. It is a path problem, fixed with ./, and chmod will not help.
  • Permission denied means it was located and not allowed. chmod u+x is the fix; u+x is the right default because it answers only the question you asked.
  • The shebang names the interpreter, must be the first two characters of the file, and produces an error naming a path you did not type when it is wrong.
  • An error naming a path you did not type is gate three. Chapter 4.7's invisible carriage return is the same failure with a different cause.
  • A missing shebang is not an error — the shell falls back to running the file itself. That is unspecified rather than reliable, which is the argument for always writing one.
  • bash script.sh bypasses gates two and three, because you have answered both questions explicitly.
  • A copy can lose the execute bit. ls -l is the one-command check.

One chapter of Module 5 remains, and it answers a question raised twice already: why does a new file arrive as 644 rather than anything else, and what has to change so that a team can genuinely write to each other's files?

Where this fits

Part of the Linux & Bash curriculum.