我正在尝试创建一个简单的 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" ]]
能否请您告知何时使用:
单方括号和双方括号
插入语
?
-a
具有重复性。当与 Bourne shell 风格的 test
命令(又名 [
)一起使用时,它的意思是 and
。当用作 条件表达式 时,它正在测试文件是否存在。是的,这令人困惑,最好避免。
你所拥有的应该可以工作,除非 ${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" 运算符也不起作用: if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]
如需更详细的解释:[
和 ]
不是 Bash 保留字。 if
关键字引入了要由作业评估的条件(如果作业的返回值为 0
,则条件为真,否则为假)。
对于简单的测试,有 test
程序 (man test
)。
由于某些行如 if test -f filename; then foo bar; fi
等令人讨厌,在大多数系统上,您会发现一个名为 [
的程序,它实际上只是 test
程序的符号链接。当 test
被称为 [
时,您必须添加 ]
作为最后一个位置参数。
因此,if test -f filename
与 if [ -f filename ]
基本相同(就产生的进程而言)。在这两种情况下,都会启动 test
程序,并且两个进程的行为应该相同。
这是您的错误:if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]]
将解析为 if
+ 一些作业,该作业是除 if
本身之外的所有内容。这项工作只是一个简单的命令(Bash 代表导致单个进程的东西),这意味着第一个单词 ([
) 是命令,其余是它的位置参数。 在第一个 ]
之后还有剩余的参数。
同样不是,[[
确实是 Bash 关键字,但在这种情况下,它仅被解析为普通命令参数,因为它不在命令的前面。
首先,您不需要将变量名大写。通常,应将全大写变量名保留给export
为了其他可执行文件的利益而被编入环境的变量名。
其次,您所拥有的 &&
应该可以工作,但在面对可能为空或非数字的状态变量时不是很有弹性。
最后,-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; then bar; fi
的缩写。)类似地,foo || bar
运行 foo
,然后,如果该命令以 nonzero 状态退出,则运行 bar
。 (因此它实际上是 if ! foo; then bar; fi
的缩写。)
这是许多初学 shell 程序员错过的主要事情。 shell 本身没有表达式,只有运行命令。
但是,由于您经常想要在常规非 shell 编程语言中执行比较和其他可能是布尔表达式的操作,因此您可以运行一个特殊的命令来执行这些操作。它曾经是一个单独的二进制文件,但现在它已内置到 shell 中。它的真实名称是 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 引入了一个新版本的测试命令,它用两个括号而不是一个括号拼写,在这两个括号之间是一个特殊的语法环境,它与常规命令的解析方式不同。在双括号之间,您可以安全地使用不带引号的参数扩展、不带引号的括号进行分组、<
和 >
进行字符串比较(坚持使用 -lt
和 -gt
进行数字比较)以及 &&
和 ||
作为布尔运算符。 [[
...]]
还增加了匹配 glob 模式 ([[
foo == f* ]]
) 和正则表达式 ([[ foo =~ ^f ]]
) 的能力。
Bash 和 Zsh 等现代 shell 从 Ksh 继承了这种结构,但它不是 POSIX 规范的一部分。如果您处于必须严格遵守 POSIX 的环境中,请远离它;否则,这基本上取决于个人喜好。
请注意,算术替换表达式 $((
...))
是 POSIX 的一部分,它的规则与 [[
...]]
非常相似。主要区别在于相等和不等式测试(这里产生一个数值,0 表示假,1 表示真)以数字方式而不是字符串方式完成:[[ 10 < 2 ]]
为真,但 $(( 10 < 2 ))
产生 0。
同样,现代 shell 在 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
STATUS
为空,来自 @HTF 的代码将在-ne: unary operator expected
上失败。在您的情况下,它会在integer expression expected
上失败,不是吗?$STATUS
可能为空的问题并提出了解决方案(引用它)。您的解决方案仍然以空STATUS
失败,这就是我的意思。${STATUS:-0"
。将编辑。[[
a="this is not an integer"; test "$a" -ge 0; [[ $a -ge 0 ]];
IMO 中生成的(缺少)错误消息,该错误消息很有用,并且 [[ 错误地抑制了它。此外,[[ 返回 0,而 test 返回 1。IMO,这使得[[
无法使用。