一个“这样;“if”的运算符;Bash语句

我试图创建一个简单的Bash脚本,以检查网站是否关闭,由于某种原因"和quot;操作符不工作:

#!/usr/bin/env bash


WEBSITE=domain.example
SUBJECT="$WEBSITE DOWN!"
EMAILID="an@email.example"
STATUS=$(curl -sI $WEBSITE | awk '/HTTP\/1.1/ { print $2 }')
STRING=$(curl -s $WEBSITE | grep -o "string_to_search")
VALUE="string_to_search"


if [ $STATUS -ne 200 ] && [[ "$STRING" != "$VALUE" ]]; then
echo "Website: $WEBSITE is down, status code: '$STATUS' - $(date)" | mail -s "$SUBJECT" $EMAILID
fi

“-a"操作符也不工作:

if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]

你还可以建议什么时候使用:

  • 单方括号和双方括号
  • 括号

?

670217 次浏览

你所拥有的应该可以工作,除非${STATUS}为空。可能更好的做法是:

if ! [ "${STATUS}" -eq 200 ] 2> /dev/null && [ "${STRING}" != "${VALUE}" ]; then

if [ "${STATUS}" != 200 ] && [ "${STRING}" != "${VALUE}" ]; then

很难说,因为你还没有告诉我们你的剧本到底出了什么问题。

个人意见:永远不要使用[[。它抑制了重要的错误消息,并且不能移植到不同的shell。

试试这个:

if [ $STATUS -ne 200 -a "$STRING" != "$VALUE" ]; then

引用:

“-a"操作符也不工作:

if [$STATUS -ne 200] -a [["$STRING"! =“VALUE"美元;]]

更详细的解释是:[]不是Bash保留词。if关键字引入了要由进程1计算的条件。如果进程的退出状态为0,则条件为真,否则为假)。

作为条件评估过程,有test程序(man test),这是一个让你评估简单条件的程序,如文件存在性测试,字符串测试,以及使用例如-a-o逻辑运算符参数的组合。

因为有些人觉得像if test -f filename; then foo bar; fi这样的行很烦人,还有一个名为[的程序,它实际上是test程序的符号链接。当test被调用为[时,它期望找到]作为一个额外的终止参数——这个语法是由test程序组成的,而bash根本不知道它。

因此,if test -f filename基本上与if [ -f filename ]相同(就生成的进程而言)。在这两种情况下,test程序将被启动,并且两个进程的行为应该相同。

这是你的错误:if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]将解析为if +一些流程调用。换句话说,[程序将使用参数$STATUS-ne200]-a[["$STRING"if0、if1、if2、if6运行。现在注意,在传递给[的参数列表中间的某个地方有一个],并且在列表的末尾缺少一个]。这就是程序会报错的原因。

还要注意,与[程序相比,[[确实是一个特殊的,“保留的”;它是一个bash扩展,不是在POSIX sh中标准化的)。但只有当它是命令中的第一个单词时,它才会被识别为特殊单词。在你的例子中,[[出现在命令的后面,因此将被解析为普通参数,字面上是字符串"[[",并作为参数列表的一部分传递给[程序的调用。


1实际上它不必是一个单独的进程,但可以是一个工作,一个由|&&||等连接的进程的组合。

2不是字面上的这些参数——shell将对$STATUS等进行变量替换。

试试这个:

if [ "${STATUS}" -ne 100 -a "${STRING}" = "${VALUE}" ]

if [ "${STATUS}" -ne 100 ] && [ "${STRING}" = "${VALUE}" ]

首先,您不需要将变量名大写。一般来说,全大写的变量名应该保留给那些被exported到环境中的变量名,以便其他可执行文件使用。

其次,你使用&&所拥有的应该可以工作,但在面对可能为空或非数值的状态变量时不是很有弹性。

最后,-a尝试没有成功,因为-atest / [];它是内部括号。

从根本上说,关于shell中的条件,最重要的事情是它们只是命令. conf。这个if表达式:

if foo; then
echo true
else
echo false
fi

做的事情和这个完全一样:

foo
if [ $? -eq 0 ]; then
echo true
else
echo false
fi

所以if(或whileuntil)后面的东西可以是任何命令。

这也适用于&&||操作符:任何一边的东西都只是命令。序列foo && bar运行命令foo,然后,只有当该命令以状态0退出时,它才运行bar。(所以它实际上是if foo; then bar; fi的简称。)类似地,foo || bar运行foo,然后,如果该命令以||0状态退出,则运行bar。(所以它实际上是if ! foo; then bar; fi的简称。)

这是很多刚开始编程shell的人忽略的主要问题。shell本身没有表达式,只有要运行的命令。

但是,由于您经常希望执行比较和其他在常规非shell编程语言中使用布尔表达式的操作,因此可以运行一个特殊的命令来执行这些操作。它曾经是一个单独的二进制文件,但现在它被内置到shell中。它的真实名称是test,但它也可以被调用为[,在这种情况下,如果它的最后一个参数不是匹配的],它将报错。括号内或test之后的参数构成要求值的表达式,如果表达式为真,test命令退出时状态为0,如果为假,则状态为非零。因此,它将if语句在其他编程语言中通常所期望的东西转换为您可以运行的命令,有效地将其转换为shell。

[的出现…]可能会误导;它确实被解析,就像它被拼写为test一样,使用与任何常规shell命令相同的命令行处理。这意味着如果你尝试使用<>比较事物,它们将被解释为重定向。尝试使用()进行分组,您将创建一个子shell(并可能通过在命令的参数中间这样做而生成语法错误)。尝试在括号内组合带有&&||的表达式,您将提前终止命令并再次触发语法错误。

你仍然可以使用括号中的&&|| ;此时,您只需运行test命令的多个实例,而不是一个实例。但你也可以使用-a-o,只要它们是括号中的内部。所以这两个管道产生了相同的结果:

[ "$status" -ne 200 -a "$string" != "$value" ]


[ "$status" -ne 200 ] && [ "$string" != "value" ]

不同之处在于,第一个命令全部由test命令的一个实例检查,而第二个命令运行其中两个。

Korn Shell引入了用两个括号而不是一个括号拼写的test命令的新版本,其中包含一个特殊的语法环境,它与常规命令的解析方式不同。在双括号之间,可以安全地使用不带引号的参数展开,不带引号的括号进行分组,<>用于字符串比较(坚持使用-lt-gt进行数值比较),&&||作为布尔操作符。[[……]]还增加了匹配glob模式([[ foo == f* ]])和正则表达式(>0)的能力。

现代shell(如Bash和Zsh)从Ksh继承了这个构造,但它不是POSIX规范的一部分。如果您所处的环境必须严格遵循POSIX,那么请远离它;否则,基本上就取决于个人喜好了。

注意算术替换表达式$(()) 是POSIX的一部分,它与[[…主要的区别是,等式和不等式测试(这里产生一个数值,0表示假,1表示真)是以数字而不是字符串方式完成的:[[ 10 < 2 ]]为真,但$(( 10 < 2 ))产生0。

在这里,现代shell再次扩展了POSIX规范,添加了一个独立的$-less版本的(())的功能基本上是一个自动引用的let命令,如果你处理的是数字,可以在if表达式中使用它。所以第一个条件也可以写成(( status != 200 ))。同样,除了必须坚持POSIX之外,这取决于个人偏好。我喜欢使用((…当我处理数字时,)),但其他人更喜欢坚持使用[[[,并使用像((0这样的数字操作符。

因此,无论如何,以下是我重写代码的方法:

website=domain.example
subject="$website DOWN!"
email=an@email.example
value="string_to_search"
status=$(curl -sI "$website" | awk '/HTTP\/1.1/ { print $2 }')
string=$(curl -s "$website" | grep -o "$value")


if (( status != 200 )) && [[ $string != $value ]]; then
mail -s "$subject" "$email" <<<"Website: $website is down, status code: '$status' - $(date)"
fi