9. 远离问题

优质
小牛编辑
139浏览
2023-12-01

本节指出一些写 shell 脚本时常犯的错误。先创建一个 trouble.bash 脚本:

#!/bin/bash

number=1

if [ $number = "1" ]; then
    echo "Number equals 1"
else
    echo "Number does not equal 1"
fi

运行以上脚本输出:「"Number equals 1"」。

空变量

修改第三行代码:

number=1

为:

number=

并再次执行脚本,结果:

[me@linuxbox me]$ ./trouble.bash
/trouble.bash: [: =: unary operator expected.
Number does not equal 1

可以看到报错了,你可能以为错误是在第三行,但是根据错误提示,错误发生在第五行,test 命令的 [ 写法这里。 首先,number= 是没有语法错误的,你可以定义一个变量为空。你可以在命令行输入 number= 来验证它是没错的。 那么,第五行有什么错呢?我们尝试用 shell 的运行原理去解释。将第五行的 $number 用其值去代替,先前的脚本得到:

if [ 1 = "1" ]; then

number 为空时得到:

if [ = "1" ]; then

这样就错误了。因为 = 是一个二元运算符,等号两边要有两个选项。而 shell 发现只有一个选项,所有错误提示「期望是一个一元运算符」。 要解决这个错误,我们可以将第五行改成:

if [ "$number" = "1" ]; then

替换后就是:

if [ "" = "1" ]; then

这样就正确了。 这就提示我们在写脚本时需要重视一个重要的东西:考虑变量设为空的情况

缺少引号

修改第六行,去掉后面的引号:

   echo "Number equals 1

并再次运行脚本,得到:

[me@linuxbox me]$ ./trouble.bash
./trouble.bash: line 8: unexpected EOF while looking for matching "
./trouble.bash: line 10 syntax error: unexpected end of file

这里就引出了另一个常见的错误,shell 会通过首尾双引号来获取一个字符串,这里从第六行开始直到脚本运行结束都没有找到末尾的双引号,所以报错了。 这种错误在很长的脚本里找起来很痛苦。这就是为什么你需要边写脚本边测试,写一点测一点。当然,使用带有语法高亮的文本编辑器也可以轻松找出错误。

隔离问题

定位问题的几个方法: 通过注释来隔离代码块。例如:

#!/bin/bash

number=1

if [ $number = "1" ]; then
    echo "Number equals 1
#else
# echo "Number does not equal 1"
fi

通过注释掉 else 部分的代码块,运行脚本,发现仍然报错,就可以知道错误(双引号未关闭)不在 else 代码块中

使用 echo 命令来验证你的猜测。你可以通过 echo 命令来输出你的脚本运行预期。来证明脚本运行到了那一块。类似之前说过的 存根 代码。

监控你的脚本执行

如果你要 bash 向你显示其正在进行的工作(执行了什么代码),你可以在第一行使用 -x 参数:

#!/bin/bash -x

此时运行你的脚本,bash 会在执行代码的同时显示正在执行的每一行代码。这个技术叫做 追踪。 同时,你也可以用 set 命令来开启和关闭追踪。例如:

#!/bin/bash

number=1

set -x
if [ $number = "1" ]; then
    echo "Number equals 1"
else
    echo "Number does not equal 1"
fi
set +x