Shebangs, Execution Permissions, and Script Launch Methods
Several commands can appear to “run the same script,” but they create different process, environment, and interpreter behavior. DevOps engineers need to know those differences because deployment hooks, CI runners, containers, and service managers launch scripts in specific ways.
Learning objectives
By the end of this lesson
- Explain the purpose and limitations of a shebang line.
- Distinguish direct execution from invoking a script through bash.
- Use chmod safely to grant executable permission.
- Compare normal execution, source, and exec in terms of process behavior.
- Choose a portable shebang strategy appropriate to the deployment environment.
1. Four launch methods that are not equivalent
bash script.shStart Bash and tell it to read the fileShebang is not used to choose the interpreter
./script.shAsk the OS to execute the fileNeeds execute permission; shebang selects interpreter
source script.shRead the file in the current Bash processCan modify current variables, directory, options, and
functions
exec ./script.shReplace the current process with the programNo return to the original shell process
The distinction becomes critical in automation. A file that works
with bash script.sh may fail when a CI tool tries to
execute it directly if the execute bit is missing or the shebang
points to a nonexistent interpreter.
2. What the shebang actually does
A shebang is the first line of an executable text script and begins
with #!. On Unix-like systems, the kernel recognizes
this marker when the file is executed directly and uses the rest of
the line to identify an interpreter.
#!/usr/bin/env bash
printf 'hello from Bash %s\n' "$BASH_VERSION"
flowchart TD C["./deploy.sh"] --> K["Kernel execute request"] K --> H["Read #! interpreter line"] H --> I["Launch interpreter"] I --> S["Interpreter reads deploy.sh"]
The shebang is not a Bash comment in this context. Bash treats it as a comment after startup, but the operating system uses it before Bash starts when the file is executed directly.
3. /usr/bin/env bash versus an absolute Bash path
Two common shebangs are:
#!/bin/bash
# or
#!/usr/bin/env bash
#!/bin/bash requests one exact interpreter path. This
is predictable on systems where that path is guaranteed.
#!/usr/bin/env bash asks env to locate
bash using PATH, which can be useful on
systems where Bash lives elsewhere.
The tradeoff is control. Searching PATH may choose an
unexpected Bash in a managed environment. Conversely, hard-coding
/bin/bash may fail on platforms that intentionally
install Bash elsewhere.
Choose the shebang based on the deployment contract. For a
controlled Linux image, an absolute path can be appropriate. For
developer tooling across heterogeneous Unix-like environments,
/usr/bin/env bash is often practical.
4. Executable permission is a filesystem decision
On Unix-like filesystems, direct execution requires an execute permission bit. Inspect and change it deliberately:
printf '#!/usr/bin/env bash\nprintf "ok\\n"\n' > demo.sh
ls -l demo.sh
bash demo.sh
chmod u+x demo.sh
ls -l demo.sh
./demo.sh
chmod u+x grants execute permission to the file owner
without broadly changing group or other permissions. Avoid
reflexively using chmod 777; it grants read, write, and
execute permission to everyone and is almost never the correct fix.
5. source runs code inside your current shell
source file and its POSIX shorthand
. file do not create the same isolation boundary as
ordinary script execution. The sourced file runs in the current
shell context.
cat > settings.sh <<'EOF'
project_name=academy
cd /tmp
academy_hello() {
printf 'hello %s\n' "$project_name"
}
EOF
printf 'before: %s\n' "$PWD"
source settings.sh
printf 'after: %s\n' "$PWD"
academy_hello
This behavior is useful for shell libraries, virtual-environment
activation scripts, and configuration that intentionally changes the
current shell. It is dangerous when used casually: a sourced file
can change PATH, options, traps, aliases, functions,
and the current directory.
Sourcing a file is equivalent to executing its shell commands with the privileges and state of your current shell. Review untrusted files before sourcing them.
6. exec replaces the process
The shell builtin exec can replace the current shell
process with another program. There is no child Bash that later
returns to the caller.
bash -c '
printf "before pid=%s\n" "$$"
exec bash -c '\''printf "replacement pid=%s\n" "$$"'\''
printf "this line is never reached\n"
'
This is common in container entrypoints and process supervisors because replacing the shell lets the intended application become the main process and receive signals directly.
7. Line endings and encoding can break otherwise valid scripts
A script copied from Windows may contain CRLF line endings. Under
direct Unix execution, the carriage-return character can
accidentally become part of the interpreter path, producing errors
such as
/usr/bin/env: 'bash\r': No such file or directory.
file deploy.sh
# Display hidden carriage returns if they exist.
sed -n '1,5l' deploy.sh
# One safe conversion approach when dos2unix is available:
dos2unix deploy.sh
For repositories, use an appropriate
.gitattributes policy so shell scripts are checked out
with LF line endings where required.
8. Hands-on lab: compare launch semantics
Create one script and run it four ways while observing process IDs and variable scope.
mkdir -p "$HOME/devops-academy/bash/chapter02/lesson02"
cd "$HOME/devops-academy/bash/chapter02/lesson02"
cat > launch-demo.sh <<'EOF'
#!/usr/bin/env bash
demo_value="set-by-script"
printf 'script pid=%s parent=%s pwd=%s\n' "$$" "$PPID" "$PWD"
EOF
printf 'caller pid=%s\n' "$$"
bash launch-demo.sh
printf 'after bash: demo_value=%s\n' "${demo_value-unset}"
chmod u+x launch-demo.sh
./launch-demo.sh
printf 'after direct: demo_value=%s\n' "${demo_value-unset}"
source launch-demo.sh
printf 'after source: demo_value=%s\n' "${demo_value-unset}
Verification checklist
9. Knowledge check
Question 1. Which launch method ignores the
shebang for interpreter selection: bash script.sh or
./script.sh?
bash script.sh. You explicitly selected Bash. Direct
execution asks the operating system to use the shebang.
Question 2. Why can source be more
dangerous than ordinary execution?
Question 3. What is the core effect of
exec?
10. Summary
Script launch syntax determines interpreter selection, process
boundaries, and environment effects. Direct execution uses the
shebang and execute permission; explicit
bash invocation selects Bash directly;
source runs inside the current shell; and
exec replaces the current process. These are
operational semantics, not cosmetic syntax choices.
11. Further reading
- GNU Bash Reference Manual — Invoking Bash and Bourne Shell Builtins.
-
Linux
execve(2)manual page — program execution semantics. - GNU Coreutils manual —
envbehavior. -
Git documentation for
gitattributes— line-ending normalization.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.