bashdb.sh负责生成debug档
#!/bin/bash #bashdb - bash debugger #该脚本将bashbd.pre和目标脚本处理成调试脚本 echo 'bash Debugger version 1.0' _dbname=${0##*/} if (( $# < 1 )); then echo "$_dbname: Usage: $_dbname filename" >&2 exit 1 fi _guineapig=$1 if [ ! -r $1 ]; then echo "$_dbname: Cannot read file '$_guineapig'." >&2 exit 1 fi shift _tmpdir=/tmp _libdir=. _debugfile=$_tmpdir/bashdb.$$ #正在被调试脚本的临时文件 cat $_libdir/bashdb.pre $_guineapig > $_debugfile exec bash $_debugfile $_guineapig $_tmpdir $_libdir "[email protected]"
bashdb.pre负责对被调试函数进行预处理 bashdb.pre负责对被调试函数进行预处理、 #!/bin/bash #bashdb预处理部分 #本文件预处理被调试的shell脚本 #参数: #$1=初始试验脚本的名字 #$2=临时文件所保存在的目录 #$3=bashdb.pre和bashdb.fns被保存的目录 _debugfile=$0 _guineapig=$1 _tmpdir=$2 _libdir=$3 shift 3 source $_libdir/bashdb.fns declare -a _linebp let _trace=0 let _i=1 while read; do _lines[$_i]=$REPLY let _i=$_i+1 done < $_guineapig trap _cleanup EXIT let _steps=1 LINENO=-2 trap '_steptrap $LINENO' DEBUG
bashdb.fns包含了DEBUG调用的调试函数
#!/bin/bash #测试脚本的每行被执行之后,shell进入本函数 function _steptrap { _curline=$1 #当前运行行的行号 (( $_trace )) && _msg "$PS4 line $_curline: ${_lines[$_curline]}" if (( $_steps >= 0 )); then let _steps=$_steps-1 fi #首先查看是否达到行编号断点 #如果达到,则进入调试器 if _at_linenumbp ; then _msg "Reached breakpoint at line $_curline" _cmdloop #如果没有达到,则检查是否有中断条件存在且为真 #如果是,则进入调试器 elif [ -n "$_brcond" ] && eval $_brcond; then _msg "Bread condition $_brcond true at line $_curline" _cmdloop #如果不是,则检查是否在采用步进方式,步数是否达到。如果是,则进入调试器 elif (( $_steps == 0 )); then _msg "Stopped at line $_curline" _cmdloop fi } #调试器命令循环 function _cmdloop { local cmd args while read -e -p "bashdb> " cmd args; do case $cmd in h ) _menu ;; #打印命令菜单 bc) _setbc $args ;; #设置中断条件 bp) _setbp $args ;; #设置断点在给定行 cb) _clearbp $args ;; #清除一个或所有断点 ds) _displayscript ;; #列出脚本并显示断点 g ) return ;; #开始/再继续执行脚本 q ) exit ;; #退出 s ) let _steps=${args:-1} #单步执行N次(默认为1) return ;; x ) _xtrace ;; #切换执行追踪 !*) eval ${cmd#!} $args ;; #传递给shell * ) _msg "Invalid command: '$cmd'" ;; esac done } #查看这个行编号是否有一个断点 function _at_linenumbp { local i=0 #循环遍历断点数组并查看它们是否与当前行编号匹配。如果匹配就返回真(0), #否则就返回假 if [ "$_linebp" ]; then while (( $i < ${#_linebp[@]} )); do if (( ${_linebp[$i]} == $_curline )); then return 0 fi let i=$i+1 done fi return 1 } #设置断点在给定的行编号或列出断点 function _setbp { local i #如果无参数,调用断点列表函数。否则查看参数是否为正数 #如果不是,则打印错误消息。如果是,则查看行编号是否包含文本 #如果不是则打印错误信息。如果是,则回应当前断点和新的附加。并将它们 #输送到"排序",并将结果赋值给断点列表。这将导致断点按数字顺序排列 #注意,使用-u选项可以删除重复的断点 if [ -z "$1" ]; then _listbp elif [ $(echo $1 | grep '^[0-9]*') ]; then if [ -n "${_lines[$1]}" ]; then _linebp=($(echo $( (for i in "${_linebp[*]} $1"; do echo $i; done) | sort -n) )) _msg "Breakpoint set at line $1" else _msg "Breakpoints can only be set on non-blank lines" fi else _msg "Please specify a numeric line number" fi } #列出断点及中断条件 function _listbp { if [ -n "$_linebp" ]; then _msg "Breakpoints at lines: ${_linebp[*]}" else _msg "No breakpoints have been set" fi _msg "Break on condition:" _msg "$_brcond" } #清除单个或所有断点 function _clearbp { local i bps #如果没有参数,那么删除所有断点。否则查看参数是否为正数,如果不是 #则打印错误消息。如果是,则回应除被传递的那个之外的所有当前断点 #并将它们赋值给局部变量。(我们需要这样做是因为将它们赋值给_linebp #将使数组保持在同一大小并将值向回移动一位置,导致重复值)。然后销毁旧数组 #并将局部数组中的元素赋值,于是我们高效地重创了它,减掉了被传递的断点 if [ -z "$1" ]; then unset _linebp[*] _msg "All breakpoints have been cleared" elif [ $(echo $1 | grep '^[0-9]*') ]; then bps=($(echo $(for i in ${_linebp[*]}; do if (( $1 != $i )); then echo $i; fi; done) )) unset _linebp[*] _linebp=(${bps[*]}) _msg "Breakpoint cleared at line $1" else _msg "Please specify a numeric line number" fi } #设置或清除中断条件 function _setbc { if [ -n "$*" ]; then _brcond=$args _msg "Break when true: $_brcond" else _brcond= _msg "Break condition cleared" fi } #打印出shell脚本并标出断点的位置以及当前行 function _displayscript { local i=1 j=0 bp cl ( while (( $i <= ${#_lines[@]} )); do if [ ${_linebp[$j]} ] && (( ${_linebp[$j]} == $i )); then bp='*' let j=$j+1 else bp=' ' fi if (( $_curline == $i )); then cl=">" else cl=" " fi echo "$i:$bp $cl ${_lines[$i]}" let i=$i+1 done ) | more } #切换执行追踪on/off function _xtrace { let _trace="! $_trace" _msg "Execution trace " if (( $_trace )); then _msg "on" else _msg "off" fi } #打印传进来的参数到标准错误 function _msg { echo -e "[email protected]" >&2 } #打印命令菜单 function _menu { _msg 'bashdb commands: bp N set breakpoint at line N bp list breakpoints and break condition bc string set break condition to string bc clear break condition cb N clear breakpoint at line N cb clear all breakpoints ds displays the test script and breakpoints g start/resume execution s [N] execute N statements (default 1) x toggle execution trace on/off h,? print this menu ! string passes string to a shell q quit' } #退出之前删除临时文件 function _cleanup { rm $_debugfile 2>/dev/null }