网站搜索

如何使用 Shell 跟踪来跟踪 Shell 脚本中命令的执行


在shell脚本调试系列的这篇文章中,我们将解释第三种shell脚本调试模式,即shell跟踪,并通过一些示例来演示它的工作原理以及如何使用它。

本系列的前一部分清楚地阐明了另外两种 shell 脚本调试模式:详细模式语法检查模式,并提供了如何启用 shell 的易于理解的示例在这些模式下进行脚本调试。

  1. 如何在 Linux 中启用 Shell 脚本调试模式 - 第 1 部分
  2. 如何在 Shell 脚本中执行语法检查调试模式 – 第 2 部分

Shell 跟踪简单来说就是跟踪 shell 脚本中命令的执行情况。要打开 shell 跟踪,请使用 -x 调试选项。

这指示 shell 在执行时在终端上显示所有命令及其参数。

我们将使用下面的 sys_info.sh shell 脚本,它会简要打印您的系统日期和时间、登录用户数以及系统正常运行时间。但是,它包含我们需要查找并更正的语法错误。

#!/bin/bash
#script to print brief system info

ROOT_ID="0"

DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`

check_root(){
    if [ "$UID" -ne "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;    
}

print_sys_info(){
    echo "System Time    : $DATE"
    echo "Number of users: $NO_USERS"
    echo "System Uptime  : $UPTIME
}

check_root
print_sys_info

exit 0

保存文件并使脚本可执行。该脚本只能由 root 运行,因此使用 sudo 命令运行它,如下所示:

chmod +x sys_info.sh
sudo bash -x sys_info.sh

从上面的输出中,我们可以观察到,首先执行命令,然后将其输出替换为变量的值。

例如,首先执行日期,并将其输出替换为变量DATE的值。

我们可以进行语法检查,只显示语法错误,如下所示:

sudo bash -n sys_info.sh 

如果我们仔细观察 shell 脚本,我们会发现 if 语句 缺少结束 fi 词。因此,让我们添加它,新脚本现在应如下所示:

#!/bin/bash
#script to print brief system info

ROOT_ID="0"

DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`

check_root(){
    if [ "$UID" -ne "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;
   fi    
}

print_sys_info(){
    echo "System Time    : $DATE"
    echo "Number of users: $NO_USERS"
    echo "System Uptime  : $UPTIME
}

check_root
print_sys_info

exit 0

再次保存文件并以 root 身份调用它并进行一些语法检查:

sudo bash -n sys_info.sh

上面的语法检查操作的结果仍然表明我们的脚本中的第 21 行还存在一个错误。因此,我们仍然需要进行一些语法修正。

如果我们再次分析地查看该脚本,第 21 行上的错误是由于 中最后一个 echo 命令中缺少结束双引号 ( ”) 造成的。 >print_sys_info 函数。

我们将在 echo 命令中添加右双引号并保存文件。更改后的脚本如下:

#!/bin/bash
#script to print brief system info

ROOT_ID="0"

DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`

check_root(){
    if [ "$UID" -ne "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;
    fi
}

print_sys_info(){
    echo "System Time    : $DATE"
    echo "Number of users: $NO_USERS"
    echo "System Uptime  : $UPTIME"
}

check_root
print_sys_info

exit 0

现在再次检查脚本的语法。

sudo bash -n sys_info.sh

上面的命令不会产生任何输出,因为我们的脚本现在在语法上是正确的。我们还可以再次跟踪脚本的执行情况,它应该运行良好:

sudo bash -x sys_info.sh

现在运行脚本。

sudo ./sys_info.sh

Shell 脚本执行跟踪的重要性

Shell 脚本跟踪可以帮助我们识别语法错误,更重要的是识别逻辑错误。以sys_info.sh shell脚本中的check_root函数为例,该函数的目的是确定用户是否为root,因为该脚本只允许执行由超级用户。

check_root(){
    if [ "$UID" -ne "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;
    fi
}

这里的魔力是由 if 语句 表达式 [ "$UID" -ne "$ROOT_ID" ] 控制的,一旦我们不使用合适的数字运算符 ( -ne 在这种情况下,这意味着不等于),我们最终会遇到一个可能的逻辑错误。

假设我们使用 -eq (意味着等于),这将允许任何系统用户以及 root 用户运行该脚本,因此会出现逻辑错误。

check_root(){
    if [ "$UID" -eq "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;
    fi
}

注意:正如我们在本系列开始时所讨论的,set shell 内置命令可以激活 shell 脚本特定部分的调试。

因此,下面的行将帮助我们通过跟踪函数的执行来找到函数中的逻辑错误:

脚本有逻辑错误:

#!/bin/bash
#script to print brief system info

ROOT_ID="0"

DATE=`date`
NO_USERS=`who | wc -l`
UPTIME=`uptime`

check_root(){
    if [ "$UID" -eq "$ROOT_ID" ]; then
        echo "You are not allowed to execute this program!"
        exit 1;
    fi
}

print_sys_info(){
    echo "System Time    : $DATE"
    echo "Number of users: $NO_USERS"
    echo "System Uptime  : $UPTIME"
}

#turning on and off debugging of check_root function
set -x ; check_root;  set +x ;
print_sys_info

exit 0

保存文件并调用脚本,我们可以看到常规系统用户可以在没有 sudo 的情况下运行脚本,如下面的输出所示。这是因为 USER_ID 的值为 100,它不等于根 ROOT_ID0)。

./sys_info.sh

好了,现在就是这样,我们已经结束了 shell 脚本调试系列,下面的回复表单可用于解决有关本指南或整个 3 部分系列的任何问题或反馈给我们。