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

#!/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" ]]

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

单方括号和双方括号 括号

?


当前回答

试试这个:

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

其他回答

首先,您不需要将变量名大写。通常,全大写的变量名应该保留给要导出到环境中的变量名,以便其他可执行文件使用。

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

最后,-a尝试没有成功,因为-a是test /[…];它在括号里。

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

if foo; then
  echo true
else
  echo false
fi

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

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

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

这也适用于&&和||操作符:两侧的东西都只是命令。序列foo && bar运行命令foo,然后,只有当该命令以状态0退出时,它才运行bar。(所以它实际上是if foo的缩写;然后酒吧;fi)。类似地,foo || bar运行foo,然后,如果该命令以非零状态退出,则运行bar。(所以它实际上是if !foo;然后酒吧;fi)。

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

However, because you so often want to perform comparisons and other operations that would be Boolean expressions in regular non-shell programming languages, there is a special command that you can run to do those. It used to be a separate binary, but these days it's built into the shell. Its real name is test, but it can also be invoked as [, in which case it will complain if its last argument is not a matching ]. The arguments inside the brackets or after test constitute an expression to evaluate, and the test command exits with status 0 if the expression is true, and nonzero if it's false. So it turns the sort of thing that is normally what an if statement expects in other programming languages into a command that you can run, effectively translating it for the shell.

The appearance of [...] can be misleading; it really is parsed just as if it were spelled test, with the same command-line processing as any regular shell command. That means that if you try to compare things using < or > they'll be interpreted as redirects. Try to use ( and ) for grouping and you'll be creating a subshell (and probably generating a syntax error by doing so in the middle of a command's arguments). Try to combine expressions with && and || inside the brackets and you'll be terminating the command early and again triggering a syntax error.

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

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

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

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

The Korn Shell introduced a new version of the test command spelled with two brackets instead of one, between which lies a special syntactic environment that is not parsed the same as a regular command. Between the double brackets you can safely use unquoted parameter expansions, unquoted parentheses for grouping, < and > for string comparison (stick to -lt and -gt to compare numerically), and && and || as Boolean operators. [[...]] also adds the ability to match against glob patterns ([[ foo == f* ]]) and regular expressions ([[ foo =~ ^f ]]).

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

注意,算术替换表达式$((…))是POSIX的一部分,它具有与[[…]]非常相似的规则。主要的区别是相等和不相等测试(这里产生一个数值,0表示false, 1表示true)是按数字而不是按字符串完成的:[[10 < 2]]是真,但是$((10 < 2))产生0。

Here again, modern shells have expanded upon the POSIX spec to add a standalone $-less version of ((...)) that functions as basically an autoquoted let command, which you can use in if expressions if you're dealing with numbers. So your first condition could also be written (( status != 200 )). Again, outside of having to stick to POSIX, that's down to personal preference. I like using ((...)) whenever I'm dealing with numbers, but others prefer to just stick to [ or [[ and use the numeric operators like -lt.

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

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

引用:

"-a"运算符也不能用: 如果[状态- ne 200美元]——[[" $字符串" ! = " $价值”]]

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

要用作条件评估过程,可以使用测试程序(man test),该程序允许您评估简单的条件,如文件存在性测试、字符串测试以及使用例如-a和-o逻辑运算符参数的组合。

有些人会找到这样的行:if test -f filename;然后是foo bar;Fi等烦人的,还有一个程序叫[,实际上是一个符号链接到测试程序。当test被作为[调用时,它期望找到]作为一个额外的终止参数——这个语法是由测试程序组成的,bash根本不知道它。

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

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

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


实际上,它不一定是一个单独的进程,也可以是一个作业,是由|、&&、||等连接起来的多个进程的组合。

不是字面上的这些参数——shell会对$STATUS等做变量替换。

试试这个:

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

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

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

or

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

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

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

试试这个:

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

or

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