BASH调试器
时间:2008-05-02 来源:剑心通明
感谢作者:LYOO兄[原创]
代码:
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 "$@"
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
代码:
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 "$@"
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 "$@" >&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
}
相关阅读 更多 +