Debugging
Chapter 31: Debugging Techniques in Bash
Section titled “Chapter 31: Debugging Techniques in Bash”Overview
Section titled “Overview”Debugging bash scripts is essential for DevOps engineers. This chapter covers various techniques to identify and fix issues in bash scripts, from simple echo statements to advanced debugging tools.
Debugging Options
Section titled “Debugging Options”set -x (Trace)
Section titled “set -x (Trace)”#!/bin/bash# Enable command tracing
set -x # Start tracing
echo "Hello World"ls -la
set +x # Stop tracing
echo "Tracing disabled"set -v (Print Input)
Section titled “set -v (Print Input)”#!/bin/bash# Print shell input lines as they are read
set -v
echo "This will be printed"cat <<EOFThis here documentwill be printedline by lineEOF
set +vCombined Options
Section titled “Combined Options”#!/bin/bash
# Multiple optionsset -xv
# Or using flagsset -o xtrace -o verboseDebug Mode Flags
Section titled “Debug Mode Flags”Common Debug Options
Section titled “Common Debug Options”| Option | Flag | Description |
|---|---|---|
| errexit | -e | Exit on error |
| nounset | -u | Exit on undefined variable |
| pipefail | -o pipefail | Pipeline fails on error |
| xtrace | -x | Print commands before execution |
| verbose | -v | Print input as read |
| noexec | -n | Check syntax without executing |
Recommended Debug Mode
Section titled “Recommended Debug Mode”#!/bin/bash# Recommended for developmentset -euo pipefail -x
# In production (less verbose)set -euo pipefailDebugging Techniques
Section titled “Debugging Techniques”Echo Debugging
Section titled “Echo Debugging”#!/bin/bash# Simple debug output
debug() { echo "[DEBUG] $*"}
debug "Starting script"debug "Variable value: $myvar"
# Conditional debugDEBUG=1if [[ $DEBUG -eq 1 ]]; then set -xfiPS4 for Better Tracing
Section titled “PS4 for Better Tracing”#!/bin/bash# Customize trace output
# Default: + script.sh:linenumber:command# Custom: show more infoexport PS4='+[$(date +%H:%M:%S)] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x
echo "Hello"myvar="test"echo "$myvar"Using bash -x
Section titled “Using bash -x”Command Line Debugging
Section titled “Command Line Debugging”# Debug script executionbash -x script.sh
# Debug with specific optionsbash -x -e script.shbash -x -u script.shbash -x -o pipefail script.sh
# Debug specific functionsbash -c 'source script.sh; function_name'Debug with Core Options
Section titled “Debug with Core Options”# Syntax check onlybash -n script.sh
# Interactive debuggingbash -i script.sh
# Trace specific portions{ set -x; command; set +x; }Using shellcheck
Section titled “Using shellcheck”Install and Use
Section titled “Install and Use”# Install shellcheck (Arch Linux)sudo pacman -S shellcheck
# Run shellcheckshellcheck script.sh
# With specific optionsshellcheck -s bash script.shshellcheck -e SC1090,SC1091 script.sh # Ignore specific warnings
# Enable all available checksshellcheck -a script.shCommon Warnings
Section titled “Common Warnings”# SC2086: Quote variables# shellcheck disable=SC2086echo $var # Quoted version: echo "$var"
# SC1090: Can't follow non-constant source# shellcheck disable=SC1090source "$SCRIPT"
# SC2029: Use escape for server-side variables# shellcheck disable=SC2029ssh user@host "echo $local_var"Using shdb (Bash Debugger)
Section titled “Using shdb (Bash Debugger)”Basic Usage
Section titled “Basic Usage”# Installsudo pacman -S bashdb
# Run debuggerbashdb script.sh
# Debugger commands:# break [file:]line# continue# step# next# print $variable# watch variable# backtraceCommon Issues and Fixes
Section titled “Common Issues and Fixes”Unset Variables
Section titled “Unset Variables”#!/bin/bash
# Problem: using unset variableset -u
echo "$undefined_var" # Error!
# Fix: use default valueecho "${undefined_var:-default}"
# Or set default: "${VAR:=default}"Failed Commands
Section titled “Failed Commands”#!/bin/bashset -e
# Problem: command failure stops scriptfalseecho "This never runs"
# Fix: handle failurefalse || echo "Command failed"false && echo "Command succeeded"
# Or use command that always succeedsfalse ; trueArray Issues
Section titled “Array Issues”#!/bin/bash
# Problem: word splittingarr=( $(ls) ) # Breaks on spaces
# Fix: use globbingarr=( * ) # Safe
# Or use read with IFSread -ra arr <<< "$(ls)" # Still problematicmapfile -t arr < <(ls) # BestLogging Strategies
Section titled “Logging Strategies”Log to File
Section titled “Log to File”#!/bin/bash# Logging function
log() { local level="$1" shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a /var/log/script.log}
log INFO "Starting script"log ERROR "Something went wrong"log DEBUG "Variable value: $var"Debug Logging
Section titled “Debug Logging”#!/bin/bash
DEBUG="${DEBUG:-0}"
debug() { if [[ "$DEBUG" == "1" ]]; then echo "[DEBUG] $*" >&2 fi}
debug "Processing file: $filename"Variable Inspection
Section titled “Variable Inspection”Print All Variables
Section titled “Print All Variables”# Print all variablesdeclare -p
# Print environment variablesprintenv
# Print functionsdeclare -Fdeclare -f function_nameTrap DEBUG
Section titled “Trap DEBUG”#!/bin/bash
# Run before each commandDEBUG_TRAP() { echo "About to run: $BASH_COMMAND"}
trap DEBUG_TRAP DEBUG
echo "Hello"lsAdvanced Debugging
Section titled “Advanced Debugging”Capture Trace Output
Section titled “Capture Trace Output”#!/bin/bash
# Capture trace to fileexec 4>&2 # Save stderr to fd 4exec 2> >(tee /tmp/trace.log)
set -x
echo "Hello"ls -laConditional Debug
Section titled “Conditional Debug”#!/bin/bash
# Debug modeDEBUG="${DEBUG:-0}"
if [[ "$DEBUG" == "1" ]]; then set -xfi
# Or toggledebug_on() { set -x; }debug_off() { set +x; }Real-World Debugging
Section titled “Real-World Debugging”Script Template with Debugging
Section titled “Script Template with Debugging”#!/usr/bin/env bash
# =============================================================================# Script Template with Debugging# =============================================================================
set -o errexit # Exit on errorset -o nounset # Exit on unset variableset -o pipefail # Exit on pipeline error
# Debug modeDEBUG="${DEBUG:-0}"
if [[ "$DEBUG" == "1" ]]; then set -o xtracefi
# LoggingLOG_FILE="${LOG_FILE:-/tmp/script.log}"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"}
log_error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "$LOG_FILE" >&2}
# Cleanup trapcleanup() { local exit_status=$? log "Cleaning up (exit status: $exit_status)" # Add cleanup code here exit $exit_status}
trap cleanup EXIT
# ============================================================================# Main Script# ============================================================================
main() { log "Starting script"
# Your code here echo "Hello, World!"
log "Script completed"}
main "$@"Summary
Section titled “Summary”In this chapter, you learned:
- ✅ Using set -x for tracing
- ✅ Using set -v for verbose output
- ✅ Debug mode flags and options
- ✅ PS4 for better tracing
- ✅ Using shellcheck for linting
- ✅ Common issues and fixes
- ✅ Logging strategies
- ✅ Variable inspection
- ✅ Advanced debugging techniques
- ✅ Real-world debugging template
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Strict Mode and best practices.
Previous Chapter: Exit Status and Error Handling Next Chapter: Strict Mode (set -euo pipefail)