第四章 流程控制之条件判断
if语句语法单分支结构# 语法1 if 条件表达式 then 指令 fi#语法2 if 条件表达式;then 指令 fi双分支结构if 条件表达式 then 指令序列1 else 指令序列2 fi多分支结构if 条件表达式1 then 指令序列1 elif 条件表达式2 then 指令序列2 else 指令序列n fi案例例1编写脚本choice1.sh利用单分支结构实现输入2个整数输出最大值[rootserver ~]# vim choice1.sh #!/bin/bash read -p 请输入第一个整数: x read -p 请输入第二个整数: y max$x if (($maxy)) then max$y fi echo 最大值$max例2面试题编写脚本choice2.sh判断系统内存剩余容量大小若低于100M则发送消息进行告警#!/bin/bash free_mem$( free -m | grep Mem | tr -s | cut -d -f4 ) if (($free_mem100)) then echo 警告剩余内存$free_mem ,低于警戒线100MB else echo 剩余内存$free_mem,空间足够. fi # free -m 表示显示内存即虚拟内存的信息-m以MB单位显示数字 # grep Mem表示过滤包含Mem所在行 # tr -s 压缩空格为一个 # cut -d -f4 :以空格为间隔符取其第4部分例3编写脚本choice3.sh判断当前脚本的执行者若不是root账户则提示并退出# 检查当前账户的4种方法 [rootserver ~]# whoami root [rootserver ~]# echo $USER root [rootserver ~]# id -u 0 [rootserver ~]# echo $UID 0[rootserver ~]# vim choice3.sh #!/bin/bash if [ $USER ! root ] then echo please switch user root fi [rootserver ~]# bash choice3.sh # root账户下执行 [rootserver ~]# pwd /root [rootserver ~]# mv choice3.sh / # 让普通账户能访问脚本需要移动到/ [rootserver ~]# chmod x /choice3.sh # 赋予执行权限 [rootserver ~]# ll /choice3.sh -rwxr-xr-x 1 root root 81 6月 18 10:10 /choice3.sh [rootserver ~]# tail -1 /etc/passwd # 查看普通账户名 fox:x:1000:1000::/home/fox:/bin/bash [rootserver ~]# su fox # 切换账户 [foxserver root]$ cd / [foxserver /]$ bash choice3.sh please switch user root例4编写脚本choice4.sh实现闰年的判断[rootserver ~]# vim choice4.sh #!/bin/bash read -p 请输入4位数整数年份: year if [ -n $year ] # 非空测试 then if ((year%40)) ((year%100!0)) || ((year%4000)) then echo 闰年 else echo 平年 fi else echo 请输入有效4位整数年份 fi例5编写脚本choice5.sh判断sshd是否运行# 分析 # 根据进程数判断 [rootserver ~]# ps -ef | grep sshd | grep -v grep | wc -l # 根据端口号判断 [rootserver ~]# netstat -lntup | grep 22 | wc -l[rootserver ~]# vim choice5.sh #!/bin/bash num$(ps -ef | grep sshd | grep -v grep | wc -l) if (($num0)) then echo sshd is running else echo sshd is not running fi例6通过状态码判断网站是否正常# 下载nginx设置网页目录且启动nginx过程略 # 编写脚本 [rootserver ~]# vim choice6.sh #!/bin/bash # 定义要检查的URL URLhttp://192.168.88.129 # 使用curl命令检查URL并获取HTTP状态码 STATUS_CODE$(curl -o /dev/null -s -w %{http_code}\n $URL) # 检查状态码 if [ $STATUS_CODE -eq 200 ] then echo 网站正常 else echo 网站异常 fi例7面试题编写脚本choice7.sh判断主机是否存活[rootserver ~]# vim choice7.sh #!/bin/bash read -p 请输入测试主机的IP地址 ip ping -c 2 -w 3 $ip /dev/null # -c 2 表示发送2个数据包-w 3 表示等待3秒结束注意不能等待1秒结束有可能第二个数据包没有ping完就结束了会返回错误的状态码 if [ $? -eq 0 ] then echo 主机$ip 已运行 else echo 主机$ip 未运行 fi# 上例修改使用循环测试多台主机是否存活设置显示颜色 [rootserver ~]# vim choice6.sh #!/bin/bash for ip in 192.168.48.{125..135} do ping -c 2 -w 3 $ip /dev/null if [ $? -eq 0 ] then echo -e \e[1;31m!!主机 $ip 已运行!!\e[0m # 高亮、红色 else echo 主机$ip 未运行 fi done例8编写脚本choice8.sh输入百分制成绩判断等级成绩[rootserver ~]# vim choice8.sh #!/bin/bash read -p 请输入百分制成绩: score if [ -z $score ] then echo 未输入请重新输入 elif [ $score -lt 0 -o $score -gt 100 ] then echo 成绩输入有误请输入0-100间数字 elif [ $score -ge 90 ] then echo 成绩优秀 elif [ $score -ge 80 ] then echo 成绩良好 elif [ $score -ge 60 ] then echo 成绩及格 else echo 补考 fi 例9编写脚本根据用户输入的内容判断是数字、字母、其它字符[rootserver ~]# vim choice9.sh #!/bin/bash read -p 请输入数字、字母、符号: str if echo $str | grep [a-zA-Z] /dev/null then echo 字母 elif echo $str | grep [0-9] /dev/null then echo 数字 else echo 符号 fi例10编写脚本choice10.sh判断当前主机cpu生产商[rootserver ~]# vim choice10.sh #!/bin/bash vendor$(grep vendor_id /proc/cpuinfo | uniq | cut -d -f2) if [ $vendor GenuineIntel ] then echo Intel elif [ $vendor AuthenticAMD ] then echo AMD else echo other ficase多条件判断格式case 变量名 in 值1) 指令1 ;; 值2) 指令2 ;; 值3) 指令3 ;; * 默认 esac执行过程case语句会将该变量的值与 )括号中的值相比较如果与某个值相等则执行对应语句。当遇到“;;”符号时就跳出case语句执行esac语句后面的语句。如果没有与任何一个值相匹配则执行*)后面的一组语句示例例10编写脚本choice10.sh对上例7的百分制成绩判断等级成绩进行改写[rootserver ~]# vim choice10.sh #!/bin/bash read -p 请输入百分制成绩: score case $score in 9[0-9]|100) echo 成绩优秀 ;; 8[0-9]) echo 成绩良好 ;; 6[0-9]|7[0-9]) echo 成绩及格 ;; *) echo 补考 esac第五章 流程控制之循环for循环for循环是最简单也是最常用的循环语句。与其他的程序设计语言一样for循环都是初学者在学习循环结构时的入门课程。for循环通常用于遍历整个对象或者数字列表。按照循环条件的不同for循环语句可以分为带列表的for循环、不带列表的for循环以及类C风格的for循环。本节将介绍这3种不同的for循环结构。带列表的for循环格式for variable in {list} do statement1 statement2 ... done分析variable称为循环变量list是一个列表可以是一系列的数字或者字符串元素之间使用空格隔开do和done之间的所有的语句称为循环体即循环结构中重复执行的语句执行时Shell会将in关键字后面的list列表的第1个元素的值赋给变量variable然后执行循环体当循环体中的语句执行完毕之后剩余以此类推当list列表中的所有的元素都被访问后for循环结构终止程序将继续执行done语句后面的其他的语句例1[rootserver ~]# vim for1.sh #!/bin/bash for ip in 192.168.48.130 192.168.48.131 do echo $ip done [rootserver ~]# bash for1.sh 192.168.48.130 192.168.48.131[rootserver ~]# vim for2.sh #!/bin/bash for ip in 192.168.48.{130..140..2} # 使用{start..end..step} 进行扩展 do echo $ip done [rootserver ~]# bash for2.sh 192.168.48.130 192.168.48.132 192.168.48.134 192.168.48.136 192.168.48.138 192.168.48.140例2获取指定目录下的所有目录的文件名[rootserver ~]# vim for3.sh #!/bin/bash for FILE in $(ls -F / | grep /$) do echo $FILE done [rootserver ~]# bash for3.sh afs/ boot/ dev/ etc/ home/ media/ mnt/ opt/ proc/ root/ run/ srv/ sys/ tmp/ usr/ var/ # ls -F :用于将目录内的文件按照文件类型进行归类显示会在不同类别的文件名尾部增加不同的标识符目录以/结尾、链接文件以结尾、可执行文件以*结尾、普通文件无符号 # grep /$ 表示以/结尾的行shell允许用户指定for语句的步长格式如下for variable in {start..end..step} do statement1 statement2 ... done例3计算12345……100和[rootserver ~]# bash for4.sh #!/bin/bash sum0 for i in {1..100..1} do ((sumsumi)) done echo sum$sum # 上例修改 [rootserver ~]# bash for4.sh #!/bin/bash sum0 for i in $(seq 100) do ((sumsumi)) done echo sum$sum不带列表的for循环原则for不提供循环列表shell将从命令行获取循环列表数据及条件格式for variable do statement1 statement2 ... done由于系统变量$可以获取所有位置参数以上的语法等价于for variable in $或$* do statement1 statement2 ... done例4[rootserver ~]# vim for5.sh #!/bin/bash for arg do echo $arg done [rootserver ~]# bash for5.sh {1..10}基于C语言风格的for循环格式for ((初始化条件表达式步长)) # 注意2个园括号 do statement1 statement2 ... done例5面试题批量创建账户[rootserver ~]# vim for6.sh #!/bin/bash for ((i1;i10;i)) do if ! id -u test$i /dev/null then useradd test$i echo 123456 | passwd --stdin test$i /dev/null else echo test$i 已存在! fi done注意i表示置后自增i置前自增[rootserver ~]# x1 [rootserver ~]# y2 [rootserver ~]# echo $((x)) 1 [rootserver ~]# echo $x 2 [rootserver ~]# echo $((y)) 3 [rootserver ~]# echo $y 3while循环格式初始化 while expression do statement1 statement1 …… 步长 done上例语法中expression表示条件表达式循环必须满足该表达式否则循环结束可以是任何符合语法的shell命令但推荐书写测试表达式while循环读取文件# 法1 采用exec读取文件 [rootserver ~]# vim file1.txt data1 data2 data3 data4 data5 [rootserver ~]# vim while1.sh #!/bin/bash exec file1.txt while read a do echo $a done # exec file1.txt 将file.txt文件内容读入到标准输入中 # read a 表示从标准输入中按照行读取内容到变量a# 法2使用cat读取文件通过管道符进入while循环处理 cat 文件名 | while read line do statement1 statement1 …… done#法3 在while循环尾部使用输入重定向指定输入数据 while read line do statement1 statement1 …… done 文件名while语句示例例1猜商品价格通过变量RANDOM获取随机整数并控制20以内提示用户猜测商品价格猜测时需要记录次数猜中后退出程序[rootserver ~]# vim while2.sh #!/bin/bash # RANDOM是一个环境变量产生[1,32767]的整数对20取余是控制随机数在1-19间 PRICE$((RANDOM%20)) count0 while true # 恒真执行 do read -p 请输入[1-20]间的商品价格: INT ((count)) if (($INT$PRICE)) then echo 恭喜你猜对了 echo 你使用了$count次机会. break # 跳出while循环 elif (($INT$PRICE)) then echo 你输入的商品价格高了. else echo 你输入的商品价格低了. fi done 例2将例5的账户批量删除[rootserver ~]# vim while3.sh #!/bin/bash i1 while ((i10)) do if id -u test$i /dev/null then userdel -r test$i else echo test$i 账户已删除 fi ((i)) # 注意步长不能遗忘 doneuntil循环作用until循环不断地重复执行循环中的语句直至某个条件成立与while循环的执行条件相反until expreession do statement1 statement2 …… done示例# while3.sh 的改写 #!/bin/bash i1 until [ $i -gt 10 ] # i10 注意条件要相反 do if id -u test$i /dev/null then userdel -r test$i else echo test$i 账户已删除 fi let i done循环控制组成break: 是立即跳出某个循环结构。break语句可以用在for、while或者until等循环语句的循环体中continue: 作用不是退出循环体。而是跳过当前循环体中该语句后面的语句重新从循环语句开始的位置执行详细语法示例例1判断素数[rootserver ~]# vim for7.sh #!/bin/bash read -p 请输入一个正整数: prime if ((prime2)) then echo 数据错误请输入大于等于2的正整数. else for ((i2;i$prime;i)) do if (($prime%i0)) then echo 平数 break fi done if (($primei)) then echo 素数 fi fi例2计算100到150之间不能被3整除的数输出时一行显示5个#!/bin/bash count0 for ((i100;i150;i)) do if ((i%30)) then continue fi echo -ne $i\t ((count)) if ((count%50)) then echo fi done echo循环嵌套例1打印九九乘法表#!/bin/bash for i in seq 9 do for j in seq 9 do ((ji)) echo -ne $i*$jecho $((i*j))\t done echo done例2打印数字三角形#!/bin/bash for ((i1;i10;i)) # 打印9行 do for ((j1;j10-i;j)) # 每一行打印一些空格 do echo -n done for ((k1;ki;k)) # 每一行打印一些数字 do echo -n $i done echo # 打印一个\n done第六章 文本搜索工具--grep作用grep是linux中一种强大的文件搜索过滤工具可以按照正则表达式检索文件内容并把匹配的结果显示到屏幕上匹配的内容会标红格式grep -参数 条件 文件名参数-i 忽略大小写 -c 统计匹配的行数 -v 取反不显示匹配的行 -w 匹配单词 -E 等价于egrep即启用扩展正则表达式 -n 显示行号 -rl 将指定目录内的文件打印 -A数字 匹配行及以下n行 -B数字 匹配行及以上n行 -C数字 匹配行及上下n行 -q 静默模式没有任何内容输出使用$?来判断是否执行成功 -o 只显示匹配的内容注意工作方式grep可以在一个或多个文件中检索如果条件中包含了空格需要使用双引号引起工作结果如果搜索成功则返回0状态码否则为1状态码示例操作对象文件/etc/passwd作用记录账户的信息共分为7段使用冒号分割意义账户名密码代号xUID:GID注释家目录登录的Shell注意最后一段/sbin/nologin表述不允许登录grep过滤命令示例[rootserver ~]# grep -n root /etc/passwd [rootserver ~]# grep -n sshd /etc/passwd [rootserver ~]# grep -n /sbin/nologin /etc/passwd [rootserver ~]# grep -c /bin/bash /etc/passwd [rootserver ~]# grep /bin/bash /etc/passwd | wc -l [rootserver ~]# grep -nv /sbin/nologin /etc/passwd [rootserver ~]# grep -ni d /etc/passwd [rootserver ~]# grep -nA2 ftp /etc/passwd [rootserver ~]# grep -nB3 ftp /etc/passwd [rootserver ~]# grep -nC3 ftp /etc/passwd