Shell编程——检测某个条件是否成立(test、[ ]、[[ ]])

以下内容源于C语言中文网的学习与整理,如有侵权,请告知删除。

前言

(1)检测某个条件是否成立,可以用test命令(其简写为[ ])、[[ ]]关键字。

(2)[[ ]] 是 test 的升级版,对细节进行了优化,并且扩展了一些功能,完全可以取代[ ]。

(3)[[ ]] 对数字的比较仍然不友好,因此使用 if 判断条件时,建议用 (()) 来处理整型数字,用 [[ ]] 来处理字符串或者文件。

一、test命令的简介

1、test命令的用法

(1)test 是 Shell 内置命令,用来检测某个条件是否成立。

(2)test 命令的格式如下,也可以简写为[ ],如下所示。

test expression
#或者
[ expression ]
  • 当 expression 成立时,test命令的退出状态为 0,否则为非 0 值。
  • 注意[]expression之间必须有空格。

(3)test命令通常和 if 语句一起使用,并且大部分 if 语句都依赖 test。

下面代码中,-le选项表示小于等于,-ge选项表示大于等于,&&是逻辑与运算符。

#!/bin/bash

read age

if test $age -le 2; then
    echo "婴儿"
elif test $age -ge 3 && test $age -le 8; then
    echo "幼儿"
elif [ $age -ge 9 ] && [ $age -le 17 ]; then
    echo "少年"
elif [ $age -ge 18 ] && [ $age -le 25 ]; then
    echo "成年"
elif test $age -ge 26 && test $age -le 40; then
    echo "青年"
elif test $age -ge 41 && [ $age -le 60 ]; then
    echo "中年"
else
    echo "老年"
fi

(4)test 命令有很多选项,可以进行数值、字符串和文件三个方面的检测。学习 test 命令,重点是学习它的各种选项,下面我们就逐一讲解。

2、与文件检测相关的 test 选项

(1)与文件检测相关的test选项如下表所示。

表1:test 文件检测相关选项列表
文件类型判断
选 项作 用
-b filename判断文件是否存在,并且是否为块设备文件。
-c filename判断文件是否存在,并且是否为字符设备文件。
-d filename判断文件是否存在,并且是否为目录文件。
-e filename判断文件是否存在。
-f filename判断文件是否存在,井且是否为普通文件。
-L filename判断文件是否存在,并且是否为符号链接文件。
-p filename判断文件是否存在,并且是否为管道文件。
-s filename判断文件是否存在,并且是否为非空。
-S filename判断该文件是否存在,并且是否为套接字文件。
文件权限判断
选 项作 用
-r filename判断文件是否存在,并且是否拥有读权限。
-w filename判断文件是否存在,并且是否拥有写权限。
-x filename判断文件是否存在,并且是否拥有执行权限。
-u filename判断文件是否存在,并且是否拥有 SUID 权限。
-g filename判断文件是否存在,并且是否拥有 SGID 权限。
-k filename判断该文件是否存在,并且是否拥有 SBIT 权限。
文件比较
选 项作 用
filename1 -nt filename2判断 filename1 的修改时间是否比 filename2 的新。
filename -ot filename2判断 filename1 的修改时间是否比 filename2 的旧。
filename1 -ef filename2判断 filename1 是否和 filename2 的 inode 号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

(2)实例演示。

xjh@ubuntu:~/iot/tmp$ ls
test.sh  test.txt
xjh@ubuntu:~/iot/tmp$ cat test.sh 
#!/bin/bash

read filename
read url

if test -w $filename && test -n $url
then
    echo $url > $filename
    echo "写入成功"
else
    echo "写入失败"
fi
xjh@ubuntu:~/iot/tmp$ ./test.sh 
test.txt
http://www.baidu.com.cn
写入成功
xjh@ubuntu:~/iot/tmp$

3、与数值比较相关的 test 选项

(1)与数值比较相关的test选项如下表所示。

表2:test 数值比较相关选项列表
选 项作 用
num1 -eq num2判断 num1 是否和 num2 相等。
num1 -ne num2判断 num1 是否和 num2 不相等。
num1 -gt num2判断 num1 是否大于 num2 。
num1 -lt num2判断 num1 是否小于 num2。
num1 -ge num2判断 num1 是否大于等于 num2。
num1 -le num2判断 num1 是否小于等于 num2。

(2)实例演示。

#!/bin/bash

read a b

if test $a -eq $b
then
    echo "两个数相等"
else
    echo "两个数不相等"
fi

(3)注意,test 只能用来比较整数,小数相关的比较需要用bc命令。

4、与字符串判断相关的 test 选项

(1)与字符串判断相关的test选项如下表所示。

表3:test 字符串判断相关选项列表
选 项作 用
-z str判断字符串 str 是否为空。
-n str判断宇符串 str 是否为非空。
str1 = str2
str1 == str2
===是等价的,都用来判断 str1 是否和 str2 相等。
str1 != str2判断 str1 是否和 str2 不相等。
str1 \> str2

判断 str1 是否大于 str2。

\>>的转义字符,这样写是为了防止>被误认为成重定向运算符。

str1 \< str2判断 str1 是否小于 str2。同样,\<也是转义字符。

(2)test命令比较奇葩,用==、\>、\<这些选项来比较两个字符串,用-eq、-lt这些选项来比较两个数值的大小,注意不能混用,即==、\>、\<这些选项不能用于比较两个数字,而-eq、-lt这些选项也不能用于比较两个字符串。另外,test不支持 >= 和 <= 。

(3)实例演示。

#!/bin/bash

read str1
read str2

#检测字符串是否为空
if [ -z "$str1" ] || [ -z "$str2" ]
then
    echo "字符串不能为空"
    exit 0
fi

#比较字符串
if [ $str1 = $str2 ]
then
    echo "两个字符串相等"
else
    echo "两个字符串不相等"
fi

(4)上面实例中,变量 $str1 和 $str2 都被双引号包围起来,这样做是为了防止 $str1 或者 $str2 是空字符串时出现错误。为什么这么说呢?

  • 我们知道一个shell命令本质上对应一个函数,假设 test 命令对应的函数是 func(),使用“test -z $str1”命令时,会先将变量 $str1 替换成字符串。
  • 如果 $str1 是一个正常的字符串,比如 abc123,则替换后就是“test -z abc123”,调用 func() 函数的形式就是func("-z abc123"),即test 命令后面附带的所有选项和参数会被看成一个整体,并作为实参传递进函数。
  • 但是如果 $str1 是一个空字符串,则替换后就是“test -z”,调用 func() 函数的形式就是func("-z "),因为-z选项没有和参数成对出现,func() 在分析时就会出错。
  • 如果我们给 $str1 变量加上双引号,当 $str1 是空字符串时,test -z "$str1"就会被替换为test -z "",调用 func() 函数的形式就是func("-z \"\""),很显然,-z选项后面跟的是一个空字符串(\"表示转义字符),这样 func() 在分析时就不会出错了。
  • 因此当在 test 命令中使用变量时,建议将变量用双引号""包围起来,能避免变量为空值时导致的很多奇葩问题。

5、与逻辑运算相关的 test 选项

(1)与逻辑运算相关的test选项如下表所示。

表4:test 逻辑运算相关选项列表
选 项作 用
expression1 -a expression逻辑与,表达式 expression1 和 expression2 都成立,最终的结果才是成立的。
expression1 -o expression2逻辑或,表达式 expression1 和 expression2 有一个成立,最终的结果就成立。
!expression逻辑非,对 expression 进行取反。

(2)改写上面的代码,使用逻辑运算选项。上面的代码我们使用两个[]命令,并使用||运算符将它们连接起来,这里我们改成-o选项,只使用一个[]命令就可以了。

#!/bin/bash

read str1
read str2

#检测字符串是否为空
if [ -z "$str1" -o -z "$str2" ]  #使用 -o 选项取代之前的 ||
then
    echo "字符串不能为空"
    exit 0
fi

#比较字符串
if [ $str1 = $str2 ]
then
    echo "两个字符串相等"
else
    echo "两个字符串不相等"
fi

二、关键字[[ ]]的简介 

1、[[ ]]的用法格式

(1)[[ ]]是 Shell 内置的关键字,用来检测某个条件是否成立。

(2)[[ ]]的用法格式如下:

[[ expression ]]
  • 注意[]expression之间必须有空格。
  • 当 [[ ]] 判断 expression 成立时,退出状态为 0,否则为非 0 值。

2、[[ ]] 不需要注意细节

(1)[[ ]] 是 Shell 内置关键字,不是命令,在使用时没有给函数传递参数的过程,所以 test 命令的某些注意事项在 [[ ]] 中就不存在了,具体包括:

  • 不需要把变量名用双引号""包围起来,即使变量是空值,也不会出错。
  • 不需要、也不能对 >、< 进行转义,转义后会出错。

(2)实例演示

xjh@ubuntu:~/iot/tmp$ cat test.sh 
#!/bin/bash

read str1
read str2

if [[ -z $str1 ]] || [[ -z $str2 ]]  #不需要对变量名加双引号
then
    echo "字符串不能为空"
elif [[ $str1 < $str2 ]]  #不需要也不能对 < 进行转义
then
    echo "str1 < str2"
else
    echo "str1 >= str2"
fi
xjh@ubuntu:~/iot/tmp$ ./test.sh 
http://c.biancheng.net/shell/
http://data.biancheng.net/
str1 < str2
xjh@ubuntu:~/iot/tmp$ ./test.sh 
c
d
str1 < str2
xjh@ubuntu:~/iot/tmp$ ./test.sh 
d
c
str1 >= str2
xjh@ubuntu:~/iot/tmp$ 

3、[[ ]] 支持逻辑运算符

(1)对多个表达式进行逻辑运算时,可以使用逻辑运算符将多个 test 命令连接起来。

[ -z "$str1" ] || [ -z "$str2" ]

也可以借助选项把多个表达式写在一个 test 命令中,例如:

[ -z "$str1" -o -z "$str2" ]

(2)上面的两种写法“别扭”,我们希望在一个命令中使用逻辑运算符将多个表达式连接起来。这可以使用关键字 [[ ]] 来实现,它支持 &&、|| 和 ! 三种逻辑运算符。

[[ -z $str1 || -z $str2 ]]

(3)[[ ]] 没有 test 命令那样的-o-a选项,所以下面的形式是错误的。

[[ -z $str1 -o -z $str2 ]] #错误的,[[]]没有-o 选项 

(4)当然,使用逻辑运算符将多个 [[ ]] 连接起来是可以的,因为这是 Shell 本身提供的功能,跟 [[ ]] 或者 test 没有关系,如下所示:

[[ -z $str1 ]] || [[ -z $str2 ]]

(5)各种写法的对错

test 或 [ ][[ ]]说明
[ -z "$str1" ] || [ -z "$str2" ][[ -z $str1 ]] || [[ -z $str2 ]]shell本身提供的功能
[ -z "$str1" -o -z "$str2" ][[ -z $str1 -o -z $str2 ]]×

[ ]内部可以有-o、-a选项

[[ ]]内部不能有这些选项

[ -z $str1 || -z $str2 ]×[[ -z $str1 || -z $str2 ]]

[ ]内部不能用||连接多个表达式

[[ ]]内部可以用||连接多个表达式

4、[[ ]] 支持正则表达式

(1)在[[ ]] 中,可以使用=~来检测字符串是否符合某个正则表达式。

(2)其用法如下,其中str 表示字符串,regex 表示正则表达式。

[[ str =~ regex ]]

(3)下面的代码检测一个字符串是否是手机号。对^1[0-9]{10}$的说明如下:

  • ^匹配字符串的开头(一个位置);
  • [0-9]{10}匹配连续的十个数字;
  • $匹配字符串的末尾(一个位置)。
xjh@ubuntu:~/iot/tmp$ cat test.sh 
#!/bin/bash

read tel

if [[ $tel =~ ^1[0-9]{10}$ ]]
then
    echo "你输入的是手机号码"
else
    echo "你输入的不是手机号码"
fi
xjh@ubuntu:~/iot/tmp$ ./test.sh 
139777
你输入的不是手机号码
xjh@ubuntu:~/iot/tmp$ ./test.sh 
13977732777
你输入的是手机号码
xjh@ubuntu:~/iot/tmp$ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天糊土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值