目录
一、shell条件测试
shell环境根据命令执行后的返回状态值($?)来判断是否执行成功,当返回值为0时表示成功,否则表示失败或者异常。
1. 格式
test测试文件的表达式是否成立
格式1:test [选项] [对象]
格式2:[ 条件表达式 ] [ 操作符 文件或目录 ]
需要有空格,否则会失败;测试是否成功使用 $?,返回值为0,代表成功,否则失败。
格式3:[[ 条件表达式 ]]
操作符:
-d | 测试是否为目录(Directory) |
-e | 测试目录或文件是否存在(Exist) |
-a | 测试目录或文件是否存在(Exist) |
-f | 测试是否为文件(File) |
-r | 测试当前用户是否有权限读取(Read) |
-w | 测试当前用户是否有权限写入(Write) |
-x | 测试当前用户是否有权限执行(eXcute) |
-L | 测试是否为软连接文件 |
属性测试补充:
- -s FILE:是否存在且非空
- -t fd:fd文件描述符是否在某终端已经打开
- -N FILE:文件自从上一次被读取之后是否被修改过
- -O FILE:当前有效用户是否为文件属主
- -G FILE:当前有效用户是否为文件属组
2. 不同括号的区别
① 单括号 (...)
常用的用法有两种,一种是用于shell中的数组,一种是获取命令执行后的结果
② (( ))只能进行整数比较,不能用于字符串比较,括号中的变量可以省略$符号前缀,当然也可以带着;运算符前后可以有空格,也可以没有;多个运算符用逗号分隔
③ [ ]和[[ ]]的区别:
- 本质上,[ ]和test是等价的,都是命令,而[[ ]]是关键字,所以重定向等字符在[ ]中会被认为成重定向,而在[[ ]]中是比较符号的意思
- &&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不使用双括号, 则为if [ $a != 1] && [ $a != 2 ]或者if [ $a != 1 -a $a != 2 ]
- [[]]支持字符串的模式匹配,[]不支持。使用=或==进行字符串比较,等号右边的可以作为一个模式,比如[[ "hello" == hell? ]]为真。模式匹配不能加引号,否则会作为固定字符串,如[[ "hello" == "hell?" ]]为假
3. 文件测试
根据给定的路径名称,判断对应的是文件还是目录,或者判断文件是否可读、可写、可执行等。
[root@localhost ~]# test -d /root ; echo $?
0
[root@localhost ~]# test -f /root ; echo $?
1
[root@localhost ~]# [ -d /root ];echo $?
0
[root@localhost ~]# [ -f /root ];echo $?
1
[root@localhost ~]# test -e /root && echo "yes"
yes
&&表示且的意思,前面的表达式成立才会输出yes
a和e的区别:
[root@localhost ~]# [ ! -e /root ];echo $?
1
[root@localhost ~]# [ ! -a /root ];echo $?
0
-a取反存在bug,建议使用-e。
4. 整数值比较
根据给定的两个整数值,判断第一个数与第二个数的关系,如是否大于、等于、小于第二个数。格式:[ 整数1 -操作符 整数2 ] 公式
操作符及C语言风格:
在[]以及 test 中使用的比较符号 | 在(())和 []]中使用的比较符号 | 说明 |
-eq | ==或= | 相等,全拼为equal |
-ne | != | 不相等,全拼为 not equal |
-gt | > | 大于,全拼为 greater than |
-ge | >= | 大于等于,全拼为 greater equal |
-lt | < | 小于,全拼为less than |
-le | <= | 小于等于,全拼为 less equal |
示例:
[root@localhost ~]# a=1;b=2
[root@localhost ~]# [ $a -eq $b ];echo $?
1
[root@localhost ~]# ((1<2));echo $?
0
[root@localhost ~]# (( a<$b));echo $?
0
注意:
- 双括号 (( ))是用于整数运算和逻辑运算的,不能用于其他类型数据的运算;双括号中引用变量可以加$也可以不加;运算符前后可以有空格,也可以没有;多个运算符用逗号分隔
- 在[ ] [[ ]]中,数字对比不支持>,<,=等;在(( )) 中,数字对比是支持>,<,=的,但不支持-eq,-ne,-gt等
- 在[ ] [[ ]]中,=与==表示判断,是等价的;在(( )) 中,=表示赋值,==表示判断
5. 字符串比较
通常用来检查用户输入、系统环境等是否满足条件,在提供交互式操作的shell脚本中,也可以用来判断用户输入的位置参数是否符合要求。
常用的测试操作符:
- =:字符串内容相同
- !=:字符串内容不同,! 号表示相反的意思
- -z:字符串内容为空
- -n: 字符是否存在
格式:
- [ 字符串1 = 字符串2 ] 是否相同
- [ 字符串1 != 字符串2 ] 是否不相同
- [ -z 字符串 ] 是否为空
- [ -n 字符串 ] 字符是否存在
示例:
[root@localhost ~]# test1=a;test2=b
[root@localhost ~]# [ $test1 = $test2 ];echo $?
1
[root@localhost ~]# [ $USER = root ] && echo true || echo flase
true
[root@localhost ~]# [ $USER != root ] && echo true || echo flase
flase
注意:
- 使用[ ]对比字符串时,末尾一定要加上x(或者a、b等)一个字符,因为[ $ax == "ab"x ]如果没有了x ,并且$a是"",这个语句会翻译成if [ == "ab" ],左边相当于没有东西了,会报语法错误。或者使用[[ ]],就不需要x了
- 使用<或者>时,如果是用[ ],需要用转义\,如\>
- 使用-z -n时,如果是用 [ ],则字符串一定要加双引号(单引号都不行),如:[ -n "$a" ];如果是用[[ ]],则加不加双引号都可以(但一定不能加单引号)
6. 逻辑测试(短路运算)
格式:
格式1:[ 表达式1 ] 操作符 [ 表达式2 ] ...
格式2:命令1 操作符 命令2 ...
且&&:cmd1 && cmd2,全真才真,一假即假
或||:cmd1 || cmd2,全假才假,一真即真
常见条件
- -a或&&:逻辑与,“而且”的意思全真才为真
- -o或||:逻辑或,“或者”的意思一真即为真
- !:逻辑否
括号组合使用:
- [[ ]]只能使用&& ||,不能使用-a或-o。&&和||写在括号里或外都可以
- (( ))与[[ ]]相同,只不过可以省略变量前的$,且只能用于整数比较
- [ ]既可以使用&& ||,也可以使用-a或-o。但&&和||必须在括号外,-a或-o必须在括号内
括号内连接 | 括号外连接 | |
[ ] | -a -o | && || |
[[ ]] | && || | && || |
示例:判断b>a且b<c
a=1;b=2;c=3
if (( b > a && b < c )) #正确
if (( b > a )) && (( b < c )) #正确
if [[ $b -gt $a ]] && [[ $b -lt $c ]] #正确
if [[ $b -gt $a && $b -lt $c ]] #正确
if [ $b -gt $a ] && [ $b -lt $c ] #正确
if [ $b -gt $a -a $b -lt $c ] #正确
示例2:判断判断a<b,正确输出t,否则f
[ $a -lt $b ] && echo t || echo f
注意在[[ ]]用法中:
- 当使用==和!=操作符时,操作符右边字符用通配符
- 当使用=~操作符时,操作符右边的字符用正则表达式
7. ()和{}
(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行
区别:
[root@localhost ~]# ( cd /opt;ls );pwd #()会开启子shell
rh
/root
[root@localhost ~]# { cd /opt;ls; };pwd # { } 不会开启子shell
rh
/opt
二、流程控制if
if
语句是Shell脚本中常用的条件流程控制语句,用于根据条件的真假执行不同的操作。使用if
语句判断数字的正负性,并输出相应的结果。也可以使用比较运算符(如 -eq
、-lt
、-gt
等)进行条件判断,或者使用逻辑运算符(如 &&
、||
)进行条件组合。另外,还有[ ]
和[[ ]]
两种形式用于条件判断,[ ]
是Shell内建的test命令,而[[ ]]
是Shell的扩展语法。[[ ]]
语法通常更灵活且功能更强大,可以进行模式匹配、正则匹配等操作。
1. 单分支结构
语法:
if 判断条件;then
条件为真的分支代码
fi
示例:由用户输入用户名,如果用户不存在,则创建该用户
#!/bin/bash
read -p "输入用户名: " name
id $name &> /dev/null
if [ $? -ne 0 ];then
useradd $name
fi
2. 双分支结构
语法:
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
示例:由用户输入用户名,如果用户不存在,则创建该用户,并设置密码为123456;否则,提示用户已经存在
#!/bin/bash
read -p "输入用户名: " name
if id $name &> /dev/null;then
echo "$name已存在"
else
useradd $name
echo "123456" | passwd --stdin $name &> /dev/null
echo "$name用户已创建,密码是123456"
fi
3. 多分枝结构
语法:
if 判断条件1;then
条件1为真的分支代码
elif 判断条件2;then
条件2为真的分支代码
elif 判断条件3;then
条件3为真的分支代码
...
else
以上条件都为假的分支代码 托底
fi
示例:根据成绩范围查询优良情况
#!/bin/bash
read -p "请输入你的考试分数:" grade
if [ $grade -ge 85 ] && [ $grade -le 100 ];then
echo "你的成绩为$grade,成绩优秀"
elif [ $grade -ge 70 ] && [ $grade -le 84 ];then
echo "你的成绩为$grade,成绩良好"
elif [ $grade -ge 60 ] && [ $grade -le 69 ];then
echo "你的成绩为$grade,成绩合格"
elif [ $grade -ge 0 ] && [ $grade -le 59 ];then
echo "你的成绩为$grade,成绩不合格"
else
echo "你的输入有误,请重新输入"
bash $0
fi
4. 嵌套结构
语法:
if [ condition1 ]; then
嵌套的 if 语句
if [ condition2 ]; then
嵌套的命令1
else
嵌套的命令2
fi
else
命令3
fi
示例:由用户输入用户名,如果用户存在提示存在;如果用户不存在,由用户输入密码,密码长度需要大于7位,否则提示密码不合格
#!/bin/bash
read -p "输入用户名: " name
id $name &> /dev/null
if [ $? -eq 0 ];then
echo "$name已存在"
else
useradd $name
echo "$name用户名创建成功"
read -p "请输入用户密码: " pass
if [ ${#pass} -ge 7 ];then
echo "$pass" | passwd --stdin $name
echo "$name 用户密码是 $pass"
else
echo "密码不合格"
fi
fi
5. 应用示例
示例1:石头剪刀布
#!/bin/bash
read -p "请输入 1(石头) 2(剪刀) 3(布):" h
m=`echo $[RANDOM%3+1]`
if [ $h -eq $m ]
then
echo "平局!"
elif [ $h -eq 1 -a $m -eq 2 -o $h -eq 2 -a $m -eq 3 -o $h -eq 3 -a $m -eq 1 ];then
echo "您赢了!"
else
echo "您输了!"
fi
示例2:鸡兔同笼
#!/bin/bash
read -p "脚的数量:" jiao
read -p "头的数量:" tou
a=2
JIAO=$[tou*a]
if [ $jiao -ge 0 ];then
tujiao=$[jiao-JIAO]
tnum=$[tujiao/a]
jnum=$[tou-tnum]
echo "鸡的数量:$jnum"
echo "兔的数量:$tnum"
fi
示例3:服务器磁盘空间不足邮件告警
[root@localhost ~]# vim mail.sh
#!/bin/bash
use=80
disk=`df|grep "sd"|tr -s " "|cut -d " " -f5|cut -d "%" -f1`
[ $disk -ge $use ] && echo "磁盘使用率过高"|mail -s test *********@qq.com
[root@localhost ~]# vim /etc/mail.rc
set from=*********@qq.com
set smtp=smtp.qq.com
set smtp-auth-user=********@qq.com
set smtp-auth-password=qqwsd*********
[root@localhost ~]# dd if=/dev/zero of=/boot/bigfile
[root@localhost ~]# bash mail.sh
三、模式匹配case
case是一种条件语句,用于根据不同的条件执行不同的命令或操作。在case
语句中,每个模式以|
分隔,并以右圆括号 )
结束。当用户输入的值与某个模式匹配时,将执行相应的操作,直到遇到 ;;
表示结束。最后的默认模式 *)
会匹配所有未被前面模式匹配的情况。case
语句是根据从上到下的顺序依次匹配模式的,所以在编写语句时请注意模式的顺序。
1. case语法结构
case <变量> in
<模式1>)
命令1
;;
<模式2>)
命令2
;;
<模式3>)
命令3
;;
*)
默认命令
;;
esac
2. 模式匹配示例
示例1:邀请用户输入待删除用户名,询问用户,确定要继续删除吗 yes/no
#!/bin/bash
read -p "请输入删除的用户名: " user
read -p "确认删除吗?[yes/no] " action
case "$action" in
Y|y|YES|yes|Yes|YeS|YEs) #或者写成[yY]|[yY][eE][sS])
userdel -r $user
echo "$user已删除"
;;
*)
echo "thank you"
;;
esac
示例2: 系统工具箱查看不同的系统信息
#!/bin/bash
cat <<EOF
----------------------------------------
|1.查看系统类型
|2.查看主机名
|3.IP地址
|4.查看磁盘大小
|5.查看内存
|6.查看内核版本
|7.查看CPU型号
|8.退出工具箱
----------------------------------------
EOF
read -p "请输入(1~8)选项:" num
case $num in
1)
cat /etc/redhat-release
echo "------------------------------------"
echo
bash $0
;;
2)
hostname
echo "------------------------------------"
echo
bash $0
;;
3)
ifconfig ens33 | grep netmask | tr -s " " | cut -d " " -f3
echo "------------------------------------"
echo
bash $0
;;
4)
lsblk | grep disk | awk '{print $4}'
echo "------------------------------------"
echo
bash $0
;;
5)
free
echo "------------------------------------"
echo
bash $0
;;
6)
uname -r
echo "------------------------------------"
echo
bash $0
;;
7)
lscpu | grep 型号名称 | tr -s " " | cut -d" " -f2-8
echo "------------------------------------"
echo
bash $0
;;
8)
exit
echo "------------------------------------"
echo
bash $0
;;
esac