• BASH 的调试技巧


    平时在写 BASH 脚本时,总是会碰到让人抓狂的 BUG。和 C/C++ 这么丰富的调试工具相比,BASH 又有什么调试手段呢?

    1 echo/print (普通技)

    打印一些变量,或者提示信息。这应该是一个通用的方法了。在 BASH 里,我们可以简单的用 echo,或者 print 来输出一些 log,或者加一些 loglevel 来过滤一些 log。这里贴一下我平常用的函数:

    1. _loglevel=2
    2.  
    3. DIE() {
    4. echo "Critical: $1" >&2
    5. exit 1
    6. }
    7.  
    8. INFO() {
    9. [ $_loglevel -ge 2 ] && echo "INFO: $1" >&2
    10. }
    11.  
    12. ERROR() {
    13. [ $_loglevel -ge 1 ] && echo "ERROR: $1" >&2
    14. }

    这里的实现只是简单的加了一个 loglevel,其实可以把 log 输出到一个文件中,或者给 log 加上颜色。比如:

    1. # add color
    2. [ $_loglevel -ge 1 ] && echo -e "33[31m ERROR:33[0m $1" >&2
    3. # redirect to file
    4. [ $_loglevel -ge 1 ] && echo "ERROR: $1" > /var/log/xxx_log.$BASHPID
    5.  

    2 set -x (稀有技)

    -x(xtrace) 选项会导致 BASH 在执行命令之前,先把要执行的命令打印出来。这个选项对调试一些命令错误很有帮助。

    有的时候,由于传进来的参数带有一些特殊字符,导致 BASH 解析时不是按照我们预想的进行。这个时候,把 -x 打开,就能在命令执行前,把扩展后的命令打印出来。比如基于前面写的函数:

    1. set -x
    2. INFO "this is a info log"
    3. ERROR "this is a error log"
    4. set +x

    然后就可以看到如下输出:

    1. + INFO 'this is a info log'
    2. + '[' 2 -ge 2 ']'
    3. + echo -e '33[32m INFO:33[0m this is a info log'
    4. INFO: this is a info log
    5. + ERROR 'this is a error log'
    6. + '[' 2 -ge 1 ']'
    7. + echo -e '33[33m ERR:33[0m this is a error log'
    8. ERR: this is a error log
    9. + set +x

    如果想全程打开 xtrace,可以在执行脚本的时候加 -x 参数。

    3 trap/bashdb (史诗技)

    为了方便调试,BASH 也提供了陷阱机制。这跟之前介绍的两种方法高级不少。我们可以利用 trap 这个内置命令来指定各个 sigspec 应该执行的命令。trap 的具体用法如下:

    1. trap [-lp] [[arg] sigspec ...]

    sigspec 包括 <signal.h> 中定义的各个 signal, EXIT,ERR,RETURN 和 DEBUG。

    各个 signal 这里就不介绍了。EXIT 会在 shell 退出时执行指定的命令。若当前 shell 中有命令执行返回非零值,则会执行与 ERR 相关联的命令。而 RETURN 是针对 source 和 . ,每次执行都会触发 RETURN 陷阱。若绑定一个命令到 DEBUG,则会在每一个命令执行之前,都会先执行 DEBUG 这个 trap。这里要注意的是,ERR 和 DEBUG 只在当前 shell 有效。若想函数和子 shell 自动继承这些 trap,则可以设置 -T(DEBUG/RETURN) 和 -E(ERR)。

    比如,下面的脚本会在退出时,执行echo:

    1. #!/bin/bash
    2.  
    3. trap "echo this is a exit echo" EXIT
    4.  
    5. echo "this is a normal echo"

    或者,让脚本中命令出错时,把相应的命令打印出来:

    1. #!/bin/bash
    2.  
    3. trap 'echo $BASH_COMMAND return err' ERR
    4.  
    5. echo this is a normal test
    6. UnknownCmd

    这个脚本的输出如下:

    1. this is a normal test
    2. tt.sh: line 6: UnknownCmd: command not found
    3. UnknownCmd return err

    亦或者,让脚本的命令单步执行:

    1. #!/bin/bash
    2.  
    3. trap '(read -p "[$0 : $LINENO] $BASH_COMMAND ?")' DEBUG
    4.  
    5. echo this is a test
    6.  
    7. i=0
    8. while [ true ]
    9. do
    10. echo $i
    11. ((i++))
    12. done

    其输出如下:

    1. [tt.sh : 5] echo this is a test ?
    2. this is a test
    3. [tt.sh : 7] i=0 ?
    4. [tt.sh : 8] [ true ] ?
    5. [tt.sh : 10] echo $i ?
    6. 0
    7. [tt.sh : 11] ((i++)) ?
    8. [tt.sh : 8] [ true ] ?
    9. [tt.sh : 10] echo $i ?
    10. 1
    11. [tt.sh : 11] ((i++)) ?
    12. [tt.sh : 8] [ true ] ?
    13. [tt.sh : 10] echo $i ?
    14. 2
    15. [tt.sh : 11] ((i++)) ?

    是不是有点意思了?其实有一个 bashdb 的开源项目,也是利用 trap 机制,模拟 gdb 做了一个 bash 脚本的调试器。它本身也是一个 bash 脚本。在加载要调试的脚本后,可以用和 gdb 类似的命令,甚至缩写也是一样的,大家可以尝试一下:)

    (上个月沉迷于 Diablo3,最后发现自己脸不行,悴!还是回来写点东西吧!)

  • 相关阅读:
    2021.3.3
    2021.3.2
    2021.3.1
    2021.2.28(每周总结)
    2021.2.27
    2021.2.26
    2021.2.25
    2021.2.23
    Redis系统学习之五大基本数据类型(List(列表))
    Redis系统学习之五大基本数据类型(String(字符串))
  • 原文地址:https://www.cnblogs.com/wish123/p/5525667.html
Copyright © 2020-2023  润新知