Debugging
| Since: | set -x(トレース) | POSIX(sh互換) |
|---|---|---|
| PS4 / trap DEBUG / BASH_SOURCE / LINENO | Bash(bash拡張) |
Shell scripts include built-in debugging features for tracing and inspecting script behavior in detail. set -x enables trace mode, which prints each command to standard error just before it executes, showing the actual values after variable expansion. trap '...' DEBUG lets you insert custom processing before or after every command. You can also customize the PS4 environment variable to add line numbers or timestamps to trace output for even more detail. For full step-through debugging, the interactive debugger bashdb is available.
Syntax
# -----------------------------------------------
# set -x / set +x Enable/disable trace mode
# -----------------------------------------------
set -x # Start tracing subsequent commands
コマンド群
set +x # Stop tracing
# To trace the entire script, write this right after the shebang
#!/bin/bash
set -x
# -----------------------------------------------
# PS4 Customize the trace line prefix
# -----------------------------------------------
# Default is "+" (one "+" per nesting level)
# A common setting that adds line number and script name
export PS4='+(${BASH_SOURCE}:${LINENO}): '
# Add a timestamp to measure performance as well
export PS4='[$(date +%T.%3N)] ${BASH_SOURCE}:${LINENO}: '
# -----------------------------------------------
# trap '...' DEBUG Intercept before every command
# -----------------------------------------------
trap 'echo "LINE ${LINENO}: ${BASH_COMMAND}"' DEBUG
# -----------------------------------------------
# bashdb How to launch the interactive debugger
# -----------------------------------------------
bashdb スクリプト名.sh # Launch with bashdb
bashdb -- スクリプト名.sh 引数 # Launch with arguments
Debugging Techniques
| Technique / Syntax | Description |
|---|---|
set -x | Enables trace mode. Prints each command to standard error after variable expansion, just before execution. |
set +x | Disables trace mode. Wrap only the section you want to inspect to keep output focused. |
set -v | Enables verbose mode. Prints each line of the script as it is read, before expansion. |
PS4 | Environment variable that sets the prefix string for trace output. Use ${LINENO} and ${BASH_SOURCE} to include line numbers and filenames. |
trap '...' DEBUG | Runs the specified command just before every command in the script. Can be used as a watchpoint to monitor variable changes. |
BASH_COMMAND | Special variable holding the string of the currently executing command. Referenced inside a trap DEBUG handler. |
LINENO | Special variable holding the current line number. Embed it in error messages or PS4 for location context. |
BASH_SOURCE | Array of script filenames. Use ${BASH_SOURCE[0]} to get the name of the current file. |
FUNCNAME | Array of function names on the call stack. Use ${FUNCNAME[0]} to get the name of the current function. |
bashdb | An interactive debugger for bash. Supports breakpoints, step execution, and interactive variable inspection. |
Sample Code
db_debug_trace.sh
#!/bin/bash
# -----------------------------------------------
# Demonstrates trace debugging with set -x and PS4
# while processing Dragon Ball character data
# -----------------------------------------------
# -----------------------------------------------
# Customize PS4
# Add the filename and line number to trace output
# -----------------------------------------------
export PS4='+(${BASH_SOURCE[0]##*/}:${LINENO}): '
# -----------------------------------------------
# Define character data as arrays
# -----------------------------------------------
characters=("孫悟空" "ベジータ" "ピッコロ" "クリリン" "フリーザ")
powers=(9000 8000 3500 1750 530000)
# -----------------------------------------------
# Function to find the index of the highest power level
# -----------------------------------------------
find_max_power() {
local max_index=0
local max_val=${powers[0]}
# Compare the power level of each character
for i in "${!powers[@]}"; do
if [ "${powers[$i]}" -gt "${max_val}" ]; then
max_val=${powers[$i]}
max_index=$i
fi
done
echo "${max_index}"
}
echo "=== 戦闘力ランキング ==="
echo ""
# -----------------------------------------------
# Trace only this section with set -x
# -----------------------------------------------
set -x
# Get the index of the highest power level
max_idx=$(find_max_power)
# Display the result
echo "最強キャラクター: ${characters[$max_idx]}"
echo "戦闘力: ${powers[$max_idx]}"
set +x
# -----------------------------------------------
# End of trace mode
# -----------------------------------------------
echo ""
echo "=== 全キャラクター一覧 ==="
for i in "${!characters[@]}"; do
printf " %-30s 戦闘力: %6d\n" "${characters[$i]}" "${powers[$i]}"
done
$ chmod +x db_debug_trace.sh
$ ./db_debug_trace.sh
=== 戦闘力ランキング ===
+(db_debug_trace.sh:40): find_max_power
+(db_debug_trace.sh:18): local max_index=0
+(db_debug_trace.sh:19): local max_val=9000
+(db_debug_trace.sh:22): for i in "${!powers[@]}"
+(db_debug_trace.sh:23): '[' 9000 -gt 9000 ']'
+(db_debug_trace.sh:22): for i in "${!powers[@]}"
+(db_debug_trace.sh:23): '[' 8000 -gt 9000 ']'
+(db_debug_trace.sh:22): for i in "${!powers[@]}"
+(db_debug_trace.sh:23): '[' 3500 -gt 9000 ']'
+(db_debug_trace.sh:22): for i in "${!powers[@]}"
+(db_debug_trace.sh:23): '[' 1750 -gt 9000 ']'
+(db_debug_trace.sh:22): for i in "${!powers[@]}"
+(db_debug_trace.sh:23): '[' 530000 -gt 9000 ']'
+(db_debug_trace.sh:24): max_val=530000
+(db_debug_trace.sh:25): max_index=4
+(db_debug_trace.sh:29): echo 4
+(db_debug_trace.sh:40): max_idx=4
+(db_debug_trace.sh:43): echo '最強キャラクター: フリーザ'
最強キャラクター: フリーザ
+(db_debug_trace.sh:44): echo '戦闘力: 530000'
戦闘力: 530000
+(db_debug_trace.sh:46): set +x
=== 全キャラクター一覧 ===
孫悟空 戦闘力: 9000
ベジータ 戦闘力: 8000
ピッコロ 戦闘力: 3500
クリリン 戦闘力: 1750
フリーザ 戦闘力: 530000
db_debug_trap.sh
#!/bin/bash
# -----------------------------------------------
# Uses trap '...' DEBUG to watch for variable changes.
# Tracks score accumulation for Dragon Ball characters
# command by command.
# -----------------------------------------------
# -----------------------------------------------
# Specify the variable name to watch.
# Prints a message whenever this variable changes.
# -----------------------------------------------
WATCH_VAR="total_score"
total_score=0
# -----------------------------------------------
# DEBUG trap handler
# Called before every command.
# BASH_COMMAND holds the command about to be executed.
# -----------------------------------------------
debug_handler() {
# Only log commands that involve total_score
case "${BASH_COMMAND}" in
*total_score*) echo " [DEBUG L${LINENO}] CMD: ${BASH_COMMAND}" >&2 ;;
esac
}
trap debug_handler DEBUG
# -----------------------------------------------
# Accumulate battle scores for Dragon Ball characters
# -----------------------------------------------
declare -A scores
scores["孫悟空"]=3
scores["ベジータ"]=2
scores["ピッコロ"]=4
scores["クリリン"]=1
scores["フリーザ"]=5
echo "=== バトルスコア集計 ==="
echo ""
for name in "孫悟空" "ベジータ" "ピッコロ" "クリリン" "フリーザ"; do
point=${scores[$name]}
total_score=$((total_score + point))
echo " ${name}: ${point}点 (累計: ${total_score}点)"
done
echo ""
echo "=== 最終合計: ${total_score}点 ==="
# Remove the DEBUG trap
trap - DEBUG
$ chmod +x db_debug_trap.sh $ ./db_debug_trap.sh === バトルスコア集計 === [DEBUG L42] CMD: total_score=$((total_score + point)) 孫悟空: 3点 (累計: 3点) [DEBUG L42] CMD: total_score=$((total_score + point)) ベジータ: 2点 (累計: 5点) [DEBUG L42] CMD: total_score=$((total_score + point)) ピッコロ: 4点 (累計: 9点) [DEBUG L42] CMD: total_score=$((total_score + point)) クリリン: 1点 (累計: 10点) [DEBUG L42] CMD: total_score=$((total_score + point)) フリーザ: 5点 (累計: 15点) === 最終合計: 15点 ===
Notes
A step-by-step approach is effective for debugging shell scripts. Start by enabling trace mode with set -x to see commands after variable expansion. If the output becomes too noisy, wrap only the section you care about between set -x and set +x. Setting PS4 to ${BASH_SOURCE[0]##*/}:${LINENO} adds the filename and line number, so you can immediately pinpoint where and what is happening.
trap '...' DEBUG intercepts execution before every command. Combined with the BASH_COMMAND variable, it acts as a watchpoint that selectively logs only the changes you care about. The special variables LINENO, BASH_SOURCE, and FUNCNAME let you embed location information in error messages. Use bashdb for full-featured debugging that requires breakpoints and step execution. For the basics of signals, see also trap (signal handling); for script-wide error control, see set options (-e / -u / -o pipefail).
If you find any errors or copyright issues, please contact us.