BASH调试器

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负责对被调试函数进行预处理



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
}  
标签: 无
返回文章列表 文章二维码
本页链接的二维码
打赏二维码