脚本错误定位
脚本错误定位
常见错误
编写 Shell 脚本的时候,一定要考虑到命令失败的情况,否则很容易出错。
#! /bin/bash
dir_name=/path/not/exist
cd $dir_name
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
。
# script.sh
echo hello world
加上 -x
参数,执行每条命令之前,都会显示该命令。
$ bash -x script.sh
+ echo hello world
hello world
上面例子中,行首为 +
的行,显示该行是所要执行的命令,下一行才是该命令的执行结果。
其实这个就是 set -x
的效果,这命令我们在《set 命令和 shopt 命令》的 set -x
小节学习过
环境变量
有一些环境变量常用于错位 u 定位,我们可以用这些变量来手动记录错误日志
-
LINENO:变量
LINENO
返回使用这个变量的命令在脚本里面的行号。 -
FUNCNAME:变量
FUNCNAME
返回一个数组,内容是当前的函数调用堆栈。该数组的 0 号成员是当前调用的函数,1 号成员是调用当前函数的函数,以此类推。最后一位是最开始的调用发起者,一般都是函数或者脚本名。 -
BASH_SOURCE:变量
BASH_SOURCE
返回一个数组,内容是当前的脚本调用堆栈。该数组的 0 号成员是当前执行的脚本,1 号成员是调用当前脚本的脚本,以此类推,跟变量FUNCNAME
是一一对应关系。最后一位是最开始的调用发起者,一般都是函数或者脚本名。 -
BASH_LINENO:变量
BASH_LINENO
返回一个数组,内容是每一轮调用对应的行号。${BASH_LINENO[$i]}
跟${FUNCNAME[$i]}
是一一对应关系,表示${FUNCNAME[$i]}
在调用它的脚本文件${BASH_SOURCE[$i+1]}
里面的行号。最后一个元素,即,第一个调用所在的行号,一般都是 0。
简单实践如下:
创建脚本 shell2.sh
#!/usr/bin/env bash
function shell_fun2(){
echo ===========================
echo "${LINENO}"
echo "${BASH_SOURCE[@]}"
echo "${BASH_LINENO[@]}"
echo "${FUNCNAME[@]}"
}
shell1.sh
#!/usr/bin/env bash
function shell_fun1(){
echo ===========================
echo "${LINENO}"
echo "${BASH_SOURCE[@]}"
echo "${BASH_LINENO[@]}"
echo "${FUNCNAME[@]}"
shell_fun2
}
main.sh
#!/usr/bin/env bash
# 引用其他脚本
. ./shell1.sh
. ./shell2.sh
shell_fun1
赋权后执行 main.sh
$ ./main.sh
===========================
5
./shell1.sh ./main.sh
6 0
shell_fun1 main
===========================
5
./shell2.sh ./shell1.sh ./main.sh
9 6 0
shell_fun2 shell_fun1 main
直接看 shell_fun2
的输出
BASH_SOURCE:main.sh
调用 shell1.sh
,shell1.sh
调用 shell2.sh
。
BASH_LINENO:在交互式 shell 中调用 main.sh
为第 0 行(约定),main.sh
调用 shell1.sh
,是在第 6 行,注释不算行,shell1.sh
调用 shell2.sh
,是在第 9 行。
FUNCNAME:main 脚本调用 shell_fun1,shell_fun1 调用 shell_fun2。