Chapter 02Lesson 02~45 minutes

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.

BeginnerBash scriptingHands-on lab

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

ConceptMeaningOperational note
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" 
Execution model
flowchart TD
  C["./deploy.sh"] --> K["Kernel execute request"]
  K --> H["Read #! interpreter line"]
  H --> I["Launch interpreter"]
  I --> S["Interpreter reads deploy.sh"]
Key point

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.

Production decision

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.

Trust boundary

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?

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 — env behavior.
  • Git documentation for gitattributes — line-ending normalization.
Next lesson

Variables, Environment Variables, readonly, and export

Continue Chapter 2 by building on this execution and data model.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.