我正在尝试创建一个简单的 Bash 脚本来检查网站是否关闭并且由于某种原因"and"运算符不起作用:
#!/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" ]]
您能否也告知何时使用:
- 单方括号和双方括号
- 括号
?
你所拥有的应该有效,除非${STATUS}
是空的。 最好这样做:
if ! [ "${STATUS}" -eq 200 ] 2> /dev/null && [ "${STRING}" != "${VALUE}" ]; then
或
if [ "${STATUS}" != 200 ] && [ "${STRING}" != "${VALUE}" ]; then
很难说,因为你还没有向我们展示你的脚本到底出了什么问题。
个人意见:切勿使用[[
。 它禁止显示重要的错误消息,并且不能移植到不同的 shell。
试试这个:
if [ "${STATUS}" -ne 100 -a "${STRING}" = "${VALUE}" ]
或
if [ "${STATUS}" -ne 100 ] && [ "${STRING}" = "${VALUE}" ]
试试这个:
if [ $STATUS -ne 200 -a "$STRING" != "$VALUE" ]; then
引用:
"-a"运算符也不起作用:
如果 [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]
对于更详细的解释:[
和]
不是 Bash 保留词。if
关键字引入了要由进程1评估的条件。如果进程的退出状态为0
,则条件为 true,否则为 false)。
为了用作条件评估过程,有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
、-ne
、200
、]
、-a
、[[
、"$STRING"
、!=
、"$VALUE"
、]]
2运行。现在请注意,在传递给[
的参数列表中间的某个地方有一个]
,并且在列表的末尾也缺少一个]
。这就是程序会抱怨的原因。
另请注意,与[
程序相反,[[
确实是 shell 语法中的一个特殊的"保留"词(它是一个bash
扩展,在 POSIXsh
中没有标准化)。但是,只有当它是命令中的第一个单词时,它才会被识别为特殊单词。在您的示例中,[[
在命令的后面出现,因此将解析为普通参数,实际上是字符串"[["
,并作为参数列表的一部分传递给[
程序的调用。
1实际上,它不一定是一个单一的过程,而可以是一个作业,一个由|
、&&
、||
等连接的过程的组合。
2不是字面上的这些参数 - shell 将对$STATUS
等进行变量替换。
首先,您不需要将变量名称大写。通常,应为那些为了其他可执行文件的利益而export
到环境中的变量名称保留全大写变量名称。
其次,你对&&
所拥有的东西应该可以工作,但在面对可能为空或非数字的状态变量时,弹性不是很强。
最后,-a
尝试没有奏效-a
因为它是test
/[
语法的一部分......]
;它进入括号内。
从根本上说,关于 shell 中的条件,要知道的最重要的事情是它们只是命令。这个如果表达式:
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; then bar; fi
的缩写。同样,foo || bar
运行foo
,然后,如果该命令以非零状态退出,则运行bar
。(所以它实际上是if ! foo; then bar; fi
的缩写。
这是许多初级 shell 程序员错过的主要内容。shell 本身没有表达式,只有要运行的命令。
但是,由于您经常希望执行比较和其他操作,这些操作在常规非 shell 编程语言中是布尔表达式,因此您可以运行一个特殊命令来执行这些操作。它曾经是一个单独的二进制文件,但现在它内置在 shell 中。它的真名是test
,但它也可以作为[
调用,在这种情况下,如果它的最后一个参数不是匹配的]
,它会抱怨。括号内或括号后的参数test
构成要计算的表达式,如果表达式为 true,则 test 命令以状态 0 退出,如果表达式为 false,则以非零状态退出。因此,它将if
语句在其他编程语言中通常期望的东西转换为您可以运行的命令,从而有效地将其转换为 shell。
[
的外观...]
可能会产生误导;它真的被解析得就像它被拼写test
一样,具有与任何常规shell命令相同的命令行处理。这意味着,如果您尝试使用<
或>
来比较内容,它们将被解释为重定向。尝试使用(
和)
进行分组,您将创建一个子 shell(并且可能会通过在命令的参数中间这样做来生成语法错误)。尝试将表达式与括号内的&&
和||
组合在一起,您将提前终止命令并再次触发语法错误。
您仍然可以在括号外使用&&
和||
;此时,您只需运行测试命令的多个实例,而不仅仅是一个实例。但您也可以使用-a
和-o
,只要它们在括号内。因此,这两个管道产生相同的结果:
[ "$status" -ne 200 -a "$string" != "$value" ]
[ "$status" -ne 200 ] && [ "$string" != "value" ]
不同之处在于,第一个全部由test
命令的一个实例检查,而第二个实例运行其中两个。
Korn Shell 引入了新版本的测试命令,该命令用两个括号而不是一个括号拼写,在括号之间有一个特殊的语法环境,其解析方式与常规命令不同。在双括号之间,您可以安全地使用不带引号的参数扩展、不带引号的括号进行分组、<
和>
进行字符串比较(坚持使用-lt
和-gt
进行数字比较),以及将&&
和||
作为布尔运算符。[[
......]]
还增加了匹配全球模式([[
foo == f*]]
)和正则表达式([[ foo =~ ^f ]]
)的能力。
像 Bash 和 Zsh 这样的现代 shell 继承了 Ksh 的这种结构,但它不是 POSIX 规范的一部分。如果您所处的环境必须严格遵守 POSIX,请远离它;否则,这基本上取决于个人喜好。
请注意,算术替换表达式$((
...))
是POSIX的一部分,它的规则与[[
非常相似......]]
.主要区别在于相等和不等式测试(此处生成一个数值,0 表示 false,1 表示 true)以数字方式完成,而不是按字符串完成:[[ 10 < 2 ]]
为 true,但$(( 10 < 2 ))
为 0。
在这里,现代外壳再次扩展了 POSIX 规范,以添加独立$
无((
版本......))
它基本上是一个自动引号的let
命令,如果你正在处理数字,你可以在if
表达式中使用它。所以你的第一个条件也可以写(( status != 200 ))
.同样,除了必须坚持POSIX之外,这取决于个人喜好。我喜欢使用((
...每当我处理数字时,))
,但其他人更喜欢坚持使用[
或[[
并使用数字运算符,例如-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