要使Shell脚本程序具备一定的“智能”,面临的第一个问题就是如何区分不同的情况以确定执行何种操作。Shell环境根据命令执行后的返回状态值($?)来判断是否执行成功,当返回值为0时表示成功,否则(非0值)表示失败或异常。
使用专门的测试工具——test命令,可以对特定条件进行测试,并根据返回值来判断条件是否成立(返回值为0表示条件成立)。
使用test测试命令时,有以下两种形式:
格式一: test 条件表达式 格式二: [ 条件表达式 ] #注意:每一项之间要有空格,格式二更常用 复制代码
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成。
测试过程,实现评估布尔声明,以便用在条件性环境下进行执行:
- 若为真,则状态码变量 $? 返回0
- 若为假,则状态码变量 $? 返回非0
1 条件测试语句
1.1文件测试
文件测试格式:
格式一: test 操作符 表达式 格式二: [ 操作符 表达式 ] 复制代码
常用的文件测试操作符:
操作符 | 作用 |
-d | 测试文件是否为目录文件(Directory) |
-f | 测试文件是否为普通文件(File) |
-e | 测试目录或文件是否存在(Exist)。建议使用-e,-a可能不准确。 |
-a | 测试目录或文件是否存在(Exist) |
-r | 测试当前用户是否有权限读取(Read) |
-w | 测试当前用户是否有权限写入(Write) |
-x | 测试当前用户是否有权限执行(eXcute) |
-L | 测试是否为符号链接(软链接文件) |
-nt | 判断文件A是否比文件B新 |
-ot | 判断文件A是否比文件B旧 |
-ef | 判断两个文件是否为同一文件。可用于硬链接,主要判断两个文件是否指向同一个inode。 |
属性测试补充:
操作符 | 作用 |
-s FILE | 判断文件是否存在且非空 |
-t fd | fd 指定文件描述符是否在某终端已经打开 |
-N FILE | 文件自从上一次被读取之后是否被修改过 |
-O FILE | 当前有效用户是否为文件属主 |
-G FILE | 当前有效用户是否为文件属组 |
示例1:
[root@yuji ~]# test -d /etc/ //测试是否为目录 [root@yuji ~]# echo $? 0 //状态码返回0,表示为真,是目录 [root@yuji ~]# [ -d /etc/ ] //测试是否为目录 [root@yuji ~]# echo $? 0 [root@yuji ~]# test -f /etc/sysconfig //测试是否为普通文件 [root@yuji ~]# echo $? 1 //状态码返回1,表示为假,不是普通文件 [root@yuji ~]# [ -f /etc/sysconfig ] //测试是否为普通文件 [root@yuji ~]# echo $? 1 复制代码
示例2:
-e和-a都可以测试文件是否存在,但和 !(取反)连用时,-a可能不准确,建议使用-e。
[root@yuji ~]# [ -e /etc/shadow ] //测试文件是否存在 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ -a /etc/shadow ] //测试文件是否存在 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ ! -a /etc/shadow ];echo $? //测试文件是否不存在,判断结果有误 0 [root@yuji ~]# [ ! -e /etc/shadow ];echo $? //测试文件是否不存在 1 复制代码
示例3:
测试root用户对文件是否拥有读、写、执行权限。
发现root用户的读取和写入权限,不受文件的基本权限控制;但执行权限受文件基本权限控制。
[root@yuji ~]# ll /etc/shadow ----------. 1 root root 3792 3月 31 21:59 /etc/shadow [root@yuji ~]# [ -r /etc/shadow ] //测试root用户对文件是否拥有读取权限 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ -w /etc/shadow ] //测试root用户对文件是否拥有写入权限 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ -x /etc/shadow ] //测试root用户对文件是否拥有执行权限 [root@yuji ~]# echo $? 1 复制代码
示例4:
测试软链接时,目录名称后面不要加"/",不然会被判断为目录文件。
[root@yuji /]# ls -dl /bin lrwxrwxrwx. 1 root root 7 3月 8 19:13 /bin -> usr/bin //该文件为软链接文件 [root@yuji /]# [ -L /bin ] //测试是否为软链接,是 [root@yuji /]# echo $? 0 [root@yuji /]# [ -L /bin/ ] //测试是否为软链接,不是 [root@yuji /]# echo $? 1 [root@yuji /]# ls -dl /bin/ dr-xr-xr-x. 2 root root 49152 4月 5 02:15 /bin/ //该文件为目录文件 复制代码
1.2 整数测试
整数值比较:
[ 整数变量1 操作符 整数变量2 ] test 整数变量1 操作符 整数变量2 复制代码
操作符:
操作符 | 含义 |
-eq | 等于(Equal) |
-ne | 不等于(Not Equal) |
-gt | 大于(Greater Than) |
-lt | 小于(Lesser Than) |
-le | 小于或等于(Lesser or Equal) |
-ge | 大于或等于(Greater or Equal) |
示例1:
[root@yuji /]# [ 2 -lt 3 ] //测试2是否小于3 [root@yuji /]# echo $? 0 [root@yuji /]# a=5 [root@yuji /]# b=6 [root@yuji /]# [ $a -eq $b ] //测试变量a是否等于变量b [root@yuji /]# echo $? 1 复制代码
示例2:
查询当前目录下的文件数量是否大于10,如果大于,则进行提示。
[root@yuji ~]# ls | wc -l 40 [root@yuji ~]# test `ls |wc -l` -gt 10 && echo "文件数量大于10" 文件数量大于10 复制代码
1.3 字符串测试
常用的测试操作符
- =:字符串内容相同
- !=:字符串内容不同,! 号表示相反的意思
- -z:字符串内容为空
- -n:字符是否存在
字符串比较,常用的四种格式:
[ "字符串1" = "字符串2" ] //测试字符串1和字符串2的内容是否相同 [ "字符串1" != "字符串2” ] //测试字符串1和字符串2的内容是否不同 [ -z "字符串" ] //测试字符串是否为空,或者指定的变量是否为空值。(测试字符串的长度是否为零) [ -n "字符串" ] //测试字符串是否存在,即是否不为空。(测试字符串的长度是否非零) 复制代码
示例1:
[root@yuji ~]# str1=aa [root@yuji ~]# str2=bb [root@yuji ~]# [ $str1 = $str2 ];echo $? //测试变量1和变量2的值是否相同 1 [root@yuji ~]# [ $USER = root ];echo $? //测试当前用户是否为root 0 [root@yuji ~]# [ $USER != root ];echo $? //测试当前用户是否不为root 1 [root@yuji ~]# [ -z $HOME ];echo $? //测试变量HOME是否为空值 1 复制代码
示例2:
使用-z或-n测试变量时,变量需要加引号,避免有歧义而导致判断结果有误。
[root@yuji ~]# empty= //设置一个空值变量 [root@yuji ~]# echo $empty [root@yuji ~]# [ -z $empty ] //测试变量是否为空值,为空 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ -n $empty ] //变量没加引号,导致判断结果有误 [root@yuji ~]# echo $? 0 [root@yuji ~]# [ -n "$empty" ] //加了引号,判断结果正确 [root@yuji ~]# echo $? 1 复制代码
1.4 逻辑测试(短路运算)
逻辑测试用于连接多个测试条件,并返回整个表达式的值。 逻辑测试主要有"逻辑与","逻辑或","逻辑否"三种。
逻辑测试两种格式:
格式一:[ 表达式1 ] 逻辑运算符 [ 表达式2 ] ... #例:[表达式1] && [表达式2] 等同于 [表达式1 -a 表达式2] 等同于 [[表达式1 && 表达式2]] #例:[表达式1] || [表达式2] 等同于 [表达式1 -o 表达式2] 等同于 [[表达式1 || 表达式2]] 格式二:命令1 逻辑运算符 命令2 ... 复制代码
逻辑运算符:
- -a或&& :逻辑与,“而且”的意思,全真才为真。
- -o或|| :逻辑或,“或者”的意思,一真即为真。
- ! :逻辑否。
1)短路与 &&
CMD1 && CMD2 全真才为真,一假即为假。 第一个CMD1结果为真 ,第二个CMD2必须要参与运算,才能得到最终的结果。 第一个CMD1结果为假 ,总的结果必定为假,因此不需要执行CMD2。 同时满足命令1 和命令2 的要求,才会返回正确。 复制代码
2)短路或 ||
CMD1 || CMD2 一真即为真 第一个CMD1结果为真,总的结果必定为真,因此不需要执行CMD2。 第一个CMD1结果为假,第二个CMD2必须要参与运算,才能得到最终的结果。 复制代码
1.4.1 二元运算符
示例1:
[root@yuji ~]# [ 1 -lt 2 ] && [ 3 -eq 4 ] //测试1是否小于2,同时3是否等于4 [root@yuji ~]# echo $? 1 //结果为假 [root@yuji ~]# [ 1 -lt 2 ] || [ 3 -eq 4 ] //测试1是否小于2,或者3是否等于4 [root@yuji ~]# echo $? 0 //结果为真 复制代码
示例2:
[表达式1] && [表达式2]
等同于 [表达式1 -a 表达式2]
等同于 [[表达式1 && 表达式2]]
[root@localhost ~]# a=5 [root@localhost ~]# [ $a -ne 1 ]&&[ $a != 2 ] [root@localhost ~]# echo $? 0 [root@localhost ~]# [ $a -ne 1 -a $a != 2 ] [root@localhost ~]# echo $? 0 [root@localhost ~]# [[ $a -ne 1 && $a != 2 ]] [root@localhost ~]# echo $? 0 复制代码
1.4.2 三元运算符
1、java,C语言
- 条件表达式?a:b
- 当条件表达式为真时,取a值;当条件表达式为假时,取b值。
2、shell
- [ 表达式 ] && a || b
- 当表达式为真时,取a值;当表达式为假时,取b值。
[ 表达式 ] && a //相当于 if语句单分支结构 [ 表达式 ] && a || b //相当于 if语句双分支结构 [ 表达式 ] || b //相当于 if [表达式];else 复制代码
案例应用:查看指定主机是否在线。
1)方法一:使用三元运算符
[root@yuji ~]# vim ping001.sh //使用三元运算符写脚本 #!/bin/bash ping -c 3 -i 0.5 -w 3 $1 &>/dev/null && echo "$1 is up" || echo "$1 is down" [root@yuji ~]# bash ping001.sh 192.168.72.10 //执行脚本测试主机是否在线 192.168.72.10 is up [root@yuji ~]# bash ping001.sh 192.168.72.22 192.168.72.22 is down 复制代码
2)方法二:使用 if语句双分支结构
[root@yuji ~]# vim ping002.sh //使用if语句写脚本 #!/bin/bash ping -c 3 -i 0.5 -w 3 $1 &>/dev/null if [ $? -eq 0 ] then echo "$1 is online" else echo "$1 is offline" fi [root@yuji ~]# bash ping002.sh 192.168.72.10 //执行脚本测试主机是否在线 192.168.72.10 is online [root@yuji ~]# bash ping002.sh 192.168.72.22 192.168.72.22 is offline 复制代码
小贴士:
ping命令:
- -c,发送包的个数。
- -i,发送的间隔时间。
- -w(小写),多少秒后停止ping操作。
- -W(大写),以毫秒为单位设置ping的超时时间 。
2 if 语句
2.1 if 单分支结构
单分支语句格式:
if 判断条件 then 条件成立的分支代码 fi //条件不成立直接结束 复制代码