Bash脚本教程之脚本除错

举报
孙叫兽 发表于 2021/03/28 03:18:16 2021/03/28
【摘要】 目录   常见错误 bash的-x参数 环境变量 LINENO FUNCNAME BASH_SOURCE BASH_LINENO 常见错误 编写 Shell 脚本的时候,一定要考虑到命令失败的情况,否则很容易出错。 #! /bin/bash dir_name=/path/not/exist cd $dir_namerm * 上面脚本中,如果目录$di...

目录

 

常见错误

bash的-x参数

环境变量

LINENO

FUNCNAME

BASH_SOURCE

BASH_LINENO


常见错误

编写 Shell 脚本的时候,一定要考虑到命令失败的情况,否则很容易出错。


  
  1. #! /bin/bash
  2. dir_name=/path/not/exist
  3. cd $dir_name
  4. rm *

上面脚本中,如果目录$dir_name不存在,cd $dir_name命令就会执行失败。这时,就不会改变当前目录,脚本会继续执行下去,导致rm *命令删光当前目录的文件。

如果改成下面的样子,也会有问题。

cd $dir_name && rm *
 

上面脚本中,只有cd $dir_name执行成功,才会执行rm *。但是,如果变量$dir_name为空,cd就会进入用户主目录,从而删光用户主目录的文件。

下面的写法才是正确的。

[[ -d $dir_name ]] && cd $dir_name && rm *
 

上面代码中,先判断目录$dir_name是否存在,然后才执行其他操作。

如果不放心删除什么文件,可以先打印出来看一下。

[[ -d $dir_name ]] && cd $dir_name && echo rm *
 

上面命令中,echo rm *不会删除文件,只会打印出来要删除的文件。

 

bash-x参数

bash-x参数可以在执行每一行命令之前,打印该命令。这样就不用自己输出执行的命令,一旦出错,比较容易追查。

下面是一个脚本script.sh


  
  1. # script.sh
  2. echo hello world

加上-x参数,执行每条命令之前,都会显示该命令。


  
  1. $ bash -x script.sh
  2. + echo hello world
  3. hello world

上面例子中,行首为+的行,显示该行是所要执行的命令,下一行才是该命令的执行结果。

下面再看一个-x写在脚本内部的例子。


  
  1. #! /bin/bash -x
  2. # trouble: script to demonstrate common errors
  3. number=1
  4. if [ $number = 1 ]; then
  5. echo "Number is equal to 1."
  6. else
  7. echo "Number is not equal to 1."
  8. fi

上面的脚本执行之后,会输出每一行命令。


  
  1. $ trouble
  2. + number=1
  3. + '[' 1 = 1 ']'
  4. + echo 'Number is equal to 1.'
  5. Number is equal to 1.

输出的命令之前的+号,是由系统变量PS4决定,可以修改这个变量。


  
  1. $ export PS4='$LINENO + '
  2. $ trouble
  3. 5 + number=1
  4. 7 + '[' 1 = 1 ']'
  5. 8 + echo 'Number is equal to 1.'
  6. Number is equal to 1.

另外,set命令也可以设置 Shell 的行为参数,有利于脚本除错,详见《set 命令》一章。

 

环境变量

有一些环境变量常用于除错。

LINENO

变量LINENO返回它在脚本里面的行号。


  
  1. #!/bin/bash
  2. echo "This is line $LINENO"

执行上面的脚本test.sh$LINENO会返回3


  
  1. $ ./test.sh
  2. This is line 3

 

FUNCNAME

变量FUNCNAME返回一个数组,内容是当前的函数调用堆栈。该数组的0号成员是当前调用的函数,1号成员是调用当前函数的函数,以此类推。


  
  1. #!/bin/bash
  2. function func1()
  3. {
  4. echo "func1: FUNCNAME0 is ${FUNCNAME[0]}"
  5. echo "func1: FUNCNAME1 is ${FUNCNAME[1]}"
  6. echo "func1: FUNCNAME2 is ${FUNCNAME[2]}"
  7. func2
  8. }
  9. function func2()
  10. {
  11. echo "func2: FUNCNAME0 is ${FUNCNAME[0]}"
  12. echo "func2: FUNCNAME1 is ${FUNCNAME[1]}"
  13. echo "func2: FUNCNAME2 is ${FUNCNAME[2]}"
  14. }
  15. func1

执行上面的脚本test.sh,结果如下。


  
  1. $ ./test.sh
  2. func1: FUNCNAME0 is func1
  3. func1: FUNCNAME1 is main
  4. func1: FUNCNAME2 is
  5. func2: FUNCNAME0 is func2
  6. func2: FUNCNAME1 is func1
  7. func2: FUNCNAME2 is main

上面例子中,执行func1时,变量FUNCNAME的0号成员是func1,1号成员是调用func1的主脚本main。执行func2时,变量FUNCNAME的0号成员是func2,1号成员是调用func2func1

 

BASH_SOURCE

变量BASH_SOURCE返回一个数组,内容是当前的脚本调用堆栈。该数组的0号成员是当前执行的脚本,1号成员是调用当前脚本的脚本,以此类推,跟变量FUNCNAME是一一对应关系。

下面有两个子脚本lib1.shlib2.sh


  
  1. # lib1.sh
  2. function func1()
  3. {
  4. echo "func1: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
  5. echo "func1: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
  6. echo "func1: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
  7. func2
  8. }

  
  1. # lib2.sh
  2. function func2()
  3. {
  4. echo "func2: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
  5. echo "func2: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
  6. echo "func2: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
  7. }

然后,主脚本main.sh调用上面两个子脚本。


  
  1. #!/bin/bash
  2. # main.sh
  3. source lib1.sh
  4. source lib2.sh
  5. func1

执行主脚本main.sh,会得到下面的结果。


  
  1. $ ./main.sh
  2. func1: BASH_SOURCE0 is lib1.sh
  3. func1: BASH_SOURCE1 is ./main.sh
  4. func1: BASH_SOURCE2 is
  5. func2: BASH_SOURCE0 is lib2.sh
  6. func2: BASH_SOURCE1 is lib1.sh
  7. func2: BASH_SOURCE2 is ./main.sh

上面例子中,执行函数func1时,变量BASH_SOURCE的0号成员是func1所在的脚本lib1.sh,1号成员是主脚本main.sh;执行函数func2时,变量BASH_SOURCE的0号成员是func2所在的脚本lib2.sh,1号成员是调用func2的脚本lib1.sh

 

BASH_LINENO

变量BASH_LINENO返回一个数组,内容是每一轮调用对应的行号。${BASH_LINENO[$i]}${FUNCNAME[$i]}是一一对应关系,表示${FUNCNAME[$i]}在调用它的脚本文件${BASH_SOURCE[$i+1]}里面的行号。

下面有两个子脚本lib1.shlib2.sh


  
  1. # lib1.sh
  2. function func1()
  3. {
  4. echo "func1: BASH_LINENO is ${BASH_LINENO[0]}"
  5. echo "func1: FUNCNAME is ${FUNCNAME[0]}"
  6. echo "func1: BASH_SOURCE is ${BASH_SOURCE[1]}"
  7. func2
  8. }

  
  1. # lib2.sh
  2. function func2()
  3. {
  4. echo "func2: BASH_LINENO is ${BASH_LINENO[0]}"
  5. echo "func2: FUNCNAME is ${FUNCNAME[0]}"
  6. echo "func2: BASH_SOURCE is ${BASH_SOURCE[1]}"
  7. }

然后,主脚本main.sh调用上面两个子脚本。


  
  1. #!/bin/bash
  2. # main.sh
  3. source lib1.sh
  4. source lib2.sh
  5. func1

执行主脚本main.sh,会得到下面的结果。


  
  1. $ ./main.sh
  2. func1: BASH_LINENO is 7
  3. func1: FUNCNAME is func1
  4. func1: BASH_SOURCE is main.sh
  5. func2: BASH_LINENO is 8
  6. func2: FUNCNAME is func2
  7. func2: BASH_SOURCE is lib1.sh

上面例子中,函数func1是在main.sh的第7行调用,函数func2是在lib1.sh的第8行调用的。

 

 

下一节Bash脚本教程之mktemp 命令,trap 命令

 

文章来源: sunmenglei.blog.csdn.net,作者:孙叫兽,版权归原作者所有,如需转载,请联系作者。

原文链接:sunmenglei.blog.csdn.net/article/details/107238570

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。