一、if条件判断
1.单分支if条件语句
单分支if条件语句最为简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做
if [条件判断式];then
程序
fi
单分支条件语句需要注意几个点:
- if语句使用fi结尾,和一般语言用大括号结尾不同
- [条件判断式]就是使用test命令判断所以中括号和条件判断式之间必须有空格
- then后面跟符合条件之后执行的程序,可以放在[ ]之后,用“;”分割。也可以换行写入,就不需要“;”了,比如单分支if语句还可以这样写:
if [条件判断式]
then
程序
fi
例:判断根分区的占比有没有超过80%
rate=$( df -h | grep /dev/sda2 | awk '{print $5}' | cut -d "%" -f 1 )
if [ $"$rate" -ge 80 ]
then
echo "Warning /dev/sd2 is full!"
fi
2.双分支if条件语句
if [条件判断式]
then
条件成立时执行的程序
else
条件不成立时执行的另一个程序
fi
例:数据备份
date = $(date +%y%m%d)
# 同步系统时间,把系统时间按照“年月日”格式赋予变量date
size = $(du -sh /var/lib/mysql)
# 统计mysql数据库的大小,并把大小赋予size变量
if [ -d /tmp/dbbak]
# 判断备份目录是否存在,是否为目录
then
# 如果判断为真,执行以下脚本
echo "Date:$date!" > /tmp/dbbak/dbinfo.txt
# 把当前日期写入临时文件
echo "Date size:$size" >> /tmp/dbbak/dbinfo.txt
# 把数据库大小写入临时文件
cd /tmp/dbbak
# 进入备份目录
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
# 打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)
rm -rf /tmp/dbbak/dbinfo.txt
# 删除临时文件
else
mkdir /tmp/dbbak
# 如果判断为假,则建立备份目录
echo "Date:$date!" > /tmp/dbbak/dbinfo.txt
# 把当前日期写入临时文件
echo "Date size:$size" >> /tmp/dbbak/dbinfo.txt
# 把数据库大小写入临时文件
cd /tmp/dbbak
# 进入备份目录
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null
# 打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)
rm -rf /tmp/dbbak/dbinfo.txt
# 删除临时文件
fi
3.多分支if条件语句
if [条件判断式1]
then
当条件判断式1成立时,执行程序1
elif [条件判断式2]
then
当条件判断式2成立时,执行程序2
......
else
当所有条件不成立时,最后执行此程序
fi
例1:用if多分支条件语句来判断一下用户输入的是一个文件,还是一个目录
read -t 30 -p "please input a filename:" file
# 接收键盘的输入,并赋予变量file
if [ -z "$file"]
# 判断file变量是否为空
then
echo "Error,please input a filename"
# 如果为空,执行程序1,也就是输出报错信息
exit 1
# 退出程序,并返回值为1(把返回值赋予变量$?)
elif [! -e "$file"]
# 判断file的值是否存在
then
echo "Your input is not a file!"
# 如果不存在,则执行程序2
exit 2
# 退出程序,并返回值为2
elif [ -f "$file"]
# 判断file的值是否为普通文件
then
echo "$file is a regulare file!"
# 如果是普通文件则执行程序3
elif [ -d "$file"]
# 判断file的值是否为目录文件
then
echo "$file is a directory!"
# 如果是目录文件,则执行程序4
else
echo "file is an other file!"
# 如果以上判断都不是,则执行程序5
fi
例2:加减乘除计算器
read -t 30 -p "Please input num1:" num1
read -t 30 -p "Please input num2:" num2
# 通过read命令接收要计算的数值,并赋予num1和num2
read -t 30 -p "Please input a operator:" ope
# 通过read命令接收要计算的符号,并赋予变量ope
if [ -n "$num1" -a -n "$num2" -a -n "$ope" ]
# 第一层判断,用来判断num1、num2和ope中都有值
then
test1=$(echo $num1 | sed's/[0-9]//g')
test2=$(echo $num2 | sed's/[0-9]//g')
# 定义变量test1和test2的值为$(命令)的结果
# 后续命令的作用是,把变量test1的值替换为空。如果能替换为空,证明num1的值为数字
# 如果不能替换为空,证明num1的值为非数字。我们使用这种方法判断变量num1的值为数字
# 用同样的方法测试test2变量
if [ -z "$test1" -a -z "$test2"]
# 第二层判断,用来判断num1和num2为数值
# 如果变量test1和test2的值为空,则证明num1和num2是数字
then
# 如果test1和test2是数字,则执行以下命令
if [ "ope" == '+']
# 第三层判断用来确认运算符
# 测试变量$ope中是什么运算符
then
value=$(($num1 + $num2))
# 如果是加号则进行加法运算
elif [ "$ope" == '-']
then
value=$(($num1 - $num2))
# 如果是减号则进行减法运算
elif [ "$ope" == '*']
then
value=$(($num1 * $num2))
elif [ "$ope" == '/']
then
value=$(($num1 / $num2))
else
echo "Please enter a valid symble"
# 如果运算符不匹配,提示输入有效的符号
exit 10
# 并退出程序,返回错误代码10
fi
else
# 如果test1和test2不为空,说明num1和num2不是数字
echo "Please enter a valid value"
# 则提示输入有效的数值
exit 11
# 并退出程序,返回错误代码11
fi
else
echo "Please input:"
exit 12
fi
echo "$num1 $ope $num2 : $value"
# 输出数值运算的结果
二、多分支case条件语句
case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系
case $变量名 in
"值1")
# 如果变量的值等于值1,则执行程序1
;;
"值2")
# 如果变量的值等于值2,则执行程序2
...
*)
# 如果变量的值都不是以上的值,则执行此程序
;;
esac
三、for循环
for循环是固定循环,也就是循环时已经知道需要进行几次循环,有时也把for循环称为计数循环。for的语法有如下两种:
# 语法一
for 变量 in 值1 值2 值3...
do
程序
done
这种语法中for循环的次数,取决于in后面值的个数(空格分割),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设in后面有3个值,for会循环3次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。
# 语法二
for((初始值;循环控制条件;变量变化))
do
程序
done
- 初始值:在循环开始时,需要给某个变量赋予初始值,如i=1
- 循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续
- 变量变化:每次循环之后,变量该如何变化,如i=i+1,代表每次循环之后,变量i的值都加1
例:从1加到100
s=0
for((i=1;i<=100;i=i+1))
do
s=(($s+$i))
done
echo "The sum of 1+2+...100 is : $s"
1.批量解压缩
# 语法二
cd /root/sh/tar
# 进入到保存压缩包的目录
ls *.tar.gz > tar.log
# 把文件名写入临时文件,目的是把所有压缩包名写入临时日志中,wc才能统计压缩包的个数
# 使用*.tar.gz而不是*,能够避免其他文件干扰,值关注压缩文件
ls *.tgz >> tar.log&>/dev/null
# 同理,将其他压缩格式的压缩包文件名追加到临时日志中
# &>:把正确输出或者错误输出都输出到一个文件中
# /dev/null:相当于一个黑洞,可以理解为回收站
aa=$(cat tar.log | wc -l)
# cat输出内容,wc统计行号,aa中保存了压缩包名的个数,其是for循环的循环控制条件
for (( i = 1; i <= "$aa"; i++ )); do
bb=$(cat tar.log | awk 'NR=='$i' {print $1}')
# 每次循环把文件名赋给bb
tar -zxvf $bb -C /root/sh/tar
# 把$bb里的值解压缩到指定包内
done
# NR:当前awk所处理的行,是总数据的第几行
- 需要统计行数,有多少行循环多少次
- 每次循环时要按照行号把数据提取出来,赋在bb变量
# 语法一
cd /root/sh/tar
ls *.tar.gz > tar.log
ls *.tgz >> tar.log&>/dev/null
for i in $(cat tar.log); do
tar -zxvf $i
done
2.合法IP判断
# 语法二
grep "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$" /root/sh/ip.txt > /root/sh/ip_test1.txt
# 先通过正则,把明显不符合规则的ip过滤,把结果保存在/root/sh/ip_test1.txt临时文件中
line=$(wc -l /root/sh/ip_test1.txt | awk '{print $1}')
# 统计test1中有几行ip
echo "" > /root/sh/ip_test.txt
# 清空最终数据文件
for (( i = 1; i < $line; i++ )); do
cat /root/sh/ip_test1.txt | awk 'NR=='$i'{print}' > /root/sh/ip_test2.txt
# 第几次循环,就把第几行读入ip_test2.txt文件(此文件中只有一行ip)
a=$(cat /root/sh/ip_test2.txt | cut -d '.' -f 1)
b=$(cat /root/sh/ip_test2.txt | cut -d '.' -f 2)
c=$(cat /root/sh/ip_test2.txt | cut -d '.' -f 3)
d=$(cat /root/sh/ip_test2.txt | cut -d '.' -f 4)
# 分别把ip地址的四个数值分别读入变量a,b,c,d
if ["$a" -lt 1 -o "$a" -gt 255]
# 如果第一个数值小于1或大于255
then
continue
# 则退出本次循环
fi
if ["$b" -lt 0 -o "$b" -gt 255]
then
continue
fi
if ["$c" -lt 0 -o "$c" -gt 255]
then
continue
fi
if ["$d" -lt 0 -o "$c" -gt 255]
then
continue
fi
#依次判断四个ip数值是否超出范围,如果超出。退出本次循环
cat /root/sh/ip_test2 >> /root/sh/ip_test
# 如果四个ip数值都符合要求,则把合法ip记录在文件中
done
rm -rf /root/sh/ip_test1
rm -rf /root/sh/ip_test2
# 语法一
grep "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$" /root/sh/ip.txt > /root/sh/ip_test1.txt
# 先通过正则,把明显不符合的过滤掉,保存到临时文件中
echo "" > /root/sh/ip_valid.txt
# 清空保存数据的文件,最终该文件最终保存有效ip
for i in $(cat /root/sh/ip_test1.txt) do
a=$(echo "$i" | cut -d "." -f 1)
b=$(echo "$i" | cut -d "." -f 2)
c=$(echo "$i" | cut -d "." -f 3)
d=$(echo "$i" | cut -d "." -f 4)
# 分别把ip地址的四个数值分别读入变量a,b,c,d
if ["$a" -lt -o "$a" -gt 255]
# 如果第一个数值小于1或大于255
then
continue
# 退出本次循环
fi
if ["$b" -lt 0 -o "$b" -gt 255]
then
continue
fi
if ["$c" -lt 0 -o "$c" -gt 255]
then
continue
fi
if ["$d" -lt 0 -o "$d" -gt 255]
then
continue
fi
echo "$i" >> /root/sh/ip_valid.txt
done
rm -rf /root/sh/ip_test1.txt
3.批量添加用户
read -p "Please input user name:" -t 30 name
# 让用户输入用户名,把输入保存入变量name
read -p "Please input the number of users:" -t 30 num
# 让用户输入添加用户的数量,把输入保存入变量num
read -p "Please input the password of users:" -t 30 pass
# 让用户输入初始密码,把输入保存入变量pass
if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ]
# 判断三个变量不为空
then
y=$(echo $num | sed 's/[0-9]//g')
# 定义变量的值为后续命令的结果
# 后续命令的作用是把变量num的值替换为空,如果能替换为空,证明num的值为数字
# 如果不能替换为空,证明num的值为非数字,我们使用这种方法判断变量num的值为数字
if [ -z "$y" ]
# 如果变量y的值为空,证明num变量是数字
then
for (( i = 1; i < $num; i++ )); do
# 循环num变量指定的次数
/usr/sbin/useradd $name$i &> /dev/null
# 添加用户,用户名变量name的值加变量i的数字
echo $pass | /usr/bin/passwd --stdin $name$i &> /dev/null
# 给用户设定初始密码变量pass的值
chage -d 0 $name$i &> /dev/null
# 强制用户登录之后修改密码
done
fi
fi
4.批量删除用户
user=$(cat /etc/passwd | grep "/bin/bash" | grep -v "root" | cut -d ":" -f 1)
# 读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名
for i in $user; do
# 循环,有多少个普通用户,循环多少次
userdel -r $i
done
四、while循环
while [条件判断式]
do
程序
done
对while循环来讲,只要条件判断式成立,循环就会一直继续,知道条件判断式不成立,循环才会停止
例:1加到100
i=1
s=0
# 给变量i和变量s赋值
while [[ $i -le 100 ]]; do
# 如果变量i的值小于等于100,则执行循环
s=$(( $s+$i ))
i=$(( $i+1 ))
# 进行数学运算需要双小括号
done
echo "The sum is:$s"
五、until循环
和while循环相反,until循环只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环
until [条件判断式]
do
程序
done
例:1加到100
i=1
s=0
# 给变量i和变量s赋值
until [[ $i -gt 100 ]]; do
# 循环知道变量i的值大于100,就停止循环
s=$(( $s+$i ))
i=$(( $i+1 ))
done
echo "The sum is: $s"
六、特殊流程控制语句
1.exit语句
系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit 语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。exit 的语法如下:
exit [返回值]
如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。如果exit 之后没有定义返回值,脚本执行之后的返回值是执行exit语句之前,最后执行的一条命令的返回值。写一个exit的例子:
read -p "Please input a number:" -t 30 num
# 接收用户的输入,并把输入赋予变量num
y=$(echo $num | sed 's/[0-9]//g')
# 如果变量num的值时数字,则把num的值替换为空,否则不替换
# 把替换之后的值赋予变量y
[-n "$y"] && echo "Error! Please input number!"
# 判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
echo "The number is: $num"
# 如果没有退出脚本,则打印变量num中的数字
2.break语句
当程序执行到break语句时,会结束整个当前循环;而continue语句也是结束循环的语句,不过continue语句单次当前循环,而下次循环会继续
for (( i = 0; i < 10; i++ )); do
if [[ "$i" -eq 4 ]]; then
break
fi
echo $i
done
一旦变量i的值等于4,,整个循环都会跳出,所以只能循环三次,输出:1,2,3
3.continue语句
再来看看continue语句,continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环
for (( i = 0; i < 10; i++ )); do
if [[ "$i" -eq 4 ]]; then
continue
fi
echo $i
done
一旦变量i的值等于4,则会结束本次循环,进入下次循环,所以会输出:1,2,3,5,6,7,8,9,10