条件测试
条件测试命令
在 Bash shell 中,条件测试是通过使用测试命令来进行的。测试命令有多种形式,包括条件表达式、字符串比较、数值比较等等。
test EXPRESSION[ EXPRESSION ](和test 等价,建议使用 [ ])[[ EXPRESSION ]]
[ EXPRESSION ] 和 [[ EXPRESSION ]] 的区别
在Bash shell中,[ EXPRESSION ] 和 [[ EXPRESSION ]] 之间有一些区别:
[[ EXPRESSION ]]支持更多的操作符和模式匹配,例如&&、||、<、>、<=、>=等。- 而
[ EXPRESSION ]只支持=、!=、<、>操作符。
- 而
[[ EXPRESSION ]]可以进行模式匹配而无需转义,例如[[ "$string" == a* ]]。- 而
[ EXPRESSION ]需要用\转义一些特殊字符,如[ "$string" = a\* ]。
- 而
[[ EXPRESSION ]]可以将变量扩展为它们的值,而不需要引号,例如[[ $foo == 1 ]]。- 而
[ EXPRESSION ]中的变量需要在引号内进行扩展,例如[ "$foo" -eq 1 ]。
- 而
[[ EXPRESSION ]]中的空值会被解释为假,例如[[ $foo ]]当$foo为空时会被解释为假。- 而在
[ EXPRESSION ]中会引发语法错误,需要使用双引号[ "$foo" ]。
- 而在
总而言之,[[ EXPRESSION ]] 更加灵活和安全,建议在Bash shell中尽可能的使用[[ EXPRESSION ]]。
注意事项:
-
EXPRESSION 前后必须有空白字符
-
变量前必须加 $,否则会报错
-
所有的条件判断都是条件成立则为真,即$?返回的值为0。反之条件不成立则为假,即$?的返回值为1。
- 获取帮助:
help test
- 获取帮助:
-
在测试时要注意子shell不会继承父shell的局部变量等问题
条件表达式
用于测试文件是否存在、是否为空、是否可读等等。
文件判断
-a FILE # 同 -e
-e FILE # 文件存在性测试,存在为真,否则为假
-b FILE # 是否存在且为块设备文件
-c FILE # 是否存在且为字符设备文件
-d FILE # 是否存在且为目录文件
-f FILE # 是否存在且为普通文件
-h FILE 或 -L FILE # 存在且为符号链接文件
-p FILE # 是否存在且为命名管道文件
-S FILE # 是否存在且为套接字文件
## 文件属性测试
-s FILE # 是否存在且非空
-t fd # fd 文件描述符是否在某终端已经打开
-N FILE # 文件自从上一次被读取之后是否被修改过
-O FILE # 当前有效用户是否为文件属主
-G FILE # 当前有效用户是否为文件属组
FILE1 -ef FILE2 # FILE1是否是FILE2的硬链接
FILE1 -nt FILE2 # FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2 # FILE1是否旧于FILE2范例
- 判断文件是否存在
## [ -e /etc/passwdww ]
## echo $?
1
## [ -e /etc/passwd ]
## echo $?
0
## [ -a /etc/passwdww ]
## echo $?
1
## [ -a /etc/passwd ]
## echo $?
0- 判断是否存在且为普通文件
if [ -f file.txt ]; then
echo "file.txt 存在"
fi字符串判断
-n 判断字符串是否存在
-n 是一个用于测试字符串是否非空的参数,可以用在多种 shell 中,如 Bash、sh、ksh 等。
-n 参数用于测试一个字符串是否为空字符串,其语法为:[ -n STRING ],其中,STRING 表示要测试的字符串。
如果 STRING 不为空(即字符串长度大于 0),则测试返回真(0),否则返回假(非 0)。因此,这个测试可以用在条件语句中,如 if 语句中:
if [ -n "$string" ]; then
echo "String is not empty."
else
echo "String is empty."
fi这段代码中,如果 $string 是一个非空字符串,那么第一行将会被执行,否则第三行将会被执行。
注意,-n 参数前必须有一个空格,否则会报语法错误。此外,在变量 $string 前有双引号,可以确保即使 $string 的值为空字符串,也不会报错。
另外,还有一个测试字符串是否为空的参数 -z,其语法为:[ -z STRING ],可以用类似的方式使用。
范例
- 如果
$PROXY_HOST和$PROXY_PORT这两个变量都存在的话,则为真,即执行使用代理服务器 - 如果
$PROXY_HOST和$PROXY_PORT这两个变量都不存在的话,则为假,即执行不使用代理服务器
if [[ -n "${PROXY_HOST}" && -n "${PROXY_PORT}" ]]; then
# 使用代理服务器
ftp -v -p -n -S "${PROXY_HOST}:${PROXY_PORT}" "${FTP_HOST}" <<END_SCRIPT
quote USER ${FTP_USER}
quote PASS ${FTP_PASS}
put ${LOCAL_FILE}
quit
END_SCRIPT
else
# 不使用代理服务器
ftp -v -p -n "${FTP_HOST}" <<END_SCRIPT
quote USER ${FTP_USER}
quote PASS ${FTP_PASS}
put ${LOCAL_FILE}
quit
END_SCRIPT
fi-z 判断字符串是否为空
- 字符串未赋值或为空,$?结果返回值为0(成立)
- 字符串不为空,$?结果返回值为1(不成立)
范例
## x=6
## [ -z x ]
## echo $?
1 # 字符串不为空,$?结果返回值为1
## unset x
## [ -z $x ]
## echo $?
0 #字符串为空,$?结果返回值为0
## x=""
## [ -z $x ]
## echo $?
0 #字符串为空,$?结果返回值也为0-n 和 -z 参数用于测试字符串是否非空和空字符串,在使用上二者的区别在于其返回的值意义不同。
-n 参数用于判断字符串是否非空,-n 参数测试后,如果字符串长度大于 0,即字符串非空,则返回真(0),否则返回假(非 0)。
-z 参数则用于判断字符串是否为空,-z 参数测试后,如果字符串长度等于 0,即字符串为空,则返回真(0),否则返回假(非 0)。
下面是两种参数使用的示例:
string="hello"
if [ -n $string ]; then
echo "string is not empty"
fi上面代码中的 -n 参数测试后,因为 string 非空,所以条件成立,输出了 “string is not empty”。
string=""
if [ -z $string ]; then
echo "string is empty"
fi上面代码中的 -z 参数测试后,因为 string 为空,所以条件成立,输出了 “string is empty”。
因此,当我们想要判断一个字符串是否存在时,应该用 -n 参数;
当我们想要判断一个字符串是否为空时,应该用 -z 参数。
同时,由于字符串有可能含有空格等特殊字符,因此在使用这两个参数时,应该将测试的字符串用双引号引起来以避免出现错误。
变量判断
-v 变量是否存在
[ -v NAME ]测试条件用于检查是否存在名为NAME的变量,如果变量存在则返回真(exit status 0),否则返回假(exit status 1)。
范例一
## unset VAR
## [ -v VAR ]
## echo $?
1 # 变量没有被赋值,所以$?结果为1
## VAR=value
## [ -v VAR ]
## echo $?
0 # 变量被赋值,所以$?结果为0
## VAR=
## [ -v VAR ]
## echo $?
0 # 变量被赋空值,$?结果也是为0范例二
## vim test.sh
#!/bin/bash
VAR="123"
if [ -v VAR ];then
echo 666
else
echo 888
fi
## bash test.sh
666
---
## vim test.sh
#!/bin/bash
#VAR="123"
if [ -v VAR ];then
echo 666
else
echo 888
fi
## bash test.sh
888
---
if [ -v VAR ]; then
echo "VAR exists"
else
echo "VAR does not exist"
fi-R 是否为只读变量
[ -R NAME ]测试条件用于检查名为NAME的变量是否被设置为只读变量,如果变量为只读,则返回真,否则返回假。
范例
- 如果
VAR存在并且为只读变量,则分别打印出 “VAR exists” 和 “VAR is readonly”; - 否则,分别打印出 “VAR does not exist” 和 “VAR is not readonly”。
if [ -v VAR ]; then
echo "VAR exists"
else
echo "VAR does not exist"
fi
if [ -R VAR ]; then
echo "VAR is readonly"
else
echo "VAR is not readonly"
fi字符串比较
注意事项
- 在使用字符串比较时,变量应该被用双引号括起来以避免空格等特殊字符的问题。
=
=运算符用于字符串比较,判断两个字符串是否相等
范例
- 通过变量值解析的方式进行比较
if [ "$str1" = "$str2" ]; then
echo "两个字符串相等"
else
echo "两个字符串不相等"
fi- 直接通过字符串比较
if [ "foo" = "foo" ]; then
echo "Equal"
fi!=
- 与=相反
==
==运算符也用于字符串比较,判断两个字符串是否相等- 注意:
==运算符只在 Bash shell 中有效,在其他 shell 中可能不起作用。
范例:[ ]
if [ "foo" == "foo" ]; then
echo "Equal"
fi范例:[[ ]]
-
左侧字符串是否和右侧的PATTERN相同
-
注意:
- 此表达式用于[[ ]]中,PATTERN为通配符
- 右侧的PATTERN:
- 如果加上了双引号:“PATTERN”,即为PATTERN这个字符串本身,而不是通配符。
- 如果不加双引号,把特殊符号转义\,即为字符串本身。
## FILE=test.log
## [[ "$FILE" == *.log ]]
## echo $?
0
## [[ "$FILE" == *.txt ]]
## echo $?
1
## [[ "$FILE" != *.log ]]
## echo $?
1
## [[ "$FILE" != *.txt ]]
## echo $?
0=~
-
左侧字符串是否能够被右侧的PATTERN所匹配
-
注意: 此表达式用于[[ ]]中;PATTERN为扩展的正则表达式
范例:[[ ]]
- 判断是否以.log结尾
## FILE=test.log
## [[ "$FILE" =~ \.log$ ]]
## echo $?
0- 判断是否为纯数字
## N=100
## [[ "$N" =~ ^[0-9]+$ ]]
## echo $?
0
## N=100a
## [[ "$N" =~ ^[0-9]+$ ]]
## echo $?
1- 判断是否为IP地址(非精准,因为最大可以匹配到999.999.999.999)
## IP=1.2.3.4
## [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
## echo $?
0
## IP=999.999.999.999
## [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
## echo $?
0
## IP=1.2.3.4567
## [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
## echo $?
1- 判断是否为IP地址(精准)
## IP=999.999.999.999
## [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
## echo $?
1
## IP=255.255.255.255
## [[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
## echo $?
0- 判断$str是否只包含字母
if [[ "$str" =~ ^[a-zA-Z]+$ ]]; then
echo "str 只包含字母"
fi数值比较
注意事项
- 下面的运算符只能用于数字比较,如果用于字符串比较,则会返回错误。
-eq 等于(equal)
-eq运算符用于数字比较,判断两个数字是否相等,- 例如
[ 2 -eq 2 ]返回真。
范例一
if [ 1 -eq 1 ]; then
echo "Equal"
fi范例二
## x=6
## y=8
## [ ${x} -eq ${y} ]
## echo $?
1 # 两个数值不相等,所以$?返回值为1
## x=6
## y=6
## [ ${x} -eq ${y} ]
## echo $?
0 # 两个数值相等,所以$?返回值为0
## 因为是数值判断,所以变量前必须加$,且变量里必须是数字,否则会报错
## [ x -eq y ]
-bash: [: x: integer expression expected-gt 大于(greater than)
- 左侧变量的值小于右侧变量的值 $? 返回的值为 1
- 左侧变量的值大于右侧变量的值 $? 返回的值为 0
- 例如
[ 3 -gt 2 ]返回真; - 等价于>
范例
## x=6
## y=8
## [ ${x} -gt ${y} ]
## echo $?
1 # 左侧变量的值小于右侧变量的值,所以$?返回的值为1
## [ ${y} -gt ${x} ]
## echo $?
0 # 左侧变量的值大于右侧变量的值,所以$?返回的值为0
## 其它范例
if [ "$num1" -gt "$num2" ]; then
echo "num1 大于 num2"
fi范例:>
- “-gt"运算符和”>“运算符在Bash中是等价的,它们都用于比较两个数值的大小。
- 例如,以下两个条件语句在Bash中具有相同的作用:
if [ $num -gt 10 ]
then
echo "num is greater than 10"
fi
if (( num > 10 ))
then
echo "num is greater than 10"
fi- 其中第一个语句使用”-gt"运算符,而第二个语句使用">“运算符,但它们都会检查变量$num是否大于10。
- 注意,在使用”>“运算符时,变量名周围不需要使用空格。
- 同时,第二个语句使用了双圆括号而不是方括号,因为在双圆括号内可以进行更复杂的数学表达式计算。
-lt 小于(less than)
- 例如
[ 1 -lt 2 ]返回真; - 等价于<
范例
## x=6
## y=8
## [ ${x} -lt ${y} ]
## echo $?
0 # 左侧变量的值小于右侧变量的值,所以$?返回的值为0
## [ ${y} -lt ${x} ]
## echo $?
1 # 左侧变量的值大于右侧变量的值,所以$?返回的值为1-ne 不等于(not equal)
- 不等于$?返回结果为0
- 等于$?返回结果为1
- 例如
[ 1 -ne 2 ]返回真;
-le 小于等于(less or equal)
- 例如
[ 2 -le 2 ]返回真 - 等价于<=
-ge 大于等于(greater or equal)
- 例如
[ 2 -ge 1 ]返回真; - 等价于>=
逻辑运算
&&
范例一
if [ -n "$string" ] && [ "$string" != "hello" ]
then
echo "字符串非空,且不等于hello"
fi范例二
if [ "$str" = "hello" ] && [ "$num" -eq 10 ]; then
echo "str 是 hello 且 num 是 10"
fi||
范例一
- -h或–help
- 这样,当用户使用
-h或--help选项时,将显示帮助信息并退出脚本。
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
show_help
exit 0
fi组合条件测试
-a
[ EXPRESSION1 -a EXPRESSION2 ]并且,EXPRESSION1和EXPRESSION2都是真($?=0),结果才为真(两个条件都成立,$?结果才为0)- -a 表示 and,用来测试两个条件是否都为真。例如,[ $a -gt 10 -a a -lt 20 ] 表示变量a的值是否大于10并且小于20。
范例
## ll /data/scrips/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
## [ -f $FILE -a -x $FILE ]
## echo $?
1
## chmod +x /data/scripts/test.sh
## ll /data/scripts/test.sh
-rwxr-xr-x 1 root root 382 Dec 23 09:32 /data/script/test.sh
## [ -f $FILE -a -x $FILE ]
## echo $?
0
if [ $a -gt 10 -a $b -lt 20 ]
then
echo "a 大于 10 且 b 小于 20"
fi-o
[ EXPRESSION1 -o EXPRESSION2 ]或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为真(其中有一个条件成立,$?结果就为0)- -o 表示 or,用来测试两个条件中的至少一个是否为真。例如,[ $a -eq 10 -o a -eq 20 ] 表示变量a的值是否等于10或20。
## ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
## [ -f $FILE -o -x $FILE ]
## echo $?
0!
[ ! EXPRESSION ]取反- ! 表示not,用来翻转指定条件的逻辑。例如,[ ! -d /tmp ] 表示/tmp目录不存在。
范例
## ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
## [ -x $FILE ]
## echo $?
1
## [ ! -x $FILE ]
## echo $?
0
## ! [ -x $FILE ]
0短路与 短路或
COMMAND1 && COMMAND2
- 并且,短路与,代表条件性的 AND THEN
- 如果 COMMAND1 成功,将执行 COMMAND2 ,失败则不执行 COMMAND2
COMMAND1 || COMMAND2
- 或者,短路或,代表条件性的 OR ELSE
- 如果 COMMAND1 失败,将执行 COMMAND2 ,成功则不执行 COMMAND2
! COMMAND
- 非,取反
注意:如果 && 和 || 混合使用,&& 必须放在前,|| 放在后,否则会引发逻辑错误
在 Shell 中,&& 和 || 是逻辑运算符,用于组合条件语句。如果同时使用这两个运算符,并且没有正确地套用括号或者没有正确地设置条件顺序,就可能会引起逻辑错误。
当我们使用两个条件运算符来连接两个表达式时,我们需要考虑它们的运算优先级和结合性。**在 Shell 中,&& 运算符的优先级高于 || 运算符,且它们的结合性是从左到右。**这意味着,如果我们同时使用 && 和 || 运算符,就需要将 && 放在前面,|| 放在后面,否则就可能会引起逻辑错误。
下面是一个错误的示例:
if [ -f file.txt ] || [ -f file2.txt ] && [ -f file3.txt ]
then
echo "file.txt or file2.txt exists and file3.txt exists"
fi上面代码中,我们想要检查 file.txt 或者 file2.txt 是否存在,并且检查 file3.txt 是否存在。然而,这个条件语句的运算优先级和结合性并不正确,会被解释成:
if ([ -f file.txt ] || [ -f file2.txt ]) && [ -f file3.txt ]这样的话,只有当 file.txt 或者 file2.txt 存在,并且同时 file3.txt 存在时,才会执行 echo 语句。这并不是我们想要的结果。
正确的写法是使用括号来明确运算顺序:
if [ -f file.txt ] || { [ -f file2.txt ] && [ -f file3.txt ]; }
then
echo "file.txt or file2.txt exists and file3.txt exists"
fi上面代码中,我们使用了花括号来明确了条件语句的运算顺序,先检查 file2.txt 和 file3.txt 是否同时存在,然后再和 file.txt 做或运算,这样才能得到正确的结果。
综上,使用 && 和 || 时,应该注意它们的运算优先级和结合性,并且最好使用括号来明确运算顺序,避免逻辑错误。
范例
- 随机删/,危险!
- 大中括号表示的是条件测试test,因为需要对随机数%取模,所以小中括号表示算术运算
[ $[RANDOM%6] -eq 0 ] && rm -rf /* || echo "lucky boy"! 取反
将$?的结果取反
- $?的值为0时,取反后结果为1
- $?的值为1时,取反后结果为0
范例
## x=6
## y=8
## [ ${y} -eq ${x} ]
## echo $?
1
## [ ! ${y} -eq ${x} ]
## echo $?
0