Przejdลบ do treล›ci

๐Ÿ” 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:

  1. Parsing โ€” how the shell interprets the script.
  2. Expansion โ€” how variables, globs, and substitutions transform input.
  3. Execution โ€” which commands fork, which run as builtins.
  4. Process control โ€” how jobs, groups, and signals behave.
  5. 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
export PS4='+ ${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
set -x

This adds:

  • file name
  • line number
  • function name

to every traced command.

๐Ÿ”ง Use strace to observe syscalls

1
strace -f -e trace=process,desc sh script.sh

Reveals:

  • fork/exec
  • open/close
  • dup2
  • pipe creation
  • read/write
  • signal handling

๐Ÿ”ง Inspect open file descriptors

1
lsof -p "$pid"

Useful for debugging:

  • leaked FDs
  • pipe wiring
  • unexpected redirections

๐Ÿ”ง Inspect process trees

1
2
ps f
pstree -p

Shows:

  • subshells
  • pipeline processes
  • orphaned children

๐Ÿ”ง Debug pipelines

1
strace -f -e trace=pipe,read,write sh script.sh

๐Ÿ”ง Debug subshells

Add:

1
echo "PID=$$" >&2

to see where forks occur.


โš ๏ธ Pitfalls

โš ๏ธ set -x does not show command substitution failures

1
result=$(failing_cmd)

The failure is hidden unless explicitly checked.

โš ๏ธ set -e does not propagate into subshells

1
2
( set -e; failing_cmd )
echo $?   # often 0

โš ๏ธ Pipelines hide errors without pipefail

โš ๏ธ Pipe buffer deadlocks (~64 KB)

Producer blocks when consumer is slow.

โš ๏ธ Traps do not fire in subshells

1
( trap 'echo hi' EXIT )

Runs only inside the subshell.


๐Ÿšจ Realโ€‘World Failures

๐Ÿšจ Failure: CI pipeline hangs with no logs

1
producer | consumer

If consumer is slow โ†’ pipe fills โ†’ producer blocks โ†’ pipeline freezes.

Debug:

1
strace -f -e trace=write,read producer

๐Ÿšจ Failure: Command substitution hides critical error

1
json=$(jq . config.json)

If jq fails โ†’ $json becomes empty โ†’ deployment breaks silently.


๐Ÿšจ Failure: Zombie processes in containers

Shell as PID 1 does not reap children.

Debug:

1
ps -o pid,ppid,stat,cmd

๐Ÿ› ๏ธ Patterns

๐Ÿ› ๏ธ Pattern: Debug mode flag

1
2
DEBUG=${DEBUG:-0}
((DEBUG)) && set -x

๐Ÿ› ๏ธ Pattern: Structured logging

1
log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" >&2; }

๐Ÿ› ๏ธ Pattern: PID tracing

1
echo "PID=$$ PPID=$PPID" >&2

๐Ÿ› ๏ธ Pattern: FD tracing

1
ls -l /proc/$$/fd >&2

โŒ 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ร— slower
  • strace โ†’ 10โ€“100ร— slower
  • heavy logging โ†’ I/O bottlenecks

Use selectively.


๐Ÿงต Process Control

Debugging signals

1
strace -f -e trace=signal sh script.sh

Debugging job control

1
2
set -m
jobs -l

๐Ÿณ Containers

๐Ÿณ Debugging entrypoints

Add:

1
2
set -x
echo "PID=$$"

๐Ÿณ Debugging PID 1 signal handling

1
strace -f -e trace=signal -p 1

๐Ÿ›ฐ๏ธ CI/CD

๐Ÿ›ฐ๏ธ Debugging failing stages

  • enable set -x only for failing steps
  • log FD and PID information
  • always use pipefail

๐Ÿ›ฐ๏ธ Debugging race conditions

Insert:

1
sleep 0.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.