• Shell调试技术总结(二)


    (三)调试钩子,也称调试块,是源于其他的高级程序语言的调试方法,调试钩子实际上就是一个if/then结构的代码块,DEBUG变量控制该代码是否执行,在程序的开发调试阶段将DEBUG变量设置为TRUE,使其输出调试信息,到了程序交付阶段,将DEBUG设置为FALSE,关闭调试钩子,而无需一一删除调试钩子代码。一般调试钩子就是如下代码块:

    1 if [ "$DEBUG" ="true" ]
      2then
     3   echo "Debugginginformation: "
     4   、、、、
     5   fi

    调试钩子中DEBUG是一个全局变量,在开始调试阶段可利用export DEBUG=true 将DEBUG设置为true 。当然上面的代码块在需要许多地方需要调试的情况下非常麻烦,我以我们可以定义一个DEBUG函数使植入调试钩子,这样比较方便。

    1 #!/bin/sh
      2DEBUG()
      3 {
     4   if [ "$DEBUG" ="true" ]
     5   then
     6     $@
     7   fi
      8 }
     9  a=0
     10  b=2
     11 c=100
     12  DEBUG echo "a=$a b=$b c =$c" #第1个调试钩子
     13 while :
     14  do
     15 DEBUG echo "a=$a b=$b c =$c" #第个调试钩子
     16     if (( $a >= 10 ))
     17    then
    18      break
     19    fi
     20   let "a=$a+2"
     21   let "b=$b*2"
     22   let "c=$c-10"
     23  done
    [root@localhost shell]# export DEBUG=true
    [root@localhost shell]# sh debugblock
    a=0 b=2 c =100
    a=0 b=2 c =100
    a=2 b=4 c =90
    a=4 b=8 c =80
    a=6 b=16 c =70
    a=8 b=32 c =60
    a=10 b=64 c =50

    调试钩子是一个if/else结构。当DEBUG变量为true时,执行所有的位置函数。对于上述脚本先用export命令将DEBUG赋值为true,调试钩子启动执行脚本时不断输出a,b,c三个变量的值,达到最终跟踪变量值变化的目的。这种方法与trap命令捕捉DEBUG信号跟踪变量值方法是等价的。

    (四)使用Shell选项。

    使用Shelll选项的调试方法是一种不修改源代码的一种方法。在众多Shell选项中,有三个选项可以用于脚本的调试它们是-n、-x、和-c。

    Shell的调试选项、简写和意义

    选项名称   

    简写     

       意义

    noexec

    n

    读取脚本中的命令,进行语法检查,但不执行命令

    xtrace

    x

    在执行每个命令之前,将每个命令打印到标准输出(stdout)

    c..

    从..中读取命令

         举例

    1 #!/bin/bash
      2 set -x
      3 export PATH
      4 read -p "Please input (Y/N)"
      5 if [ "$yn" == "$Y" ] ||[ "$yn" == "$y"]; then
      6        echo "ok ,continue";
      7 elif [ "$yn" == "$N" ]|| [ "$yn" == "$n"]; then
      8        echo "oh ,interrupt!";
      9 else echo "I don't know what is yourchoise"
     10 fi
    [root@localhostshell]# sh else
    + export PATH
    + read -p 'Pleaseinput (Y/N)'
    Please input(Y/N)y
    + '[' '' == '' ']'
    + echo 'ok,continue'
    ok ,continue


    如果脚本中加入了set –x,那么在set命令之后执行的命令以及加在命令行中的任何参数(包括变量和变量的值)都会显示出来。在一行之前都会加上加号(+),提示它是跟踪输出的标致。在子Shell中执行的shell跟踪命令会在一行前面加上两个加号即“++“。

    set -参数"表示启用某选项,"set +参数"表示关闭某选项。有时候我们并不需要在启动时用"-x"选项来跟踪所有的命令行,这时我们可以在脚本中使用set命令,这时我们可以在脚本中使用set命令。

    set -x    #启动"-x"选项

    要跟踪的程序段

    set +x     #关闭"-x"选项

    set命令同样可以使用上一节中介绍的调试钩子—DEBUG函数来调用,这样可以避免脚本交付使用时删除这些调试语句的麻烦,如以下脚本片段所示:

    DEBUG set -x    #启动"-x"选项

    要跟踪的程序段

    DEBUG set +x    #关闭"-x"选项

    当然-x也有美中不足的地方,这个时候我们可以利用bash Shell提供的三个有用的内部变量,可以利用-x选项提示符的限制。

     Shell用于调试的内部变量及其意义

    变量名称

    意义

    LINENO

    表示Shell脚本的行号

    FUNCNAME

    数组变量,表示整个调用链上所有的函数名

    PS4

    设置-x选项的提示符,默认值是“+”符号

    举例:

    1 #!/bin/sh
      2isroot()
      3 {
     4   if [ "$UID" -ne 0 ]
     5   then
     6       return 1
     7   else
     8       return 0
     9   fi
     10 }
     11echroot()
     12 {
     13isroot
     14   if[ "$?" -ne 0  ]
     15  then
     16    echo "I'm not root"
     17  else
    18   echo "I'm root"
     19  fi
     20 }而且
     21 #对PS4赋值,定制-x选项的提示符
     22export PS4='+{$LINENO: ${FUNCNAME[0]}:${FUNCNAME[1]}}'
     23echroot
    [root@localhost shell]# sh -x nestfun
    + export 'PS4=+{$LINENO:${FUNCNAME[0]}:${FUNCNAME[1]}}'
    + PS4='+{$LINENO:${FUNCNAME[0]}:${FUNCNAME[1]}}'
    +{23: :}echroot
    +{13: echroot:main}isroot
    +{4: isroot:echroot}'[' 0 -ne 0 ']'
    +{8: isroot:echroot}return 0
    +{14: echroot:main}'[' 0 -ne 0 ']'
    +{18: echroot:main}echo 'I'\''m root'
    I'm root


    上面的脚本中定义了两个函数isroot和echroot,该脚本对PS4变量重新赋值PS4='+{$LINENO: ${FUNCNAME[0]}:${FUNCNAME[1]}}',即-x选项的提示符显示脚本的行号、当前函数名以及调用函数名。FUNCNAME[0]表示当前函数名,FUNCNAME[1]表示调用函数名。

    -c选项使Shell解释器从一个字符串而不是一个脚本文件中读取并执行Shell命令,当需要临时测试一小段脚本的执行结果时可使用。

    举例:

    [root@localhost shell]# sh -c 'a=2;b=3;letc=$a+$b;echo "$c"'

    5

    注意:单引号是一个字符串,字符串中包含若干条命令,命令之间用分好隔开。

    总结:针对Shell脚本中所遇到的错误进行了分析,对于逻辑错误进行的全面分析,trap能够代替echo方便的用于脚本输出信息,对撼树进行跟踪等。tee运用管道,便于清晰的看出管道间的数据流向,调试钩子借鉴于高级程序语言,是很好的编程风格,Shell选项不改变脚本内容,-n和-x选项是常见的脚本调试手段,-n主要用于脚本语法错误的调试,-x主要用于脚本的逻辑错误的调试。

  • 相关阅读:
    Vue.js实现的计算器功能完整示例
    vue实现简易计算器
    Vuex中mutations与actions的区别详解
    两个子组件之间的传值
    JS操作元素节点(非常详细)
    js包装类
    Vue Router 的params和query传参的使用和区别(详尽)
    初步了解生命周期
    简单介绍一下Progressive Web App(PWA)
    webpack学习笔记(阮一峰教程demo)
  • 原文地址:https://www.cnblogs.com/linuxer/p/3260334.html
Copyright © 2020-2023  润新知