shell编程与条件语句(if,case)循环语句(for,while,until)

122 阅读8分钟

条件测试操作

Shell环境根据命令执行后的返回状态值($?)来判断是否执行成功,当返回值为0时表示成功,否则(非0值)表示失败或异常。使用专门的测试工具test命令,可对条件进行测试,并根据返回值来判断条件是否成立(返回值为0表示成立)。

test命令使用语法:

test 条件表达式 [ 条件表达式 ] (符号两边至少要保证有一个空格)

csharp
复制代码
[root@localhost ~]# test 1>5
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ 1>2 ]
[root@localhost ~]# echo $?
1
csharp
复制代码
[root@localhost ~]# test 8=8
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 9=9 ]
[root@localhost ~]# echo $?
0

文件测试

语法:

[ 操作符 文件或者目录 ]

常用的操作符号:

diff
复制代码
-e :测试目录或文件是否存在(Exist)

-d:测试是否为目录(Directory)

-f:测试是否为文件(File)

-r:测试当前用户是否有权限读取(Read)

-w:测试当前用户是否有权限写入(Write)

-x:测试是否设置有可执行(Excute)权限,

-L:测试是否为符号连接

主意:要检查目录或者文件要用绝对路径去检查,
不使用绝对路径就会检查当前路径下的文件,
所以绝对经路径会更加的严谨。

扩展: [root@localhost ~]# [ -d /data ] && echo "yes" yes && 逻辑与。"而且"的意思

csharp
复制代码
[root@localhost ~]# [ -e /data ]   
[root@localhost ~]# echo $?
0                       
[root@localhost ~]# [ -d /data ]
[root@localhost ~]# echo $?
0         //测试返回0,代表为真,是目录
[root@localhost ~]# [ -f /data ]
[root@localhost ~]# echo $?
1    //测试返回1,代表为假,不是文件
[root@localhost ~]# [ -r wang ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# [ -w wang ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ -x 2 ]
[root@localhost ~]# echo $?
1

整数值比较

语法: [ 整数变量1 操作符 整数变量2 ]

test 整数变量1 操作符号 整数变量2

diff
复制代码
常用的操作符:
-eq:等于     ==
-ne:不等于   !=
-gt:大于     >
-lt:小于     <
-le:小于等于
-ge:大于等于
csharp
复制代码
[root@localhost ~]#a=1
[root@localhost ~]#b=2
[root@localhost ~]# test $a -eq 1
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 2 -gt 1 ] && echo $?
0
扩充复杂使用:
[root@localhost ~]# [[ 99+1 -eq 100 ]]
[root@localhost ~]# echo $?
0
[root@localhost ~]# (( 99+1 == 100 ))
[root@localhost ~]# echo $?
0

&&符号表示:先执行前面的命令,执行成功以后再执行后面的命令

csharp
复制代码
[root@localhost ~]# yum clean all && yum makecache
已加载插件:fastestmirror, langpacks
正在清理软件源: base extras updates

;号表示:如果前面的命令报错依旧会执行后面的命令

csharp
复制代码
[root@localhost ~]# yum clean all  ; yum makecache
已加载插件:fastestmirror, langpacks
/var/run/yum.pid 已被锁定,PID 为 48362 的另一个程序正在运行。
Another app is currently holding the yum lock; waiting for it to exit...

free 查看内存使用情况

yaml
复制代码
[root@localhost ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3865308      387016     2982040        9408      496252     3174792
Swap:       5242876           0     5242876

free -m 以兆为单位显示内存情况

yaml
复制代码
[root@localhost ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3774         373        2916           9         484        3104
Swap:          5119           0        5119

awk 可以截取字段值

sql
复制代码
[root@localhost ~]# free -m | grep 'Mem:' | awk '{print $4}'
2916

条件测试操作

字符串比较

格式1

[ 字符串1 = 字符串2 ]

[ 字符串1 ! = 字符串2 ]

格式2

[ -z "字符串" ] //检查字符串是否为空(Zero)。对于未定义或赋予空值的变量将视为空串

[ -n "字符串" ] //检查是否有字符串存在

主意:引号的使用,这是防止空格软乱代码的好方法

举例说明:

[ 字符串1 = 字符串2 ]

css
复制代码
[root@localhost ~]# [ "$a" = "abc" ]
[root@localhost ~]# echo $?
1

[ 字符串1 ! = 字符串2 ]

css
复制代码
[root@localhost ~]# [ "$a" != "abc" ]
[root@localhost ~]# echo $?
0

[ -z "字符串" ]

csharp
复制代码
[root@localhost ~]# [ -z "$a" ]
[root@localhost ~]# echo $?
1

[ -n "字符串" ]

csharp
复制代码
[root@localhost ~]# [ -n "$a" ]
[root@localhost ~]# echo $?
0

逻辑测试

格式1

[ 表达式1 ] 操作符 [ 表达式2 ]

格式2

命令1 操作符 命令2

常用的测试操作符

  • -a或&&:逻辑与,"而且"的意思
  • -o或||:逻辑或,"或者"的意思
  • !:逻辑否

举例说明:

[ 表达式1 ] 操作符 [ 表达式2 ]

csharp
复制代码
//前后两个表达式在而且的作用下,必须满足两边的要求才能让返回值为0,
[root@localhost ~]# [ $a -eq 6 ] && [ $a != 5 ]
[root@localhost ~]# echo $?
0
//在两个表达式只有一遍能满足条件的那么返回值为非0
[root@localhost ~]# [ $a -eq 6 -a $a == 2 ]
[root@localhost ~]# echo $?
1

命令1 操作符 命令2

csharp
复制代码
两边的表达式只需要满足一个条件就能让返回值为0
[root@localhost ~]# [ $a -eq 6 -o $a == 2 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ $a -eq 6 ] || [ $a == 5 ]
[root@localhost ~]# echo $?
0

扩充:

三元运算符(C Java)

  1. 条件表达式?值1:值2
  • 如果问号前面的表达式为真取冒号前面的值1
  • 如果表达式为假就取冒号后面的值2

SHELL

[ 条件表达式 ] && 值1 || 值2

例子:

java
复制代码
如果前面的条件成立就会执行或前面的运算符
[root@localhost ~]#  [ 5 -gt 2 ] && echo true || echo false
true

如果条件不成立就会执行或后面的运算符
[root@localhost ~]#  [ 5 -lt 2 ] && echo true || echo false
false

脚本测试机器是否在线

less
复制代码
[root@localhost ~]# vim ping.sh
#!/bin/bash

 #-c 几个包  -i间隔时间 -W 指定超时时间 -w(小写)代表多长时间停止ping
ping -c 3 -i 0.5 -w 2 $1 &> /dev/null && echo "$1 is online " || echo "$1 is offlib"

[root@localhost ~]# . ping.sh 192.168.85.20
192.168.85.20 is online
[root@localhost ~]# . ping.sh 192.168.85.30
192.168.85.30 is offlib

if语句的结构

单分支结构图: image.png 执行逻辑:

  • 成立:if语句会进行条件判断,当条件成立就会执行条件后面的代码块,当代码块执行完毕后就会结束if语句
  • 不成立:if语句进行条件判断,如果不成立就直接结束if语句的判断

列子:

bash
复制代码
#!/bin/bash

if [ 3 -ge 2 ];then
  echo "ok"
fi


[root@localhost ~]# . dome1.sh
ok

检查主机是否在线

bash
复制代码
#!/bin/bash

#-c 几个包  -i间隔时间 -W 指定超时时间 -w(小写)代表多长时间停止ping
#ping -c 3 -i 0.5 -w 2 $1 &> /dev/null && echo "$1 is online " || echo "$1 is offlib"
 ping -c 3 -i 0.5 -w 2 $1 &> /dev/null
if [ $? -eq 0 ];then
  echo "$1 is onlie"

fi

[root@localhost ~]# . ping.sh 192.168.85.20
192.168.85.20 is onlie
bash
复制代码
#!/bin/bash
if ping -c 3 -i 0.5 -w 2 $1 &> /dev/null
then
  echo "$1 is online "
else
  echo "$1 is offlib"
fi

[root@localhost ~]# . ping.sh 192.168.85.20
192.168.85.20 is online
[root@localhost ~]# . ping.sh 192.168.85.20
192.168.85.20 is offlib

判断磁盘空间使用率

bash
复制代码
#!/bin/bash

used_disk=$( df | grep /dev/sda1 |awk '{print $5}' |awk -F% '{print $1}')

if [ $used_disk -gt 2  ];then
   echo "警告!!/ 目录磁盘空间不足,目前磁盘使用率为${used_disk}%"
fi


[root@localhost ~]# . demo.sh
警告!!/ 目录磁盘空间不足,目前磁盘使用率为5%
bash
复制代码
三种查看端口号的方法
 netstat -nltp | grep httpd
 ss -nltp | grep httpd
 lsof -i :80

 #!/bin/bash
#80端口是否在监听


 ss -nltp | grep  httpd &> /dev/null

if [ $? -eq 0 ];then
  echo "httpd网站服务已在运行中"
else
 echo "httpd网站服务未运行,正在启动httpd服务........"
  systemctl start httpd

  if  ss -nltp | grep  httpd &> /dev/null
  then
      echo "httpd网站服务启动成功!!"
  else
      echo "httpd网站服务启动失败!!"
   fi
fi

[root@localhost ~]# . demo3.sh
httpd网站服务未运行,正在启动httpd服务........
httpd网站服务启动成功!!
[root@localhost ~]# . demo3.sh
httpd网站服务已在运行中

多分支结构

多分支依据的执行效率不高,因为它是一层一层的判断 QQ截图20220829214016.png

  • 首先判断if语句里面的条件测试操作,如果if条件测试操作满足条件就会执行then里面的代码块
  • 如果if语句不满足条件,则会进一步判断接下来的eilf条件测试操作是否满足条件,满足条件就会走then的代码块,如果不满足,就会走else条件的代码块

例子

考试分数

bash
复制代码
#!/bin/bash

read -p "请输入你的分数(0-100);" num

if [ $num -eq 100 ];then
   echo "你真棒!"

elif [ $num -ge 90 ] && [ $num -le 99 ];then
   echo "错题抄写10遍!"

elif [ $num -ge 70  -a $num -lt 90 ];then
   echo "错题抄写20遍! "

elif [[ $num -gt 59 && $num -le 69 ]];then
   echo "错题抄写30遍!"

elif [ $num -lt 60 ];then
   echo "所有题目抄写30遍! "

else
  echo "输入有误,请输入 0~100 的范围! "

fi


[root@localhost ~]# . demo4.sh
请输入你的分数(0-100);120
输入有误,请输入 0~100 的范围!
[root@localhost ~]# . demo4.sh
请输入你的分数(0-100);100
你真棒!
[root@localhost ~]# . demo4.sh
请输入你的分数(0-100);80
错题抄写20遍!

case语句结构

并行判断。同时判断,执行效率高一些 QQ截图20220829220129.png 如果传入的值满足模式1的值,那么就会去执行模式一所对应的代码块

如果不满足条件会判断模式二,满足模式二的条件就会走模式二的代码块

如果都不满足就会直接结束

列子:

考试分数

bash
复制代码
#!/bin/bash

read -p "请输入你的分数(0-100);" num


case $num in
100)
  echo "你真棒!"
  ;;

9[0-9])
  echo "错题抄写10遍!"
  ;;

[78][0-9])
  echo "错题抄写20遍! "
  ;;

6[0-9])
  echo "错题抄写30遍!"
  ;;

[0-9]|[1-5][0-9] )
  echo "所有题目抄写30遍! "
  ;;

*)
  echo "输入有误,请输入 0~100 的范围! "
  ;;
esac



请输入你的分数(0-100);120
输入有误,请输入 0~100 的范围!
[root@localhost ~]# . demo5.sh
请输入你的分数(0-100);20
所有题目抄写30遍!
[root@localhost ~]# . demo5.sh
请输入你的分数(0-100);90
错题抄写10遍!
[root@localhost ~]# . demo5.sh
请输入你的分数(0-100);2
所有题目抄写30遍!
bash
复制代码
#!/bin/bash

read -p "请输入你的分数(0-100);" num
[ $num -eq 100 ] && a='A'
[ $num -ge 90 -a $num -le 99 ] && a='B'
[ $num -ge 70 -a $num -le 89 ] && a='C'
[ $num -ge 60 -a $num -le 69 ] && a='D'
[ $num -ge 0 -a $num -le 59 ] && a='E'

case $a in
A)
  echo "你真棒!"
  ;;

B)
  echo "错题抄写10遍!"
  ;;

C)
  echo "错题抄写20遍! "
  ;;

D)
  echo "错题抄写30遍!"
  ;;

E)
  echo "所有题目抄写30遍! "
  ;;

*)
  echo "输入有误,请输入 0~100 的范围! "
  ;;

esac



[root@localhost ~]# . demo6.sh
请输入你的分数(0-100);100
你真棒!
[root@localhost ~]# . demo6.sh
请输入你的分数(0-100);5
所有题目抄写30遍!

chkconfig --add添加/etc/init.d/目录中的脚本后,使用 service 命令来进行的管理操作就相当于使用路径指定脚本文件执行操作

bash
复制代码
[root@localhost init.d]# vim firewalld
#!/bin/bash
#chkconfig -99 10

case $1 in
start)
   systemctl start firewalld.service
   ;;

stop)
   systemctl stop firewalld.service
   ;;

restart)
   $0 stop
   $0 start
   ;;

status)
  systemctl status firewalld.service
  ;;

*)
  echo "usage: $0 {start|stop|restart|status}"

esac

[root@localhost init.d]# chkconfig --add firewalld
[root@localhost init.d]# service firewalld start

for语句的结构

读取不同的变量值,用来逐个执行同一组命令

QQ截图20220830163841.png

shell
复制代码
[root@localhost ~]# for num in 1 2 3 4 5 6
> do
> echo $num
> done
1
2
3
4
5
6
shell
复制代码
[root@localhost ~]# for i in $(seq 1 10)
> do
> echo $i
> done
1
2
3
4
5
6
7
8
9
10
csharp
复制代码
[root@localhost ~]# echo {1..10..2}
1 3 5 7 9
csharp
复制代码
[root@localhost ~]# seq 1  5
1
2
3
4
5
[root@localhost ~]# seq 1 2 10
1
3
5
7
9

for语句应用

批量添加用户并改密码

typescript
复制代码
[root@localhost ~]# vim userlist
wangwu
shangsan
zhuliu
laoba
laoliu
liangliang

[root@localhost ~]# cat userlist
wangwu
shangsan
zhuliu
laoba
laoliu
liangliang


[root@localhost ~]# vim demo1.sh
#!/bin/bash
#批量添加用户
#用户名存放在users.txt文件中,每行一个
#初始密码均为123456

for username in $(cat /root/userlist)
do
  useradd $username
  echo 123456 | passwd --stdin $username &> /dev/null
  echo "用户${username}添加完毕"
done

[root@localhost ~]# sh demo1.sh
用户wangwu添加完毕
用户shangsan添加完毕
用户zhuliu添加完毕
用户laoba添加完毕
用户laoliu添加完毕
用户liangliang添加完毕


[root@localhost ~]# tail /etc/passwd
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
w:x:1000:1000:w:/home/w:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
wangwu:x:1001:1001::/home/wangwu:/bin/bash
shangsan:x:1002:1002::/home/shangsan:/bin/bash
zhuliu:x:1003:1003::/home/zhuliu:/bin/bash
laoba:x:1004:1004::/home/laoba:/bin/bash
laoliu:x:1005:1005::/home/laoliu:/bin/bash
liangliang:x:1006:1006::/home/liangliang:/bin/bash
bash
复制代码
#!/bin/bash
#判断用户是否已经存在,用户不存在的添加用户

for username in $(cat /root/userlist)
do
 id $username &> /dev/null
 if [ $? -eq 0 ];then
    echo "$username 已存在"
 else

   useradd $username
   echo 123465 | passwd --stdin $username &> /dev/null
   echo "用户${username}添加完成"
 fi
done


[root@localhost ~]# sh demo2.sh
wangwu 已存在
shangsan 已存在
zhuliu 已存在
laoba 已存在
laoliu 已存在
liangliang 已存在
用户jiujiu添加完成
用户baibai添加完成
用户xuebi添加完成
用户kele添加完成

检查主机在线情况

bash
复制代码
#!/bin/bash
#检查85网段的所有主机在线情况

for IP in 192.168.85.{10..50..10}
do
 ping -c 3 -i 0.5 -w 2 $IP &> /dev/null
 if [ $? -eq 0 ];then
    echo "$IP is online"
  else
    echo "$IP is offline"
  fi
done

[root@localhost ~]# sh demo11.sh
192.168.85.10 is online
192.168.85.20 is offline
192.168.85.30 is offline
192.168.85.40 is offline
192.168.85.50 is offline

多线程查看

bash
复制代码
#!/bin/bash
#shell多线程脚本

for IP in {10..70}
do
 {
  ping -c 3 -i 0.5 -W 2 192.168.85.$IP &> /dev/null
  if [ $? -eq 0 ];then
    echo "192.168.85.$IP is online" >> /data/ping.txt
  else
    echo "192.168.85.$IP is offlien"
  fi
 }&
#{....}&代表的是将每个循环操作的进程放到后台运行,且后台进程不区分前后顺序
done

#wait命令用于等待上面所有后台进程都执行完毕后再执行后面的shell语句
wait

cat /data/ping.txt

while语句结构

重负测试某个条件,只要条件成立则反复执行 QQ截图20220830220943.png 列子:

shell
复制代码
[root@localhost ~]# while [ $i -le 10 ]
> do
> echo $i
> let i++
> done
1
2
3
4
5
6
7
8
9
10

while语句应用

批量添加用户并添加密码

bash
复制代码
#!/bin/bash
#批量添加用户
#用户名以stu开头,按数字顺序进行编号
#一共添加20个用户,如stu1 stu2 ...... stu20
#初始密码均为123456

i=1
while [ $i -le 20 ]
do
 useradd stu$i
 echo 123456 | passwd --stdin stu$i
 echo "用户 stu$i 添加成功"
 let i++
done
bash
复制代码
#!/bin/bash
#猜商品游戏
#通过变量RANDOM获取随机数
#提示用户猜测并技术次数,猜中后退出循环


#获取0-999的随机数
NUM=$[RANDOM % 1000]
#定义变量i为循环条件的变量
i=1
#定义变量a记录猜测次数的变量
a=0
while [ $i -eq 1 ]
do
#获取用户输入的数值
 read -p "请输入你猜测的价格(0-999):" price
#使用随机数和用户输入的数值比较,并且每次比较记录猜测次数,如果数值不等则会一直在死>循环中重负猜测
 if [ $price -eq $NUM ];then
    echo "恭喜!你猜对了!....."
    let a++
    let i++
  elif [ $price -gt $NUM ];then
    echo "不好意思,你猜高了....."
    let a++
  elif [ $price -lt $NUM ];then
    echo "不好意思,你猜低了....."
    let a++
  fi
done
#输出猜测的总次数
echo "你一共猜了 $a 次。"

nutil语句的结构

重复测试某个条件,只要条件不成立则反复执行 QQ截图20220831084047.png

shell
复制代码
[root@localhost ~]# until [ $i -gt 10 ]
> do
> echo $i
> let i++
> done

1
2
3
4
5
6
7
8
9
10

监控登录人数

bash
复制代码
#!/bin/bash
#使用until循环来监控系统的登录人数,当人数高于3人时每10秒提醒警告信息


users=$(who | wc -l)
until [ $users -le 3 ]
do
  echo "当前登录人数为 $users ,大于3人,请注意!!"
  sleep 10
  users=$(who | wc -l)
done


[root@localhost ~]# . demsh
当前登录人数为 6 ,大于3人,请注意!!
当前登录人数为 6 ,大于3人,请注意!!
当前登录人数为 5 ,大于3人,请注意!!

求和值

bash
复制代码
#!/bin/bash
#计算1~50的和值
#通过循环累加的方式计算1~50的和值

sum=0
i=1
until [ $i -gt 50 ]
do
  sum=$[sum + i]
  let i++
done
  echo $sum

[root@localhost ~]# sh demo16.sh
1275

echo的特殊用法大全

命令含义
echo -n表示不换行输出
echo -e输出转义字符,将转义后的内容输出到屏幕上

常用的转义字符

字符含义
\b转义后相当于按退格键(backspace),但前提是"\b"后面存在字符;"\b"表示删除前一个字符,"\b\b"表示删除前两个字符。
\c不换行输出,在"\c"后面不存在字符的情况下,作用相当于echo -n;但是当"\c"后面仍然存在字符时,"\c"后面的字符将不会被输出。
\n换行,被输出的字符从"\n"处开始另起一行
\f换行,但是换行后的新行的开头位置连接着上一行的行尾
\v与\f相同
\t转以后表示插入tab,即横向制表符
\r光标移至行首,但不换行,相当于使用"\r"以后的字符覆盖" \r"之前同等长度的字符;但是当"\r"后面不存在任何字符时,"\r"前面的字符不会被覆盖
\ \表示插入" \ "本身
swift
复制代码
[root@localhost ~]# echo -e  "abc\n123"
abc
123
[root@localhost ~]# echo -e "ancf\v123"
ancf
    123
[root@localhost ~]# echo -e "ancf\f123"
ancf
    123

截取字符串的长度(分片)

${变量名:下标:长度}

css
复制代码
[root@localhost ~]# i=123456789
[root@localhost ~]# echo $i
123456789
[root@localhost ~]# echo ${i:3:2}
45
[root@localhost ~]# echo ${i:3:3}
456
[root@localhost ~]# echo ${i:5:1}
6

字符串长度

${#变量名}

csharp
复制代码
[root@localhost ~]# name=wangwu
[root@localhost ~]# echo $name
wangwu
[root@localhost ~]# echo ${#name}
6

随机获得8位数的密码

bash
复制代码
#!/bin/bash

STR="abcdefghijklmnopqrxtuvwxyzABCDEVFHIJKLMNOPQRXTUVWXYZ123456789"
sum=''
for ((i=1; i<=8; i++))
do
   length=${#STR}
   num=$[RANDOM % length]
   value=${STR:num:1}
   sum+=$value
done
echo "$sum"

[root@localhost ~]# sh demo17.sh
KZ9dLfnZ
[root@localhost ~]# sh demo17.sh
Ele1yaDl

总结

for

读取不同的变量值,用来逐个执行同一组命令

while

重负测试某个条件,只要条件成立则反复执行

until

重复测试某个条件,只要条件不成立则反复执行