第三章 Shell条件测试
用途
-
为了能够正确处理Shell程序运行过程中遇到的各种情况,Linux Shell提供了一组测试运算符。
-
通过这些运算符,Shell程序能够判断某种或者几个条件是否成立。
-
条件测试在各种流程控制语句,例如判断语句和循环语句中发挥了重要的作用,所以了解和掌握这些条件测试是非常重要的
基本语法
格式:
格式1: test -参数 条件表达式test 数值比较 -eq test a -eq b test 字符比较 a = b test a == b [ ] 算数比较 -eq = ==但> <运算符有问题需要通过\转义符 -a -o ! [ ] 字符比较test支持= ==; 不支持-eq 测试符和测试值之间通过空格分割[[ ]] && || 算数比较不需要转义符 [[ ]] 可以支持正则 =~格式2: [ 条件表达式 ] # 注意:[]的左右要有空格
格式3: [[ 条件表达式 ]] # 注意:[]的左右要有空格
格式4: ((条件表达式))
重点数值比较符通过字符类的测试测试
test | [ ] | [[ ]] ---支持正则符 | |
---|---|---|---|
数值比较 | test 1-eq 2 (-eq -gt -lt -ge -le -ne) | -eq -gt -lt -ge -le -ne -a -o ! | -eq -gt -lt -ge -le -ne && || ! --测试符都支持 |
字符比较 | test a = b 或者 test a == b -a -o ! | = == -a -o ! | = == && || ! a =~ ".*" a == a |
-
test单独使用,判断条件为真,echo $?返回0,假返回1
-
test与[ ]等价
-
[[ ]] 是扩展命令,可以使用通配符等进行模式匹配,&& || > < 等操作符可以直接应用于双中括号中,但不能用于单中括号中
-
(()) 一般用于if语句里,两端不需要有空格,测试对象为整数
例
# test语法
[root@server ~]# test -f /etc/passwd
[root@server ~]# echo $?
0
[root@server ~]# test -f /etc/aa
[root@server ~]# echo $?
1
[root@server ~]#
[root@server ~]# test -f /etc/passwd && echo 1 || echo 0
1
[root@server ~]# test -f /etc/aa && echo 1 || echo 0
0
[root@server ~]# test -f /etc/passwd && echo yes || echo no
yes
[root@server ~]# test -f /etc/aa && echo yes || echo no
no
[root@server ~]# if test -f /etc/passwd ; then echo 1 ; else echo no ;fi
1
[root@server ~]# if test -f /etc/aa ; then echo 1 ; else echo no ;fi
no
# [] 语法
[root@server ~]# [-f /etc/passwd] # 注意:[] 中要有空格
bash: [-f: command not found...
[root@server ~]# [ -f /etc/passwd ]
[root@server ~]# echo $?
0
[root@server ~]# [ -f /etc/aa ]
[root@server ~]# echo $?
1
[root@server ~]# [ -f /etc/aa ] && echo y || echo n
n
[root@server ~]# [ -f /etc/passwd ] && echo y || echo n
y
[root@server ~]# if [ -f /etc/passwd ] ; then echo 1 ; else echo no ;fi
1
[root@server ~]# if [ -f /etc/aa ] ; then echo 1 ; else echo no ;fi
no
# [[]] 语法
[root@server ~]# [[ 3 > 2 || 1 > 2 ]] && echo 1 || echo 0
1
[root@server ~]# [[ 3 > 2 && 1 > 2 ]] && echo 1 || echo 0
0
(()) 语法
[root@server ~]# ((9>5)) && echo 1 || echo 0
1
[root@server ~]# (( 9 > 5 )) && echo 1 || echo 0
1
[root@server ~]# ((9>5)) && echo 1 || echo 0
1
[root@server ~]# ((9>13)) && echo 1 || echo 0
0
[root@server ~]# ((9>13.5)) && echo 1 || echo 0 # 注意:不支持小数运算
-bash: ((: 9>13.5:语法错误: 无效的算术运算符 (错误符号是 ".5")
0
文件测试
参数:
参数 | 作用 |
---|---|
-b 文件名 | 检测文件是否是块设备文件,是返回 true |
-c 文件名 | 是否是字符设备文件 |
-d 文件名 | 是否是目录 |
-f 文件名 | 是否是普通文件(既不是目录,也不是设备文件) |
-S 文件名 | 是否为socket文件 |
-P 文件名 | 是否为管道符文件 |
-L 文件名 | 是否为链接文件 |
-u 文件名 | 是否有suid的权限 |
-s 文件名 | 是否为空(文件大小是否大于0),不为空返回 true |
-e 文件名 | 检测文件(包括目录)是否存在,如果是,则返回 true |
-r 文件名 | 检测文件是否可读,如果是,则返回 true。 |
-w 文件名 | 检测文件是否可写,如果是,则返回 true |
-x 文件名 | 检测文件是否可执行,如果是,则返回 true |
f1 -nt f2 | 文件f1比文件f2新则为真(根据文件修改时间计算) |
f1 -ot f2 | 文件f1比文件f2旧则为真(根据文件修改时间计算) |
例
//普通文件测试
[root@localhost ~]# touch file1.txt
[root@localhost ~]# [ -f file1.txt ] && echo yes || echo no
yes
//目录测试
[root@localhost ~]# [ -d /tmp ] && echo yes || echo no
yes
//测试文件属性
[root@localhost ~]# ll file1.txt
-rw-r--r-- 1 root root 0 8月 28 12:30 file1.txt
[root@localhost ~]# [ -r file1.txt ] && echo yes || echo no
yes
[root@localhost ~]# [ -x file1.txt ] && echo yes || echo no
no
[ -e ]
[ -s ]
[root@server ~]# [ -d /root ] && echo y || echo n
y
[root@server ~]# [ -d /aa ] && echo y || echo n
n
[root@server ~]# [ -b /dev/nvme0n1 ] && echo y || echo n
y
[root@server ~]# [ -L /dev/cdrom ] && echo y || echo n
y
[root@server ~]# ll /dev/cdrom
lrwxrwxrwx 1 root root 3 3月 22 11:31 /dev/cdrom -> sr0
[root@server ~]# [ -e /file1 ] && echo y || echo n
n
[root@server ~]# touch /file1
[root@server ~]# [ -e /file1 ] && echo y || echo n
y
# 编写脚本,测试文件是否存在,不存在则创建
[root@server ~]# vim temp1.sh
#!/bin/bash
FILE=$1
echo FILE
if test -e $FILE
thenecho "$FILE文件已存在"
else echo "$FILE文件不存在,开始新建..."touch $FILEls -l $FILE
fi
[root@server ~]# bash temp1.sh /etc/passwd
/etc/passwd
/etc/passwd文件已存在
[root@server ~]# bash temp1.sh temp
temp
temp文件不存在,开始新建...
-rw-r--r-- 1 root root 0 6月 17 14:53 temp
# 上例改写
[root@server ~]# vim temp1.sh
#!/bin/bash
read -p "请输入文件名: " FILE
if test -e $FILE
thenecho "$FILE文件已存在"
elseecho "$FILE文件不存在,开始新建..."touch $FILEls -l $FILE
fi
[root@server ~]# bash temp1.sh
请输入文件名: /etc/sos/sos.conf
/etc/sos/sos.conf文件已存在
[root@server ~]# bash temp1.sh
请输入文件名: t1
t1文件不存在,开始新建...
-rw-r--r-- 1 root root 0 6月 17 14:56 t1
字符串运算符
示例:
//-n如果字符串长度不为零输出yes,否则输出no
[root@localhost ~]# [ -n "hello" ] && echo yes || echo no
yes
[root@localhost ~]# [ -n "" ] && echo yes || echo no
no
注意://变量为空时通过[[]]进行测试,[]测试有问题
[root@localhost ~]# str=`grep xixi /etc/passwd`
[root@localhost ~]# [[ -n $str ]] && echo 有 || echo 无
无
[root@localhost ~]# [ -n $str ] && echo 有 || echo 无
有//-z如果字符串长度为零输出yes,否则输出no
[root@localhost ~]# [ -z "hello" ] && echo yes || echo no
no
[root@localhost ~]# [ -z "" ] && echo yes || echo n
yes
// 字符串相等比较
注意:=的左右有空格,没有空格将会导致逻辑错误。
[root@localhost ~]# [ "HELLO" = "hello" ] && echo yes || echo no
no
[root@localhost ~]# [ "HELO" != "hello" ] && echo yes || echo no
yesif [ -z $a ]
thenecho "-z $a : 字符串长度为 0"
elseecho "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
thenecho "-n $a : 字符串长度不为 0"
elseecho "-n $a : 字符串长度为 0"
fi
if [ $a ] -------$判断z字符串是否为空,不空为真,空为假
thenecho "$a : 字符串不为空"
elseecho "$a : 字符串为空"
fi
整数测试(关系运算符 )
作用
-
用于比较两个数值的大小关系,操作的对象是数值
操作符
数值测试:
[root@172 ~]# [ 1 -eq 2 ] && echo yes || echo no
no
[root@172 ~]# test 1 -eq 2 && echo yes || echo no
no
[root@172 ~]# [[ 1 = 2 ]] && echo yes || echo no
no
[root@172 ~]# [[ 1 -eq 2 ]] && echo yes || echo no
no
[root@172 ~]# ((1>=2)) && echo yes || echo no
no字符串比较
[root@172 ~]# [[ "a" == "b" ]]
[root@172 ~]# echo $?
1
示例:
[root@localhost ~]# [ 5 -gt 3 ] && echo yes || echo no
yes
[root@localhost ~]# [ `id -u` -eq 0 ] && echo admin || echo other
admin
[root@localhost ~]# su - student
[student@localhost ~]$ [ `id -u` -eq 0 ] && echo admin || echo other
other
例
[root@server ~]# [ 5 -gt 3 ] && echo y || echo n
y
[root@server ~]# test 5 -gt 3 && echo y || echo n
y
[root@server ~]# ((5>3)) && echo y || echo 0
y
# 知识拓展
# 检查左侧内容是否包含右侧的表达式,可以使用 =~ 正则匹配,表示是否包含
[root@server ~]# n=123
[root@server ~]# [[ "$n" =~ ^[0-9]+$ ]] && echo y || echo n
y
[root@server ~]# n=123ttt
[root@server ~]# [[ "$n" =~ ^[0-9]+$ ]] && echo y || echo n
n
注意:
字符类的用数学内的比较符,数值类的用字符类比较符
如果数值内的用数学类比较符,例:[ 12 \< 2 ] 结果是true,因为比较方法是先比较第一个数字,再依次向后比较:1 < 2 所以12 < 2
逻辑操作符/布尔运算符
符号
例:
[root@server ~]# [ -f /etc/passwd -a -f /etc/services ] && echo 1 || echo 0
1
[root@server ~]# [ -f /etc/hosts -o -d /etc/services ] && echo 1 || echo 0
1
[root@server ~]# ((5<10 && 5>2)) && echo y || echo n
y
[root@server ~]# ((2<5<10)) && echo y || echo n
y
[root@server ~]# ((2<5<1)) && echo y || echo n
n
[root@server ~]# ((6<5<10)) && echo y || echo n
y
[root@server ~]# ((2<5<-1)) && echo y || echo n
n
-
命令分隔符
cmd1;cmd2 以独立的进程依次执行cmd1和cmd2
(cmd1;cmd2) 在同一进程中依次执行cmd1和cmd2
cmd1&cmd2 cmd1和cmd2同时执行,分属于不同的进程
cmd1&&cmd2 当cmd1为真时,则执行cmd2
cmd1||cmd2 当cmd1不为真时,则执行cmd2
cmd& 后台执行
# 若账户fox10不存在,则添加账户
[root@server ~]# id fox10 &> /dev/null && echo "fox10已存在" || useradd fox10# &> /dev/null 表示将左侧命令执行的正确和错误输出到“黑洞”即不显示到屏幕
命令执行顺序
复合指令:即一串命令
()和{}都是对一串的命令进行执行,但有所区别:
相同点:
- ()和{}都是把一串的命令放在括号里面,如果命令在一行命令之间用 ; 号隔开
- ()和{}中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令
不同点 :
- ()只是对一串命令重新开一个子shell进行执行, {}对一串命令在当前shell执行
- ()最后一个命令可以不用分号,{}最后一个命令要用分号
- ()里的第一个命令和左边括号不必有空格,{}的第一个命令和左括号之间必须要有一个空格
示例:
[root@localhost scripts]# (pwd;cd /tmp;pwd)
/scripts
/tmp
# ()子shell中执行,执行完毕,当前路径不变
[root@localhost tmp]# { pwd;cd /tmp;pwd; } > aaa
/tmp
/tmp
[root@localhost tmp]# pwd;cd /;pwd
比较:
(cd;pwd;hostname) == { cd;pwd;hostname;}
示例:
// 如果目录/abc存在给出提示信息目录已存在,否则创建目录
[ -e /abc -a -d /abc ]
方法1:[root@localhost ~]# [ -d /abc ] && echo exists || mkdir /abc
方法2:[root@localhost ~]# [ ! -d /abc ] && mkdir /abc || echo exists
案例分析
-
例1:判断当前已登录账户数,若超过5个则输出“Too many”
分析:1)如何查看当前登录用户--> who
2)已登录的用户数--> who | wc -l
num=$(who | wc -l)
[ $num -gt 5 ] && echo "Too many"
[root@server ~]# num=$(who | cut -d " " -f1 | sort -u | wc -l)[root@server ~]# [ $num -gt 5 ] && echo "Too many" || echo "已登录账户数:$num"
已登录账户数:1# who : 查看当前已登录的账户信息# cut -d " " -f1 :以空格为列向分割符,截取第1部分# sort -u :去重后排序# wc -l:计算行数# 上例改写为:
[root@server ~]# (($num>5)) && echo "Too many" || echo "已登录账户数:$num"
-
例2:编写脚本temp2.sh ,程序从键盘读入一个目录名,判断该命令是否存在,若不存在则创建,并显示目录信息
[root@server ~]# vim temp2.sh
#!/bin/bashcd /
ls
read -p "请输入一个目录名:" dir
test -d $dir && ls -l $dir || (echo "目录不存在,开始新建..." ; mkdir $dir ; ls -l $dir)
-
例3:如果/var/log/messages文件行数大于30行,则显示提示信息
[root@server ~]# (($(cat /var/log/messages | wc -l)>30)) && echo "好大一个文件" || echo "还能接受"
-
例4:编写脚本temp3.sh,功能:显示root目录下的文件信息,之后建立一个aa目录,在aa目录下新建一个文件bb.txt,并修改该文件的权限为可执行,最后再次浏览信息
[root@server ~]# vim temp3.sh
#!/bin/bashls -l /rootmkdir /root/aatouch /root/aa/bb.txtchmod +x /root/aa/bb.txtls -l /root
-
例5:编写脚本temp4.sh,从键盘读入x,y,计算和值后输出
[root@server ~]# vim temp4.sh
#!/bin/bashread -p "请输入x的值:" xread -p "请输入y的值:" yif [ -n "$x" -a -n "$y" ] # -n判断非空
thenif [[ "$x" =~ ^[0-9]+$ ]] && [[ "$y" =~ ^[0-9]+$ ]] # 判断是数字thenecho $[x+y]elseecho "请输入数字"fi
elseecho "请输入有效内容"
fi
-
例6:编写temp5.sh脚本显示所有用户,选择一个用户输入,判断是root账户还是系统账户还是普通账户
[root@server ~]# vim temp5.sh
#!/bin/bashcat /etc/passwd | cut -d ":" -f1 | sort -uread -p "请输入一个账户名:" usus_num=$(id -u $us)if (($us_num==0))
thenecho "此用户为管理员账户"
elseif (($us_num>=1 && $us_num<=999))thenecho "此账户为系统账户"elseecho "普通账户"fi
fi
-
例7:编写脚本temp6.sh ,给定文件/etc/sos/sos.conf,判断是否存在空白行,若存在则显示行数,否则显示“无空白行”提示信息
[root@server ~]# vim temp6.sh
#!/bin/bashnum=$(grep ^$ /etc/sos/sos.conf | wc -l)if (($num>0))
thenecho "/etc/sos/sos.conf文件含有空白行,行数:$num,位置如下:"grep -n ^$ /etc/sos/sos.conf
elseecho "/etc/sos/sos.conf此文件无空白行"
fi
总结:
- 文件测试
- 算数运算符
- 字符串运算符 -n
- 关系型运算符
- 逻辑运算符
test [ ] [ [ ] ] ( ( ) )
1.文件测试符 -f -d -e -L -b -c -p -S -s -r -w -x / -u -g -k
2.字符串判断符 -n -z
3.关系运算符 -gt -lt -eq -ge -le < =
4.逻辑运算符 布尔值 && || ! -a -o !
5.整体引用符 (cd;pwd;hostname) == { cd;pwd;hostname;}