LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 7227|回复: 7

bash脚本调试器

[复制链接]
发表于 2003-4-30 23:26:31 | 显示全部楼层 |阅读模式
抄来的一个实用的脚本,不过还没完全调通,有兴趣的朋友可以自己改改。

调试器的大体思路是这样:
由bashdb.sh将bashdb.pre与被调试脚本合并成一个debug版脚本临时文件,执行该debug版脚本,通过调用Bush内置的DEBUG的伪信号,来执行bashdb.fns中的相关调试函数。其它细节见脚本中的注释。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2003-5-1 01:09:01 | 显示全部楼层
谢谢LYOO兄,如果文件不是很大的话,还是帖出源码来吧~~,这样看着也方便,拷贝粘贴也很便捷,;)
发表于 2003-5-1 02:04:44 | 显示全部楼层
是不是用shell的内置函数eval也可以做到呢?!我的思路是这样的:
1,等待用户输入语句命令之类的字符,以{开头,以}结尾.
2,然后交给eval函数,把你所输入的字符/语句,当做eval的[argment],shell就会执行eval [argment] 命令.
3,如果eval返回状态为真,那么再等待用户输入下个{ ...}
4,如果有错误,就显示错误信息.
这样就可以在命令行下调试短小的脚本啦~~
思路归思路,还得多实验,还请LYOO兄指教,看是否行得通? ;)
 楼主| 发表于 2003-5-1 10:53:32 | 显示全部楼层
eval的这个用法的确比较有趣,可来检查语法错误,但用来跟踪变量值就勉为其难了。另外用bash -n来检查语法已相当方便了。
我想调试器更重要的目的是随时跟踪变量的值(我总是用加echo的方法来跟踪)。执行完某语句后用trap function DEBUG信号产生中断,更灵活,调入的function调试函数,可扩展出更多功能,如echo显示变量当前值,加判断条件等等。
以上是我的个人理解,呵呵,不对之处请大家指正。(尤其我对eval的使用还很生疏)
 楼主| 发表于 2003-5-4 13:07:37 | 显示全部楼层
高手就是会想,把这些简单的零件组合就成了功能强大的武器了。
终于把它给读通了,学了不少东西。不过这个脚本不能跟踪进入函数,下一步想试着加上跟踪函数的功能,javalee也来一起玩一把?

bashdb.sh负责生成debug档

  1. #!/bin/bash

  2. #bashdb - bash debugger
  3. #该脚本将bashbd.pre和目标脚本处理成调试脚本

  4. echo 'bash Debugger version 1.0'

  5. _dbname=${0##*/}

  6. if (( $# < 1 )); then
  7.     echo "$_dbname: Usage: $_dbname filename" >&2
  8.     exit 1
  9. fi

  10. _guineapig=$1

  11. if [ ! -r $1 ]; then
  12.     echo "$_dbname: Cannot read file '$_guineapig'." >&2
  13.     exit 1
  14. fi

  15. shift

  16. _tmpdir=/tmp
  17. _libdir=.
  18. _debugfile=$_tmpdir/bashdb.$$       #正在被调试脚本的临时文件
  19. cat $_libdir/bashdb.pre $_guineapig > $_debugfile
  20. exec bash $_debugfile $_guineapig $_tmpdir $_libdir "$@"
复制代码


bashdb.pre负责对被调试函数进行预处理

  1. #!/bin/bash

  2. #bashdb预处理部分
  3. #本文件预处理被调试的shell脚本
  4. #参数:
  5. #$1=初始试验脚本的名字
  6. #$2=临时文件所保存在的目录
  7. #$3=bashdb.pre和bashdb.fns被保存的目录

  8. _debugfile=$0
  9. _guineapig=$1
  10. _tmpdir=$2
  11. _libdir=$3

  12. shift 3

  13. source $_libdir/bashdb.fns
  14. declare -a _linebp
  15. let _trace=0
  16. let _i=1

  17. while read; do
  18.     _lines[$_i]=$REPLY
  19.     let _i=$_i+1
  20. done < $_guineapig

  21. trap _cleanup EXIT
  22. let _steps=1
  23. LINENO=-2
  24. trap '_steptrap $LINENO' DEBUG
复制代码


bashdb.fns包含了DEBUG调用的调试函数

  1. #!/bin/bash

  2. #测试脚本的每行被执行之后,shell进入本函数

  3. function _steptrap
  4. {
  5.         _curline=$1                #当前运行行的行号
  6.         (( $_trace )) && _msg "$PS4 line $_curline: ${_lines[$_curline]}"

  7.         if (( $_steps >= 0 )); then
  8.                 let _steps=$_steps-1
  9.         fi

  10.         #首先查看是否达到行编号断点
  11.         #如果达到,则进入调试器
  12.         if _at_linenumbp ; then
  13.                 _msg "Reached breakpoint at line $_curline"
  14.                 _cmdloop
  15.        
  16.         #如果没有达到,则检查是否有中断条件存在且为真
  17.         #如果是,则进入调试器
  18.         elif [ -n "$_brcond" ] && eval $_brcond; then
  19.                 _msg "Bread condition $_brcond true at line $_curline"
  20.                 _cmdloop
  21.        
  22.         #如果不是,则检查是否在采用步进方式,步数是否达到。如果是,则进入调试器
  23.         elif (( $_steps == 0 )); then
  24.                 _msg "Stopped at line $_curline"
  25.                 _cmdloop
  26.         fi
  27. }

  28. #调试器命令循环

  29. function _cmdloop {
  30.         local cmd args

  31.         while read -e -p "bashdb> " cmd args; do
  32.                 case $cmd in
  33.                         h ) _menu ;;                                        #打印命令菜单
  34.                         bc) _setbc $args ;;                                #设置中断条件
  35.                         bp) _setbp $args ;;                                #设置断点在给定行
  36.                         cb) _clearbp $args ;;                        #清除一个或所有断点
  37.                         ds) _displayscript ;;                        #列出脚本并显示断点
  38.                         g ) return ;;                                        #开始/再继续执行脚本
  39.                         q ) exit ;;                                                #退出
  40.                         s ) let _steps=${args:-1}                 #单步执行N次(默认为1)
  41.                                 return ;;
  42.                         x ) _xtrace ;;                                        #切换执行追踪
  43.                         !*) eval ${cmd#!} $args ;;                #传递给shell
  44.                         * ) _msg "Invalid command: '$cmd'" ;;
  45.                 esac
  46.         done
  47. }

  48. #查看这个行编号是否有一个断点
  49. function _at_linenumbp
  50. {
  51.         local i=0

  52.         #循环遍历断点数组并查看它们是否与当前行编号匹配。如果匹配就返回真(0),
  53.         #否则就返回假

  54.         if [ "$_linebp" ]; then
  55.                 while (( $i < ${#_linebp[@]} )); do
  56.                         if (( ${_linebp[$i]} == $_curline )); then
  57.                                 return 0
  58.                         fi
  59.                         let i=$i+1
  60.                 done
  61.         fi
  62.         return 1
  63. }

  64. #设置断点在给定的行编号或列出断点
  65. function _setbp
  66. {
  67.         local i
  68.         #如果无参数,调用断点列表函数。否则查看参数是否为正数
  69.         #如果不是,则打印错误消息。如果是,则查看行编号是否包含文本
  70.         #如果不是则打印错误信息。如果是,则回应当前断点和新的附加。并将它们
  71.         #输送到“排序”,并将结果赋值给断点列表。这将导致断点按数字顺序排列

  72.         #注意,使用-u选项可以删除重复的断点

  73.         if [ -z "$1" ]; then
  74.                 _listbp
  75.         elif [ $(echo $1 | grep '^[0-9]*') ]; then
  76.                 if [ -n "${_lines[$1]}" ]; then
  77.                         _linebp=($(echo $( (for i in "${_linebp[*]} $1"; do
  78.                                         echo $i; done) | sort -n) ))
  79.                         _msg "Breakpoint set at line $1"
  80.                 else
  81.                         _msg "Breakpoints can only be set on non-blank lines"
  82.                 fi
  83.         else
  84.                 _msg "Please specify a numeric line number"
  85.         fi
  86. }

  87. #列出断点及中断条件
  88. function _listbp
  89. {
  90.         if [ -n "$_linebp" ]; then
  91.                 _msg "Breakpoints at lines: ${_linebp[*]}"
  92.         else
  93.                 _msg "No breakpoints have been set"
  94.         fi

  95.         _msg "Break on condition:"
  96.         _msg "$_brcond"
  97. }

  98. #清除单个或所有断点
  99. function _clearbp
  100. {
  101.         local i bps

  102.         #如果没有参数,那么删除所有断点。否则查看参数是否为正数,如果不是
  103.         #则打印错误消息。如果是,则回应除被传递的那个之外的所有当前断点
  104.         #并将它们赋值给局部变量。(我们需要这样做是因为将它们赋值给_linebp
  105.         #将使数组保持在同一大小并将值向回移动一位置,导致重复值)。然后销毁旧数组
  106.         #并将局部数组中的元素赋值,于是我们高效地重创了它,减掉了被传递的断点

  107.         if [ -z "$1" ]; then
  108.                 unset _linebp[*]
  109.                 _msg "All breakpoints have been cleared"
  110.         elif [ $(echo $1 | grep '^[0-9]*') ]; then
  111.                 bps=($(echo $(for i in ${_linebp[*]}; do
  112.                                 if (( $1 != $i )); then echo $i; fi; done) ))
  113.                 unset _linebp[*]
  114.                 _linebp=(${bps[*]})
  115.                 _msg "Breakpoint cleared at line $1"
  116.         else
  117.                 _msg "Please specify a numeric line number"
  118.         fi
  119. }

  120. #设置或清除中断条件
  121. function _setbc
  122. {
  123.         if [ -n "$*" ]; then
  124.                 _brcond=$args
  125.                 _msg "Break when true: $_brcond"
  126.         else
  127.                 _brcond=
  128.                 _msg "Break condition cleared"
  129.         fi
  130. }

  131. #打印出shell脚本并标出断点的位置以及当前行
  132. function _displayscript
  133. {
  134.         local i=1 j=0 bp cl
  135.         ( while (( $i <= ${#_lines[@]} )); do
  136.                 if [ ${_linebp[$j]} ] && (( ${_linebp[$j]} == $i )); then
  137.                         bp='*'
  138.                         let j=$j+1
  139.                 else
  140.                         bp=' '
  141.                 fi
  142.                 if (( $_curline == $i )); then
  143.                         cl=">"
  144.                 else
  145.                         cl=" "
  146.                 fi
  147.                 echo "$i:$bp $cl ${_lines[$i]}"
  148.                 let i=$i+1
  149.          done
  150.         ) | more
  151. }

  152. #切换执行追踪on/off
  153. function _xtrace
  154. {
  155.         let _trace="! $_trace"
  156.         _msg "Execution trace "
  157.         if (( $_trace )); then
  158.                 _msg "on"
  159.         else
  160.                 _msg "off"
  161.         fi
  162. }

  163. #打印传进来的参数到标准错误
  164. function _msg
  165. {
  166.         echo -e "$@" >&2
  167. }

  168. #打印命令菜单
  169. function _menu {
  170.         _msg 'bashdb commands:
  171.         bp N                set breakpoint at line N
  172.         bp                list breakpoints and break condition
  173.         bc string        set break condition to string
  174.         bc                clear break condition
  175.         cb N                clear breakpoint at line N
  176.         cb                clear all breakpoints
  177.         ds                displays the test script and breakpoints
  178.         g                start/resume execution
  179.         s [N]                execute N statements (default 1)
  180.         x                toggle execution trace on/off
  181.         h,?                print this menu
  182.         ! string        passes string to a shell
  183.         q                quit'
  184. }

  185. #退出之前删除临时文件
  186. function _cleanup
  187. {
  188.         rm $_debugfile 2>/dev/null
  189. }       

复制代码
发表于 2003-5-6 01:57:22 | 显示全部楼层
小弟佩服!放在[精华区]收藏啦~~
怎么这两天总也上不来论坛!急死我啦~~
发表于 2003-8-18 21:02:19 | 显示全部楼层
怎么不把附带的七个限制也一把抄上来?
我也四处找调试bash脚本的工具,可就是没找到,用上面那个的话限制起来不如用echo调试。
少的话不用bashdb也可以了,多了的话用来又说会慢十倍以上,怕怕。这里牛人那么多,怎么不给这些做后辈的用C/C++写个bash脚本的调试器?
发表于 2006-9-19 11:22:15 | 显示全部楼层
有一个Bash调式工具在 http://bashdb.sourceforge.net/
大家可以参考下。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表