๐ Advanced Shell Debugging
๐ง Overview
Debugging shell scripts is fundamentally different from debugging traditional programming languages. The shell is a runtime orchestrator with:
- implicit forks
- subshells
- complex expansion rules
- hidden state
- asynchronous pipelines
- signal propagation
- file descriptor manipulation
This module teaches how to debug shell behavior at the level of processes, signals, FDs, and execution flow โ not just syntax.
๐ Who this is for
- DevOps/SRE diagnosing failures in CI/CD, containers, or automation.
- Engineers writing complex orchestration scripts or entrypoints.
- Anyone dealing with unexpected behavior in pipelines, subshells, or expansions.
- People who want to understand why the shell behaves the way it does.
๐งฉ Internals / Mechanics
๐งฉ What debugging actually means in the shell
Debugging requires observing:
- Parsing โ how the shell interprets the script.
- Expansion โ how variables, globs, and substitutions transform input.
- Execution โ which commands fork, which run as builtins.
- Process control โ how jobs, groups, and signals behave.
- File descriptors โ how redirections and pipes are wired.
๐งฉ What set -x really shows
set -x prints:
- expanded commands
- redirections
- pipeline stages
- evaluated arguments
It does not show:
- forks
- exec calls
- file descriptor operations
- signal delivery
- subshell boundaries
For that, you need systemโlevel tools.
๐ง Techniques
๐ง Enhanced tracing with PS4
1 2 | |
This adds:
- file name
- line number
- function name
to every traced command.
๐ง Use strace to observe syscalls
1 | |
Reveals:
- fork/exec
- open/close
- dup2
- pipe creation
- read/write
- signal handling
๐ง Inspect open file descriptors
1 | |
Useful for debugging:
- leaked FDs
- pipe wiring
- unexpected redirections
๐ง Inspect process trees
1 2 | |
Shows:
- subshells
- pipeline processes
- orphaned children
๐ง Debug pipelines
1 | |
๐ง Debug subshells
Add:
1 | |
to see where forks occur.
โ ๏ธ Pitfalls
โ ๏ธ set -x does not show command substitution failures
1 | |
The failure is hidden unless explicitly checked.
โ ๏ธ set -e does not propagate into subshells
1 2 | |
โ ๏ธ Pipelines hide errors without pipefail
โ ๏ธ Pipe buffer deadlocks (~64 KB)
Producer blocks when consumer is slow.
โ ๏ธ Traps do not fire in subshells
1 | |
Runs only inside the subshell.
๐จ RealโWorld Failures
๐จ Failure: CI pipeline hangs with no logs
1 | |
If consumer is slow โ pipe fills โ producer blocks โ pipeline freezes.
Debug:
1 | |
๐จ Failure: Command substitution hides critical error
1 | |
If jq fails โ $json becomes empty โ deployment breaks silently.
๐จ Failure: Zombie processes in containers
Shell as PID 1 does not reap children.
Debug:
1 | |
๐ ๏ธ Patterns
๐ ๏ธ Pattern: Debug mode flag
1 2 | |
๐ ๏ธ Pattern: Structured logging
1 | |
๐ ๏ธ Pattern: PID tracing
1 | |
๐ ๏ธ Pattern: FD tracing
1 | |
โ AntiโPatterns
โ Debugging with echo everywhere
Breaks JSON, corrupts pipelines, pollutes logs.
โ Using echo instead of printf
โ Ignoring $?
โ Debugging without checking process trees
๐ Debugging Tools Summary
| Tool | Purpose |
|---|---|
set -x |
trace expansions & commands |
PS4 |
enhanced tracing metadata |
strace |
syscalls, forks, FD, signals |
lsof |
open file descriptors |
ps, pstree |
process trees |
pgrep, pkill |
process discovery |
trap |
signal debugging |
printf '%q' |
inspect quoting |
declare -p |
inspect variables |
โ๏ธ Performance Considerations
Debugging slows execution:
set -xโ 2โ10ร slowerstraceโ 10โ100ร slower- heavy logging โ I/O bottlenecks
Use selectively.
๐งต Process Control
Debugging signals
1 | |
Debugging job control
1 2 | |
๐ณ Containers
๐ณ Debugging entrypoints
Add:
1 2 | |
๐ณ Debugging PID 1 signal handling
1 | |
๐ฐ๏ธ CI/CD
๐ฐ๏ธ Debugging failing stages
- enable
set -xonly for failing steps - log FD and PID information
- always use
pipefail
๐ฐ๏ธ Debugging race conditions
Insert:
1 | |
to expose ordering issues.
๐ง Summary
Advanced shell debugging requires:
- tracing expansions
- tracing forks and execs
- inspecting file descriptors
- analyzing process trees
- understanding pipelines and subshells
- observing signal flow
These techniques reveal why the shell behaves the way it does โ and allow you to build predictable, productionโgrade automation.