Terminals, Shells, Processes, and the Bash Execution Model
A reliable shell script starts with a correct model of what the shell is actually doing. This lesson follows commands from your keyboard through the terminal and Bash process, then into builtins, external programs, child processes, and exit status.
Learning objectives
By the end of this lesson
- Separate terminal, TTY, shell, process, builtin, function, and external-command concepts.
- Explain how Bash decides whether a command name refers to an alias, keyword, function, builtin, hashed command, or executable.
- Inspect parent/child process relationships and understand why environment changes flow to children but not back to parents.
- Recognize when pipelines, parentheses, and command substitution create subshell contexts.
- Use type, command -V, ps, and $$ safely to inspect execution behavior.
1. From keyboard to process
A terminal and a shell are separate processes. The terminal emulator handles a text-oriented user interface. Bash reads bytes from an input source, parses shell language, and either handles the request internally or launches another program.
sequenceDiagram
participant U as User or CI step
participant T as Terminal/runner
participant B as Bash process
participant C as Command
U->>T: command text
T->>B: bytes on stdin
B->>B: parse + expand + redirect
alt Bash builtin/function
B->>B: execute in shell process
else external command
B->>C: create child and exec program
C-->>B: output + exit status
end
B-->>T: stdout/stderr + prompt or completionIf a command must change the shell’s own state—such as cd changing the current directory—it generally needs to run in the shell process rather than in a disposable child process.
2. How Bash resolves a command name
Before Bash runs name arg1 arg2, it must decide what name means. The exact details depend on syntax and shell state, but type gives a practical view.
type if # shell keyword
type cd # builtin
type printf # usually builtin
type command # builtin
type grep # external command on typical systems
type -a printf
command -V ls
command -v bash
Aliases are primarily interactive conveniences. Functions live inside shell state. Builtins are implemented by Bash itself. External commands are found through PATH lookup unless you specify a path directly.
ifPart of shell grammardeploy()Shell-defined reusable logiccdRuns inside Bash/usr/bin/grepSeparate executable process3. Parent processes, child processes, and environment
Processes form a hierarchy. A child process receives an environment derived from its parent, but a child cannot normally rewrite the parent shell’s variables or working directory.
printf 'shell pid=%s\n' "$$"
ps -p "$$" -o pid=,ppid=,comm=,args=
export DEMO_PARENT="visible-to-children"
bash -c 'printf "child sees: %s\n" "$DEMO_PARENT"; printf "child pid=%s\n" "$$"'
DEMO_LOCAL="parent-only-until-exported"
bash -c 'printf "unexported child value: <%s>\n" "$DEMO_LOCAL"'
pwd
bash -c 'cd /; pwd'
pwd # parent directory did not change
Running a script as ./script.sh starts another process. Sourcing it with source script.sh or . script.sh executes its commands in the current shell, allowing it to change current-shell variables and directory state.
4. Subshell contexts are observable
Parentheses, command substitution, and pipeline behavior can create subshell contexts. Code may look linear while state changes occur in a different process environment.
name=parent
( name=subshell; printf 'inside parentheses: %s\n' "$name" )
printf 'after parentheses: %s\n' "$name"
result=$(printf 'captured output')
printf 'command substitution: %s\n' "$result"
count=0
printf '%s\n' a b c | while read -r _; do
((count+=1))
done
printf 'count after pipeline: %s\n' "$count"
# Bash can alter some pipeline behavior with shell options,
# but portable scripts should not casually rely on such differences.
The pipeline example is a classic reason to understand process boundaries before using a variable after a pipeline loop.
5. Builtins and external commands have different costs and capabilities
Builtins avoid launching a separate executable and, more importantly, can modify shell state. This does not mean “always prefer builtins.” Readability and correctness matter more than micro-optimizing ordinary scripts.
cd
Must change the shell’s own current directory, so a child-process implementation would be ineffective.
read
Reads input into shell variables and is commonly used in loops and prompts.
exec
Can replace the shell process with another command—important for container entrypoints.
grep
Specialized text searching is delegated to a dedicated executable.
6. Hands-on lab: trace command execution
Build a report that distinguishes shell state, command resolution, and child processes.
mkdir -p "$HOME/devops-academy/bash/chapter01/lesson02"
cd "$HOME/devops-academy/bash/chapter01/lesson02"
{
printf '=== shell ===\n'
printf 'pid=%s\n' "$$"
ps -p "$$" -o pid=,ppid=,comm=,args=
printf '\n=== resolution ===\n'
type cd
type printf
type grep
printf '\n=== child ===\n'
export LAB_VALUE=from_parent
bash -c 'printf "child_pid=%s value=%s\n" "$$" "$LAB_VALUE"'
printf '\n=== source vs execute ===\n'
printf 'parent_pwd=%s\n' "$PWD"
} > execution-model.txt
cat execution-model.txt
Verification checklist
7. Knowledge check
Question 1. Why is cd normally a shell builtin?
Question 2. What is the purpose of type command-name?
Question 3. Why can exported variables flow to children but not automatically back to the parent?
8. Summary
Bash is a process with its own variables, working directory, options, and file descriptors. It resolves shell syntax and builtins internally and launches external programs as child processes. Understanding those boundaries explains many otherwise surprising behaviors involving cd, environment variables, pipelines, subshells, and sourced files.
9. Further reading
- GNU Bash Reference Manual — Command Execution Environment and Shell Builtin Commands.
- GNU Bash manual — Command Search and Execution.
- POSIX process and environment model.
- Linux
execve(2),fork(2), andwait(2)documentation for process-level background. - BashFAQ entries on subshell behavior and pipelines.
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.