Shell编程中的条件判断与流程控制

【1】条件判断

① 按照文件类型进行判断

测试选项 作 用
-b 文件 判断该文件是否存在,并且是否为块设备文件(是块设备文件为真)
-c文件 判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真)
-d 文件 判断该文件是否存在,并且是否为目录文件(是目录为真)
-e 文件 判断该文件是否存在(存在为真)
-f 文件 判断该文件是否存在,并且是否为普通文件(是普通文件为真)
-L 文件 判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真)
-p 文件 判断该文件是否存在,并且是否为管道文件(是管道文件为真)
-s 文件 判断该文件是否存在,并且是否为非空(非空为真)
-S 文件 判断该文件是否存在,并且是否为套接字文件(是套接字文件为真)

判断文件是否存在:

[root@localhost shell]# [ -e /home/shell ]
[root@localhost shell]# echo $?
0
#输出为0 表示存在

test -e /home/shell 等同于 [ -e /home/shell ] 两种格式写法。

判断/home/shell是否为目录:

#这里用了&& ||
[root@localhost shell]# [ -d /home/shell ] && echo "yes" || echo "no"
yes


② 按照文件权限进行判断

测试选项 作 用
-r 文件 判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真)
-w文件 判断该文件是否存在,并且是否该文件拥有写权限(有写权限为真)
-x 文件 判断该文件是否存在,并且是否该文件拥有执行权限(有执行权限为真)
-u 文件 判断该文件是否存在,并且是否该文件拥有SUID权限(有SUID权限为真)
-g 文件 判断该文件是否存在,并且是否该文件拥有SGID权限(有SGID权限为真)
-k 文件 判断该文件是否存在,并且是否该文件拥有SBit权限(有SBit权限为真)

测试实例:

[root@localhost shell]# [ -w student.txt ] && echo "yes" || echo "no"
yes

③ 两个文件之间进行比较

测试选项 作 用
文件1 -nt 文件2 判断文件1的修改时间是否比文件2的新(如果新则为真)
文件1 -ot 文件2 判断文件1的修改时间是否比文件2的旧(如果旧则为真)
文件1 -ef 文件2 判断文件1是否和文件2的Inode号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

如下测试硬链:

#创建硬链
[root@localhost shell]# ln ./student.txt student1.txt

#进行测试
[root@localhost shell]# [ student.txt -ef student1.txt ] && echo "yes" || echo "no"
yes

#创建软连
[root@localhost shell]ln -s student.txt student3.txt
# lrwxrwxrwx 1 root root  11 Jul 20 12:10 student3.txt -> student.txt

[root@localhost shell]# [ student.txt -ef student3.txt ] && echo "yes" || echo "no"
yes

#判断是否符号链接文件
[root@localhost shell]# [  -L student3.txt ] && echo "yes" || echo "no"
yes

#判断是否符号链接文件
[root@localhost shell]# [  -L student1.txt ] && echo "yes" || echo "no"
no

关于两个文件比较,还可以之间使用diff命令,如下所示:

[root@localhost shell]# diff student.txt student2.txt
2a3
> 中    Liming  82      95      86      87.66

④ 两个整数之间用来比较

测试选项 作 用
整数1 -eq 整数2 判断整数1是否和整数2相等(相等为真)
整数1 -ne 整数2 判断整数1是否和整数2不相等(不相等为真)
整数1 -gt 整数2 判断整数1是否大于整数2(大于为真)
整数1 -lt 整数2 判断整数1是否小于整数2(小于位置)
整数1 -ge 整数2 判断整数1是否大于等于整数2(大于等于为真)
整数1 -le 整数2 判断整数1是否小于等于整数2(小于等于为真)

测试实例如下:

[root@localhost shell]# [ 23 -ge 22 ] && echo "yes" || echo "no"
yes

⑤ 字符串的判断

测试选项 作 用
-z 字符串 判断字符串是否为空(为空返回真)
-n 字符串 判断字符串是否为非空(非空返回真)
字串1 == 字串2 判断字符串1是否和字符串2相等(相等返回真)
字串1 != 字串2 判断字符串1是否和字符串2不相等(不相等返回真)

测试实例如下:

[root@localhost shell]# [  -z 22 ] && echo "yes" || echo "no"
no
[root@localhost shell]# [  -z "" ] && echo "yes" || echo "no"
yes

#给变量a b赋值
[root@localhost shell]# a=1
[root@localhost shell]# b=2

#注意==左右两边的空格
[root@localhost shell]# [  "$a" == "$b" ] && echo "yes" || echo "no"
no

⑥ 多重条件判断

测试选项 作 用
判断1 -a 判断2 逻辑与,判断1和判断2都成立,最终的结果才为真
判断1 -o 判断2 逻辑或,判断1和判断2有一个成立,最终的结果就为真
!判断 逻辑非,使原始的判断式取反

测试实例如下:

#判断a是否非空且a==b
[root@localhost shell]# [ -n "$a" -a  "$a" == "$b" ] && echo "yes" || echo "no"
no
[root@localhost shell]# [ -n "$a" -a  "$a" -gt 0 ] && echo "yes" || echo "no"
yes

#逻辑非测试
[root@localhost shell]# [ ! -n "$a" ] && echo "yes" || echo "no"
no

【2】流程控制之if条件判断

① 单分支if条件语句

单分支条件语句最为简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做。语法如下:

if [  条件判断式  ];then
    程序
fi

单分支条件语句需要注意几个点:

  • if语句使用fi结尾,和一般语言使用大括号结尾不同
  • [ 条件判断式 ]就是使用test命令判断,所以中括号和条件判断式之间必须有空格
  • then后面跟符合条件之后执行的程序,可以放在[]之后,用“;”分割。也可以换行写入,就不需要“;”了,比如单分支if语句还可以这样写:
    if [ 条件判断式 ]
    	then
    		程序
    fi
    

如下实例判断磁盘空间使用率$(df -h | grep "/dev/sda1" | awk '{print $5}' | cut -d "%" -f1)可得到磁盘使用率整数部分

[root@localhost shell]# vim if1.sh
#/bin/bash
rate=$(df -h | grep "/dev/sda1" | awk '{print $5}' | cut -d "%" -f1)
if [ $rate -le 80 ];then
        echo "Releax! /dev/sda1 is empty!!"
fi

[root@localhost shell]# ./if1.sh
Releax! /dev/sda1 is empty!!

② 双分支if条件语句

语法格式如下:

if [ 条件判断式 ]
	then
		条件成立时,执行的程序
else
		条件不成立时,执行的另一个程序
fi

如下数据备份实例:

#!/bin/bash
#同步系统时间
ntpdate asia.pool.ntp.org &>/dev/null

#把当前系统时间按照“年月日”格式赋予变量date
date=$(date +%y%m%d)

#统计mysql数据库的大小,并把大小赋予size变量
size=$(du -sh /var/lib/mysql)

if [ -d /tmp/dbbak ]
	#判断备份目录是否存在,是否为目录 	#如果判断为真,执行以下脚本
	then
		#把当前日期写入临时文件
		echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
		#把数据库大小写入临时文件
		echo "Data 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 "Data size : $size" >> /tmp/dbbak/dbinfo.txt
		#压缩备份数据库与临时文件
		cd /tmp/dbbak
		tar -zcf mysql-lib-$date.tar.gz dbinfo.txt /var/lib/mysql &>/dev/null
		#删除临时文件
		rm -rf /tmp/dbbak/dbinfo.txt
fi

③ 多分支if条件语句

语法格式如下:

if [ 条件判断式1 ]
	then
		当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]
		then
		当条件判断式2成立时,执行程序2
		…省略更多条件…
else
		当所有条件都不成立时,最后执行此程序
fi

判断用户输入的名字是什么类型文件实例如下:

#/bin/bash
read -p "Please input a filename: " file
#判断file变量是否为空
if [ -z "$file" ]
        then
                echo "Error,please input a filename"
         #退出程序,并返回值为1(把返回值赋予变量$?)
        exit 1
#判断file的值是否存在
elif [ ! -e "$file" ]
        then
                echo "Your input is not a file!"
        exit 2
#判断file的值是否为普通文件
elif [ -f "$file"  ]
        then
                echo "$file is a regulare file!"
#判断file的值是否为目录文件
elif [ -d "$file"  ]
        then
                echo "$file is a directory"
#如果以上判断都不是,则执行程序5
else
        echo "$file is an other file!"
fi

[root@bogon shell]# ./if-elif.sh
Please input a filename: test1.txt
test1.txt is a regulare file!

完善版的四则运算计算器:

#!/bin/bash
#字符界面加减乘除计算器

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 symbol"
                                #如果运算符不匹配,提示输入有效的符号
                                exit 10
								#并退出程序,返回错误代码10
                        fi
                else
				#如果test1和test2不为空,说明num1和num2 不是数字
                        echo "Please enter a valid value"
                        #则提示输入有效的数值
                        exit 11
						#并退出程序,返回错误代码11
        fi
else
        echo "qing shuru neirong"
        exit 12 
fi

echo " $num1 $ope $num2 : $value"
#输出数值运算的结果



【3】流程控制之case条件判断

case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。case语句语法如下:

case $变量名 in
	"值1")
	如果变量的值等于值1,则执行程序1
	;;
	"值2")
	如果变量的值等于值2,则执行程序2
	;;
	…省略其他分支…
	*)
	如果变量的值都不是以上的值,则执行此程序
	;;
esac

这个语句需要注意以下内容:

  • case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。如果所有的值都不符合,则执行“*)”(“*”代表所有其他值)中的程序。
  • case语句以“case”开头,以“esac”结尾(就是case倒转)。

每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束。

测试实例如下:

[root@localhost shell]# cat case.sh
#!/bin/bash
read -p "Please choose yes/no: " -t 30 cho
case "$cho" in
        "yes")
                echo "your choose is yes"
                ;;
        "no")
                echo "your choose is no"
                ;;
        *)
                echo "your choose is error"
                ;;
esac

[root@localhost shell]# ./case.sh
Please choose yes/no: yes
your choose is yes

【4】流程控制之for循环

for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。

for的语法有如下两种。语法一如下:

#语法一
for 变量 in 值1 值2 值3…
	do
		程序
	done

这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。

实例如下:

[root@bogon shell]# ./for.sh
this time is morning
this time is noon
this time is afternoon
this time is evening

[root@bogon shell]# cat for.sh
#!/bin/bash
for time in morning noon afternoon evening
        do
                echo "this time is $time"
        done

语法二如下:

for (( 初始值;循环控制条件;变量变化 ))
	do
		程序
	done

语法二中需要注意:

  • 初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;
  • 循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
  • 变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。

如求和1-100:

[root@bogon shell]# vi for2.sh
#!/bin/bash
s=0
for (( i=1;i<=100;i++  ))
        do
                s=$(( $s+$i  ))
        done
echo "the sum of 1+2+...+100 is:$s"


[root@bogon shell]# ./for2.sh
the sum of 1+2+...+100 is:5050

如下批量添加指定数量的用户:

[root@localhost ~]# vi useradd.sh
#!/bin/bash
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=i+1 ))
			#循环num变量指定的次数
				do
					/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


[root@bogon shell]# ./useradd.sh
Please input user name: jane
Please input the number of users: 1
Please input the password of users: 123456
[root@bogon shell]# cat /etc/passwd|grep "jane"
jane:x:1000:1000:jane:/home/jane:/bin/bash
jane1:x:1001:1001::/home/jane1:/bin/bash

如下批量删除用户:

#!/bin/bash
#批量删除用户

user=$(cat /etc/passwd | grep "/bin/bash"|grep -v "root"|cut -d ":" -f 1)
#读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名

for i in $user
	#循环,有多少个普通用户,循环多少次
	do
		userdel -r $i
		#每次循环,删除指定普通用户 -r表示家目录一同删除
	done

【5】流程控制之while循环

语法格式如下:

#注意空格
while  [ 条件判断式 ]
	do
		程序
	done

对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。

如下求和1-100:

[root@bogon shell]# vim while.sh
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
        do
                s=$(( $s+$i ))
                i=$(( $i+1 ))
        done
echo "the sum 1+2+...+100 is :$s"

[root@bogon shell]# ./while.sh
the sum 1+2+...+100 is :5050

【6】流程控制之until循环

再来看看until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。

语法格式如下:

until  [  条件判断式  ]
	do
		程序
	done

求和1-100:

#!/bin/bash
s=0
i=1;
until [ $i -gt 100 ]
        do
                s=$(( $s+$i ))
                #加上这句将会变成求1-100内的偶数
               # i=$(( $i+1 ))
        done
echo "the sum 1+2+...+100 is :$s"

[root@bogon shell]# ./until.sh
the sum 1+2+...+100 is :5050

【7】特殊流程控制语句

① exit语句

系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。

exit的语法如下:

exit [返回值]

如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit语句之前,最后执行的一条命令的返回值。

测试实例如下:

[root@bogon shell]# vim exit.sh

#!/bin/bash
read -p "Please input a number:" -t 30 num

#如果变量num的值是数字,则把num的值替换为空,否则不替换
#把替换之后的值赋予变量y
y=$( echo $num|sed 's/[0-9]//g' )

#判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
[ -n '$y' ] && echo "Error,please input a number !" && exit 18
echo "the number is :$num"

[root@bogon shell]# ./exit.sh
Please input a number:kk
Error,please input a number !
[root@bogon shell]# echo $?
18

② break语句

当程序执行到break语句时,会结束整个当前循环。而continue语句也是结束循环的语句,不过continue语句单次当前循环,而下次循环会继续。

测试如下:

[root@localhost ~]# vi sh/break.sh
#!/bin/bash
#演示break跳出循环

for (( i=1;i<=10;i=i+1 ))
#循环十次
	do
		if [ "$i" -eq 4 ]
		#如果变量i的值等于4
			then
				break
				#退出整个循环
		fi
		echo $i
		#输出变量i的值
	done

执行下这个脚本,因为一旦变量i的值等于4,整个循环都会跳出,所以只能循环三次:

[root@localhost ~]# chmod 755 break.sh
[root@localhost ~]# sh/break.sh
1
2
3

③ continue语句

continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环,然后继续下次循环。

[root@localhost ~]# vi sh/continue.sh
#!/bin/bash
#演示continue语句

for (( i=1;i<=10;i=i+1 ))
	do
		if [ "$i" -eq 4 ]
			then
				continue
				#退出语句换成continue
		fi
		echo $i
	done

[root@localhost ~]# chmod 755 continue.sh
#赋予执行权限
[root@localhost ~]# ./continue.sh
1
2
3
#少了4这个输出
5 
6
7
8
9
10

continue只会退出单次循环,所以并不影响后续的循环,所以只会少4的输出。这个例子和break的例子做个比较,应该可以更清楚的说明break和continue的区别。

你可能感兴趣的:(Linux全面入门)