shell脚本进阶
1. bash条件判断
1.1 条件测试类型
- 整数测试
- 字符测试
- 文件测试
1.2 条件测试的表达式
[ expression ]
[[ expression ]]
test expression
1.3 整数测试(双目)
-eq //测试两个整数是否相等
-ne //测试两个整数是否不等
-gt //测试一个数是否大于另一个数
-lt //测试一个数是否小于另一个数
-ge //大于或等于
-le //小于或等于
举例:
[root@redhat ~]# [ 1 -eq 2 ]
[root@redhat ~]# echo $?
1
1.4 字符测试
== //等值比较,检查==两边的内容是否一致,==两边都要有空格
!= //检查两边内容是否不一致,不一致为真,一致为假
=~ //左侧字符串是否能够被右侧的PATTERN所匹配到。此表达式应用于双中括号[[]]中
-z "string" //测试指定字符串是否为空,空则为真,不空则为假
-n "string" //测试指定字符串是否不空,不空则为真,空则为假
举例:
[root@redhat ~]# [ "abc" == "abcd" ]
[root@redhat ~]# echo $?
1
[root@redhat ~]# [ -z "hello" ]
[root@redhat ~]# echo $?
1
[root@redhat ~]# [ -z "" ]
[root@redhat ~]# echo $?
0
[root@redhat ~]# [ -n "hello" ]
[root@redhat ~]# echo $?
0
[root@redhat ~]# [ -n "" ]
[root@redhat ~]# echo $?
1
1.5 文件测试
//存在性测试:
-e //测试文件是否存在
举例:
[root@redhat ~]# [ -e apache.sh ] //判断文件是否存在,存在则为真
[root@redhat ~]# echo $?
0
[root@redhat ~]# ls /opt
apr-1.7.4 apr-util-1.6.3 httpd-2.4.57
[root@redhat ~]# [ -e /opt/apache.sh ] //判断文件是否存在,不存在则为假
[root@redhat ~]# echo $?
1
//存在性及类别测试:
-b //测试文件是否为块设备文件
-c //测试文件是否为字符设备文件
-f //测试文件是否为普通文件
-d //测试指定路径是否为目录
-h //测试文件是否为符号链接文件
-L //测试文件是否为符号链接文件
-p //测试文件是否为命名管道文件
-S //测试文件是否为套接字文件
举例:
[root@redhat ~]# [ -d apache.sh ] //写实apache.sh是否为目录
[root@redhat ~]# echo $? //不为目录,判断为假
1
[root@redhat ~]# [ -f apache.sh ] //写实apache.sh是否为文件
[root@redhat ~]# echo $? //是文件,判断为真
0
//文件权限测试:
-r //测试当前用户对指定文件是否有读权限
-w //测试当前用户对指定文件是否有写权限
-x //测试当前用户对指定文件是否有执行权限
举例:
[root@redhat ~]# ll apache.sh //当前用户为root
-rwxr-xr-x. 1 root root 3845 Sep 9 12:47 apache.sh
//root对该文件拥有读写执行的权限,所以三者都为真
[root@redhat ~]# [ -r apache.sh ]
[root@redhat ~]# echo $?
0
[root@redhat ~]# [ -w apache.sh ]
[root@redhat ~]# echo $?
0
[root@redhat ~]# [ -x apache.sh ]
[root@redhat ~]# echo $?
0
//文件特殊权限测试:
-g //测试文件是否有sgid权限
-u //测试文件是否有suid权限
-k //测试文件是否有sticky权限
举例:
[root@redhat ~]# ll -d /tmp
drwxrwxrwt. 12 root root 263 Sep 10 14:03 /tmp
[root@redhat ~]# [ -u /tmp ]
[root@redhat ~]# echo $?
1
[root@redhat ~]# [ -g /tmp ]
[root@redhat ~]# echo $?
1
[root@redhat ~]# [ -k /tmp ]
[root@redhat ~]# echo $?
0
//文件大小测试:
-s //测试文件是否非空,有内容则为真
举例:
[root@redhat ~]# cat ftx //查看ftx文件,文件为空
[root@redhat ~]#
[root@redhat ~]# [ -s ftx ]
[root@redhat ~]# echo $?
1
//文件是否打开测试:
-t fd //fd表示的文件描述符是否已经打开且与某终端相关
//双目测试:
file1 -ef file2 //测试file1与file2是否指向同一个设备上的相同inode,说白点就是两者是不是同一个文件
file1 -nt file2 //测试file1是否比file2新
file1 -ot file2 //测试file1是否比file2旧
举例:
[root@redhat ~]# ln apache.sh hehe.sh
[root@redhat ~]# ll
total 2240
-rw-------. 1 root root 1344 Jul 4 14:48 anaconda-ks.cfg
-rwxr-xr-x. 2 root root 3845 Sep 9 12:38 apache.sh
-rw-r--r--. 1 root root 0 Sep 9 23:45 ftx
-rwxr-xr-x. 2 root root 3845 Sep 9 12:38 hehe.sh
[root@redhat ~]# echo $?
0
[root@redhat ~]# [ apache.sh -ef ftx ]
[root@redhat ~]# echo $?
1
//无分类:
-N //测试文件自从上一次被读取之后是否被修改过
-O //测试文件是否存在并且被当前用户拥有
-G //测试文件是否存在并且默认组是否为当前用户组
举例:
[root@redhat ~]# cat ftx //查看文件
[root@redhat ~]# [ -N ftx ] //判断文件被修改没有
[root@redhat ~]# echo $? //没有修改则为假
1
[root@redhat ~]# echo 'hello' >> ftx //添加一行内容
[root@redhat ~]# [ -N ftx ] //再次判断文件是否被修改
[root@redhat ~]# echo $? //修改过后为真
0
[root@redhat ~]# [ -O ftx ] //测试文件是否存在并且被当前用户拥有
[root@redhat ~]# echo $? //此时为真
0
1.6 组合测试条件
-a //与关系
-o //或关系
! //非关系
[ $# -gt 1 -a $# -le 3 ]
[ $# -gt 1 ] && [ $# -le 3 ]
1.7 条件判断,控制结构
1.7.1 单分支if语句
if 判断条件; then
statement1
statement2
......
fi
举例:
[root@redhat ~]# cat if.sh
#!/bin/bash
# 提示用户输入两个数字
echo -e "
1. +
2. -
3. *
4. /
"
read -p "请输入第一个数字: " num1
read -p "请输入第二个数字: " num2
read -p "请输入运算方式: " yun
# 进行加法计算
if [ $yun -eq 1 ];then 判断当$yun变量等于1,执行下列运算
sum=$(($num1 + $num2))
echo "加法结果为: $sum"
fi
[root@redhat ~]#
1.7.2 双分支if语句
if 判断条件; then
statement1
statement2
......
else
statement3
statement4
......
fi
1.7.3 多分支if语句
if 判断条件1; then
statement1
statement2
......
elif 判断条件2; then
statement3
statement4
......
else
statement5
statement6
......
fi
举例:
[root@redhat ~]# ll chengji.sh
-rwxr-xr-x. 1 root root 238 Sep 10 00:08 chengji.sh
[root@redhat ~]# cat chengji.sh
#!/bin/bash
read -p "输入你的成绩: " cj
if [ $cj -ge 90 ];then
echo "优"
elif [ $cj -ge 80 ];then
echo "良"
elif [ $cj -ge 70 ];then
echo "中"
elif [ $cj -ge 60 ];then
echo "及格"
else
echo "不及格"
fi
[root@redhat ~]# ./chengji.sh
输入你的成绩: 85
良
2. 分支选择
case $变量名 in
value1)
statement
...
;;
value2)
statement
...
;;
*)
statement
...
;;
esac
//case支持glob风格的通配符:
* //任意长度任意字符
? //任意单个字符
[] //指字范围内的任意单个字符
abc|bcd //abc或bcd
举例:
[root@CentOS8 ~]# vim httpd.sh
[root@CentOS8 ~]# cat httpd.sh
#!/bin/bash
case $1 in
start) //当$1为start时,执行systemctl start httpd命令
systemctl start httpd
;;
stop)
systemctl stop httpd //当$1为start时,执行systemctl stop httpd命令
;;
restart)
systemctl restart httpd //当$1为start时,执行systemctl restart httpd命令
;;
status)
ps -ef | grep -v "grep\|$0" | grep httpd &> /dev/null //取出除本身进程以外的httpd的进程
if [ $? -eq 0 ];then //若有其他进程则为真(0),那脚本就会执行echo "httpd is started"
echo "httpd is started"
else
echo "httpd is stopped" //若有其他进程则为假(不为0),那脚本就会执行echo "httpd is stopped"
fi
;;
*) //当$1不是上面的所定义的字符,则会执行下列命令,提供正确的命令
echo "Usage: service httpd start | stop | restart | status"
;;
esac
[root@CentOS8 ~]# ./httpd.sh status //此时查看状态,进程中没有其他关于httpd的进程
httpd is stopped
3. 循环语句
循环语句通常需要有一个进入条件和一个退出条件。
3.1 for循环
for循环当列表不为空时进入循环,否则退出循环
遍历有一个列表,把一个列表里面的一个个去取出来(注:不能用在列表中存在空格的场景下,他会把空格当做成是换行,在有空格的地方再次换行)
for 变量 in 列表; do
循环体
done
for ((expr1;expr2;expr3))
{
循环体
}
for (( expr1 ; expr2 ; expr3 ));do
循环体
done
expr1 //用于指定初始条件,给控制变量一个初始值
expr2 //判定什么时候退出循环
expr3 //修正expr1指定的变量的值
举例:
[root@redhat ~]# cat for.sh
#!/bin/bash
for ((i=1;i<=10;i++));do
echo loop: $i
done
[root@redhat ~]# ./for.sh
loop: 1
loop: 2
loop: 3
loop: 4
loop: 5
loop: 6
loop: 7
loop: 8
loop: 9
loop: 10
[root@redhat ~]# bash -x for.sh
+ (( i=1 )) //起始值i=1
+ (( i<=10 )) //进行判断,满足进入循环的条件
+ echo loop: 1 打印结果
loop: 1
+ (( i++ )) //新的一轮i的值自动加1
+ (( i<=10 )) //再次进行判断
+ echo loop: 2
loop: 2
+ (( i++ ))
+ (( i<=10 ))
+ echo loop: 3
loop: 3
省略. . .
//如何生成列表:
{1..10}
seq [起始数] [步进长度] 结束数
举例:
[root@redhat ~]# seq 1 1 10 //起始值和步进长度不写的话默认为1
1
2
3
4
5
6
7
8
9
10
同时还有下列方法:
[root@redhat ~]# vim for.sh
[root@redhat ~]# chmod +x for.sh
[root@redhat ~]# cat for.sh
#!/bin/bash
for i in {1..10};do
echo loop: $i
done
[root@redhat ~]# ./for.sh
loop: 1
loop: 2
loop: 3
loop: 4
loop: 5
loop: 6
loop: 7
loop: 8
loop: 9
loop: 10
3.2 while循环
while循环适用于循环次数未知的场景,注意要有退出条件。
条件满足时进入循环,条件不满足了退出循环。
3.2.1 while循环正常用法
while 条件; do
statement
...
done
举例:
[root@redhat ~]# vim while.sh
[root@redhat ~]# chmod +x while.sh
[root@redhat ~]# cat while.sh
#!/bin/bash
flag=true //设置条件的变量,为真(true)则进入循环
i=1 //定义i的值为1
while $flag;do
if [ $i -le 10 ];then //当$1小于10时进行执行下列命令
echo $i
elif [ $i -gt 20 ] && [ $i -le 30 ];then //若上述条件不满足则进行下一个判断,$i大于20小于30
echo $i
fi
let i++ //循环一轮自动加1
if [ $i -gt 30 ];then //如果大于30则执行flag=false,将条件的变量改为假,就可以退出循环
flag=false
fi
done
[root@redhat ~]# ./while.sh //执行效果
1
2
3
4
5
6
7
8
9
10
21
22
23
24
25
26
27
28
29
30
3.2.2 while循环特殊用法
//while循环特殊用法一:死循环
while :;do
statement
...
done
//这里的冒号可以改成任何非空值
举例:
[root@redhat ~]# vim while.sh
[root@redhat ~]# cat while.sh
#!/bin/bash
i=1 //定义i的起始值
while true;do //当条件直接写为true时,那这个循环将永无止境,所以我们要今天条件判断使其在何时停止
echo "ftx" $i
let i++
if [ $i -gt 10 ];then //当$i大于10时执行下列命令
echo "stop"
break //break在脚本中有ctrl+c 的作用
fi
done
[root@redhat ~]# ./while.sh ]
ftx 1
ftx 2
ftx 3
ftx 4
ftx 5
ftx 6
ftx 7
ftx 8
ftx 9
ftx 10
stop
while循环特殊用法二:逐行读取某文件,将值存入line变量中
while read line;do
statement
...
done < /path/to/somefile
[root@redhat ~]# vim while.sh
[root@redhat ~]# cat while.sh
#!/bin/bash
while read line;do //line变量代表的是读取的文件中的每一行内容
echo $(echo $line | awk -F: '{print $1":"$NF}') //打印出passwd文件中每行的第一个和最后一个
echo ========================================= //用此符号进行分隔,方便查看
done < passwd
[root@redhat ~]# ./while.sh
root:/bin/bash
=========================================
bin:/sbin/nologin
=========================================
daemon:/sbin/nologin
=========================================
adm:/sbin/nologin
=========================================
lp:/sbin/nologin
=========================================
sync:/bin/sync
=========================================
3.3 until循环
条件不满足时进入循环,条件满足了退出循环。(与while循环正好相反)
until 条件; do
statement
...
done
3.4 循环语句特殊情况
在循环语句中,有几种特别情况:
break [num]:提前退出循环。当循环语句中出现break时,将提前退出循环,不再执行循环后面的语句
continue [num]:提前结束本轮循环而进入下一轮循环。当循环语句执行到continue时,continue后面的语句将不再执行,提前进入下一轮循环
4. 定义脚本退出状态码
//exit命令用于定义执行状态结果
exit # //此处的#号是一个数字,其范围可以是0-255
如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码
注意:脚本中一旦遇到exit命令,脚本会立即终止
=============
lp:/sbin/nologin
=========================================
sync:/bin/sync
=========================================
3.3 until循环
条件不满足时进入循环,条件满足了退出循环。(与while循环正好相反)
until 条件; do
statement
...
done
3.4 循环语句特殊情况
在循环语句中,有几种特别情况:
break [num]:提前退出循环。当循环语句中出现break时,将提前退出循环,不再执行循环后面的语句
continue [num]:提前结束本轮循环而进入下一轮循环。当循环语句执行到continue时,continue后面的语句将不再执行,提前进入下一轮循环
4. 定义脚本退出状态码
//exit命令用于定义执行状态结果
exit # //此处的#号是一个数字,其范围可以是0-255
如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码
注意:脚本中一旦遇到exit命令,脚本会立即终止