π¨ CRITICAL GUIDELINES
Windows File Path Requirements
MANDATORY: Always Use Backslashes on Windows for File Paths
When using Edit or Write tools on Windows, you MUST use backslashes (
) in file paths, NOT forward slashes (/
).
Examples:
-
β WRONG: D:/repos/project/file.tsx
-
β CORRECT: D:\repos\project\file.tsx
This applies to:
-
Edit tool file_path parameter
-
Write tool file_path parameter
-
All file operations on Windows systems
Documentation Guidelines
NEVER create new documentation files unless explicitly requested by the user.
-
Priority: Update existing README.md files rather than creating new documentation
-
Repository cleanliness: Keep repository root clean - only README.md unless user requests otherwise
-
Style: Documentation should be concise, direct, and professional - avoid AI-generated tone
-
User preference: Only create additional .md files when user specifically asks for documentation
Bash Debugging & Troubleshooting (2025)
Overview
Comprehensive debugging techniques and troubleshooting patterns for bash scripts following 2025 best practices.
Debug Mode Techniques
- Basic Debug Mode (set -x)
#!/usr/bin/env bash set -euo pipefail
Enable debug mode
set -x
Your commands here
command1 command2
Disable debug mode
set +x
Continue without debug
command3
- Enhanced Debug Output (PS4)
#!/usr/bin/env bash set -euo pipefail
Custom debug prompt with file:line:function
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x my_function() { local var="value" echo "$var" } my_function set +x
Output:
+(script.sh:10): my_function(): local var=value +(script.sh:11): my_function(): echo value value
- Conditional Debugging
#!/usr/bin/env bash set -euo pipefail
Enable via environment variable
DEBUG="${DEBUG:-false}"
debug() { if [[ "$DEBUG" == "true" ]]; then echo "[DEBUG] $*" >&2 fi }
Usage
debug "Starting process" process_data debug "Process complete"
Run: DEBUG=true ./script.sh
- Debugging Specific Functions
#!/usr/bin/env bash set -euo pipefail
Debug wrapper
debug_function() { local func_name="$1" shift
echo "[TRACE] Calling: $func_name $*" >&2
set -x
"$func_name" "$@"
local exit_code=$?
set +x
echo "[TRACE] Exit code: $exit_code" >&2
return $exit_code
}
Usage
my_complex_function() { local arg1="$1" # Complex logic echo "Result: $arg1" }
debug_function my_complex_function "test"
Tracing and Profiling
- Execution Time Profiling
#!/usr/bin/env bash set -euo pipefail
Profile function execution time
profile() { local start_ns end_ns duration_ms start_ns=$(date +%s%N)
"$@"
local exit_code=$?
end_ns=$(date +%s%N)
duration_ms=$(( (end_ns - start_ns) / 1000000 ))
echo "[PROFILE] '$*' took ${duration_ms}ms (exit: $exit_code)" >&2
return $exit_code
}
Usage
profile slow_command arg1 arg2
- Function Call Tracing
#!/usr/bin/env bash set -euo pipefail
Trace all function calls
trace_on() { set -o functrace trap 'echo "[TRACE] ${FUNCNAME[0]}() called from ${BASH_SOURCE[1]}:${BASH_LINENO[0]}" >&2' DEBUG }
trace_off() { set +o functrace trap - DEBUG }
Usage
trace_on function1 function2 trace_off
- Variable Inspection
#!/usr/bin/env bash set -euo pipefail
Inspect all variables at any point
inspect_vars() { echo "=== Variable Dump ===" >&2 declare -p | grep -v "^declare -[^ ]*r " | sort >&2 echo "===================" >&2 }
Inspect specific variable
inspect_var() { local var_name="$1" echo "[INSPECT] $var_name = ${!var_name:-<unset>}" >&2 }
Usage
my_var="test" inspect_var my_var inspect_vars
Error Handling and Recovery
- Trap-Based Error Handler
#!/usr/bin/env bash set -euo pipefail
Comprehensive error handler
error_handler() { local exit_code=$? local line_number=$1
echo "ERROR: Command failed with exit code $exit_code" >&2
echo " File: ${BASH_SOURCE[1]}" >&2
echo " Line: $line_number" >&2
echo " Function: ${FUNCNAME[1]:-main}" >&2
# Print stack trace
local frame=0
while caller $frame; do
((frame++))
done >&2
exit "$exit_code"
}
trap 'error_handler $LINENO' ERR
Your script logic
risky_command
- Dry-Run Mode
#!/usr/bin/env bash set -euo pipefail
DRY_RUN="${DRY_RUN:-false}"
Safe execution wrapper
execute() { if [[ "$DRY_RUN" == "true" ]]; then echo "[DRY-RUN] Would execute: $*" >&2 return 0 else "$@" fi }
Usage
execute rm -rf /tmp/data execute cp file.txt backup/
Run: DRY_RUN=true ./script.sh
- Rollback on Failure
#!/usr/bin/env bash set -euo pipefail
OPERATIONS=()
Track operations for rollback
track_operation() { local rollback_cmd="$1" OPERATIONS+=("$rollback_cmd") }
Execute rollback
rollback() { echo "Rolling back operations..." >&2 for ((i=${#OPERATIONS[@]}-1; i>=0; i--)); do echo " Executing: ${OPERATIONS[$i]}" >&2 eval "${OPERATIONS[$i]}" || true done }
trap rollback ERR EXIT
Example usage
mkdir /tmp/mydir track_operation "rmdir /tmp/mydir"
touch /tmp/mydir/file.txt track_operation "rm /tmp/mydir/file.txt"
If script fails, rollback executes automatically
Common Issues and Solutions
- Script Works Interactively but Fails in Cron
Problem: Script runs fine manually but fails when scheduled.
Solution:
#!/usr/bin/env bash set -euo pipefail
Fix PATH for cron
export PATH="/usr/local/bin:/usr/bin:/bin"
Set working directory
cd "$(dirname "$0")" || exit 1
Log everything for debugging
exec 1>> /var/log/myscript.log 2>&1
echo "[$(date)] Script starting"
Your commands here
echo "[$(date)] Script complete"
- Whitespace in Filenames Breaking Script
Problem: Script fails when processing files with spaces.
Debugging:
#!/usr/bin/env bash set -euo pipefail
Show exactly what the script sees
debug_filename() { local filename="$1" echo "Filename: '$filename'" >&2 echo "Length: ${#filename}" >&2 hexdump -C <<< "$filename" >&2 }
Proper handling
while IFS= read -r -d '' file; do debug_filename "$file" # Process "$file" done < <(find . -name "*.txt" -print0)
- Script Behaves Differently on Different Systems
Problem: Works on Linux but fails on macOS.
Debugging:
#!/usr/bin/env bash set -euo pipefail
Platform detection and debugging
detect_platform() { echo "=== Platform Info ===" >&2 echo "OS: $OSTYPE" >&2 echo "Bash: $BASH_VERSION" >&2 echo "PATH: $PATH" >&2
# Check tool versions
for tool in sed awk grep; do
if command -v "$tool" &> /dev/null; then
echo "$tool: $($tool --version 2>&1 | head -1)" >&2
fi
done
echo "====================" >&2
}
detect_platform
Use portable patterns
case "$OSTYPE" in linux*) SED_CMD="sed" ;; darwin*) SED_CMD=$(command -v gsed || echo sed) ;; *) echo "Unknown platform" >&2; exit 1 ;; esac
- Variable Scope Issues
Problem: Variables not available where expected.
Debugging:
#!/usr/bin/env bash set -euo pipefail
Show variable scope
test_scope() { local local_var="local" global_var="global"
echo "Inside function:" >&2
echo " local_var=$local_var" >&2
echo " global_var=$global_var" >&2
}
test_scope
echo "Outside function:" >&2 echo " local_var=${local_var:-<not set>}" >&2 echo " global_var=${global_var:-<not set>}" >&2
Subshell scope issue
echo "test" | ( read -r value echo "In subshell: $value" ) echo "After subshell: ${value:-<not set>}" # Empty!
Interactive Debugging
- Breakpoint Pattern
#!/usr/bin/env bash set -euo pipefail
Interactive breakpoint
breakpoint() { local message="${1:-Breakpoint}" echo "$message" >&2 echo "Variables:" >&2 declare -p | grep -v "^declare -[^ ]*r " >&2
read -rp "Press Enter to continue, 'i' for inspect: " choice
if [[ "$choice" == "i" ]]; then
bash # Drop into interactive shell
fi
}
Usage
value=42 breakpoint "Before critical operation" critical_operation "$value"
- Watch Mode (Continuous Debugging)
#!/usr/bin/env bash
Watch script execution in real-time
watch_script() { local script="$1" shift
while true; do
clear
echo "=== Running: $script $* ==="
echo "=== $(date) ==="
bash -x "$script" "$@" 2>&1 | tail -50
sleep 2
done
}
Usage: watch_script myscript.sh arg1 arg2
Logging Best Practices
- Structured Logging
#!/usr/bin/env bash set -euo pipefail
readonly LOG_FILE="${LOG_FILE:-/var/log/myscript.log}"
log() { local level="$1" shift local timestamp timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "${timestamp} [${level}] $*" | tee -a "$LOG_FILE" >&2
}
log_info() { log "INFO" "$@"; } log_warn() { log "WARN" "$@"; } log_error() { log "ERROR" "$@"; } log_debug() { [[ "${DEBUG:-false}" == "true" ]] && log "DEBUG" "$@"; }
Usage
log_info "Starting process" log_debug "Debug info" log_error "Something failed"
- Log Rotation Awareness
#!/usr/bin/env bash set -euo pipefail
Ensure log file exists and is writable
setup_logging() { local log_file="${1:-/var/log/myscript.log}" local log_dir log_dir=$(dirname "$log_file")
if [[ ! -d "$log_dir" ]]; then
mkdir -p "$log_dir" || {
echo "Cannot create log directory: $log_dir" >&2
return 1
}
fi
if [[ ! -w "$log_dir" ]]; then
echo "Log directory not writable: $log_dir" >&2
return 1
fi
# Redirect all output to log
exec 1>> "$log_file"
exec 2>&1
}
setup_logging
Performance Debugging
- Identify Slow Commands
#!/usr/bin/env bash set -euo pipefail
Profile each command in script
profile_script() { export PS4='+ $(date +%s.%N) ${BASH_SOURCE}:${LINENO}: ' set -x
# Your commands here
command1
command2
command3
set +x
}
Analyze output:
+ 1698765432.123456 script.sh:10: command1 (fast)
+ 1698765437.654321 script.sh:11: command2 (5 seconds - slow!)
- Memory Usage Tracking
#!/usr/bin/env bash set -euo pipefail
Track memory usage
check_memory() { local pid=${1:-$$} ps -o pid,vsz,rss,comm -p "$pid" | tail -1 }
Monitor during execution
monitor_memory() { while true; do check_memory sleep 1 done & local monitor_pid=$!
# Your commands here
"$@"
kill "$monitor_pid" 2>/dev/null || true
wait "$monitor_pid" 2>/dev/null || true
}
monitor_memory ./memory_intensive_task.sh
Testing Patterns
- Unit Test Template
#!/usr/bin/env bash
test_functions.sh
Source the script to test
source ./functions.sh
Test counter
TESTS_RUN=0 TESTS_PASSED=0 TESTS_FAILED=0
Assert function
assert_equals() { local expected="$1" local actual="$2" local test_name="${3:-Test}"
((TESTS_RUN++))
if [[ "$expected" == "$actual" ]]; then
echo "β $test_name" >&2
((TESTS_PASSED++))
else
echo "β $test_name" >&2
echo " Expected: $expected" >&2
echo " Actual: $actual" >&2
((TESTS_FAILED++))
fi
}
Run tests
test_add_numbers() { local result result=$(add_numbers 2 3) assert_equals "5" "$result" "add_numbers 2 3" }
test_add_numbers
Summary
echo "========================================" >&2 echo "Tests run: $TESTS_RUN" >&2 echo "Passed: $TESTS_PASSED" >&2 echo "Failed: $TESTS_FAILED" >&2
[[ $TESTS_FAILED -eq 0 ]]
ShellCheck Integration
#!/usr/bin/env bash set -euo pipefail
Validate script with ShellCheck
validate_script() { local script="$1"
if ! command -v shellcheck &> /dev/null; then
echo "ShellCheck not installed" >&2
return 1
fi
echo "Running ShellCheck on $script..." >&2
if shellcheck --severity=warning "$script"; then
echo "β ShellCheck passed" >&2
return 0
else
echo "β ShellCheck failed" >&2
return 1
fi
}
Usage
validate_script myscript.sh
Resources
-
Bash Hackers Wiki - Debugging
-
ShellCheck
-
BATS Testing Framework
-
Bash Reference Manual - Debugging
Effective debugging requires systematic approaches, comprehensive logging, and proper tooling. Master these techniques for production-ready bash scripts in 2025.