【问题标题】:Bash Script error: "syntax error: operand expected (error token is "/backup")"Bash 脚本错误:“语法错误:预期操作数(错误标记为“/backup”)”
【发布时间】:2014-05-30 18:47:12
【问题描述】:

我试图制作一个小脚本来检查备份驱动器是否正常及其磁盘使用详细信息,并将其添加到 nagios 作为服务器的主动检查。一旦我写了一个,我有一些未知的错误,我不想更正。

脚本是:

#!/bin/sh

BACKUP_DRIVE='/backup'

if [[ "$( grep $BACKUP_DRIVE /etc/fstab | awk '{print $2}')" -ne "0" ]]; then 

    if [[ "$( df -h | grep $BACKUP_DRIVE | awk '{print $6}')" -ne "0" ]]; then

        if [[ "$( df -h|grep backup|awk '{print $5}' | cut -d "%" -f1)" -ge "95" ]]; then
            echo "CRITICAL: Backup Drive usage exceeded 95%"
        fi
    else
        echo "CRITICAL: Backup Drive not mounted"
    fi
else
    echo "CRITICAL: Backup Drive not added in fstab"
fi

错误是:

# sh /usr/local/src/backupcheck.sh
/usr/local/src/backupcheck.sh: line 6: [[: /backup: syntax error: operand expected (error token is "/backup")
CRITICAL: Backup Drive not added in fstab

它在第一个 if 条件本身时出错,而不是进入内部 if 条件。

在提到变量 $BACKUP_DRIVE 的地方是否需要进行任何更正?请帮忙,因为我是 bash 脚本的初学者。

【问题讨论】:

  • 你为什么使用 /bin/sh 而不是 /bin/bash
  • 重复的grep | awkdf | awk... | awk | cut 和此处的类似用法效率低下、难以阅读且通常很愚蠢。使用 shell 的 read 内置函数来解析这些内容,一次一整行会更简单,更易读。
  • ...另外,将[[ ]]-ge 结合起来也很愚蠢——任何支持[[ ]] 的shell 也将支持(( )),其中更易读的@987654331 @ 可用于数值比较。

标签: linux bash shell


【解决方案1】:

立即问题 - 可能是触发语法错误的问题 - 是:

您使用的是 shebang #!/bin/sh,它不能保证是 bash(即使是,它的行为也不同),但您使用的是特定于 bash 的语法 ([[ ... ]])。

  • 改用#!/bin/bash 作为shebang。
  • 当显式启动带有可执行文件的脚本时,使用bash,而不是sh;在你的例子中:bash /usr/local/src/backupcheck.sh

或者,如果您希望能够使用 sh 显式调用您的脚本,则使用 shebang:

  • 重写您的脚本以仅使用 POSIX 功能,这需要将 [[ ... ]] 替换为 [ ... ](但在一般情况下通常需要进行更多更改)。

只有在您确定sh 在您的系统上实际上是bash 并且您不需要脚本是可移植的(在其他平台上运行)时,您才可以不重写。


但是,条件句也存在问题

看起来您的前 2 个条件仅用于测试封闭的命令是否成功

通常,您根本不需要 [[ ... ]] 进行此类测试,而只需将命令直接与 if 一起使用,可能用 ! 否定,并根据需要使用 >/dev/null 或 @987654337 抑制输出@:

因此,而不是你的命令:

if [[ "$( grep $BACKUP_DRIVE /etc/fstab | awk '{print $2}')" -ne "0" ]]; then 

你应该使用:

if grep $BACKUP_DRIVE /etc/fstab >/dev/null; then 

grep 将表明(至少)找到了与退出代码0 的匹配项,从if 的角度来看,这(也)是成功的。因此,实际上,如果备份驱动器存在if 命令的主体将被执行。

请注意,我已经删除了 awk 命令,因为它 (a) 不是测试所必需的,并且 (b) 实际上 击败 中的测试即使grep 命令失败,它也会导致整体结果为0(成功)。

同样,您的第二个条件应为:

if df -h | grep $BACKUP_DRIVE >/dev/null; then

最后,您的第三个条件是正确的原则上:它从管道捕获标准输出输出并将其与百分比数字进行比较(尽管双引号数字不是必需的,并且可能会造成混淆)。 但是,您不小心硬编码了驱动器名称,因此它应该是:

if [[ "$(df -h | grep $BACKUP_DRIVE | awk '{print $5}' | cut -d "%" -f1)" -ge 95 ]]; then

最后:

  • 您应该通过使用2>/dev/null 重定向echo 命令将错误消息输出到stderr;同样,您应该使用exit 1(或任何其他非零退出代码)在出现错误时退出,以便正确地发出错误信号。
  • 正如@Charles Duffy 在 OP 上的 cmets 中指出的那样,有可能使您的命令更高效。

【讨论】:

  • 这一点是有效的,但[[ ]] 也应该适用于ksh 和其他ksh 派生的shell,例如zsh
  • @TrippKinetics:是的,它也适用于其他 shell,但是 (a) 问题被标记为 bash,并且 (b) 关键是您保证的唯一功能 当你使用 sh 时得到的是 POSIX-only 功能。
  • 那么建议使用[] 而不是[[]],不是吗?
  • 这个问题似乎暗示脚本正在使用sh--# sh /usr/local/src/backupcheck.sh--所以这个答案是正确的。
  • 有两种选择: 1. 使用 bash。 2. 不要使用 bash。对于选项 1,shebang 需要为 #!/bin/bash,并且不应将脚本调用为 sh script-name.sh。对于选项 2,脚本不应使用 [[...]]。任何一个选项都是完全有效的。
【解决方案2】:

在某些时候,它会尝试将 $BACKUP_DRIVE 参数作为数学表达式进行计算。试试这个:

grep $BACKUP_DRIVE /etc/fstab | awk '{print $2}'
if [[ $? -ne 0 ]] ; then
...

【讨论】:

  • 不确定evaluate the $BACKUP_DRIVE parameter as a math expression 是什么意思,但问题中整个表达式"$( grep $BACKUP_DRIVE /etc/fstab | awk '{print $2}')" 中的stdout 输出 确实会被解释为number,由于使用了-ne 运算符。如果意图是先打印命令输出,然后检查命令是否成功,你的重写是有意义的。但是,我怀疑只需要后者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-24
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多