[toc]

课程目标

  • 掌握for循环语句的基本语法结构
  • 掌握while和until循环语句的基本语法结构
  • 能会使用RANDOM产生随机数
  • 理解嵌套循环

一、随机数

关键词:一切都是未知数,永远不知道明天会抽什么风嵌套循环+随机数及综合案例 - 图1嵌套循环+随机数及综合案例 - 图2

1. 如何生成随机数?

系统变量RANDOM,默认会产生0~32767的随机整数

前言:要想调用变量,不管你是什么变量都要给钱,而且是美元嵌套循环+随机数及综合案例 - 图3

  1. 打印一个随机数
  2. echo $RANDOM
  3. 查看系统上一次生成的随机数
  4. # set|grep RANDOM
  5. RANDOM=28325
  6. 产生0~1之间的随机数
  7. echo $[$RANDOM%2]
  8. 产生0~2之间的随机数
  9. echo $[$RANDOM%3]
  10. 产生0~3之间的随机数
  11. echo $[$RANDOM%4]
  12. 产生0~9内的随机数
  13. echo $[$RANDOM%10]
  14. 产生0~100内的随机数
  15. echo $[$RANDOM%101]
  16. 产生50-100之内的随机数
  17. echo $[$RANDOM%51+50]
  18. 产生三位数的随机数
  19. echo $[$RANDOM%900+100]

2. 实战案例

㈠ 随机产生以139开头的电话号码

具体需求1:

写一个脚本,产生一个phonenum.txt文件,随机产生以139开头的手机号1000个,每个一行。

① 思路

  1. 产生1000个电话号码,脚本需要循环1000次 FOR WHILE UNTIL
  2. 139+8位,后8位随机产生,可以让每一位数字都随机产生 echo $[$RANDOM%10]
  3. 将随机产生的数字分别保存到变量里,然后加上139保存到文件里

② 落地实现

  1. #!/bin/env bash
  2. #产生1000个以139开头的电话号码并保存文件phonenum.txt
  3. file=/shell03/phonenum.txt
  4. for ((i=1;i<=1000;i++))
  5. do
  6. n1=$[$RANDOM%10]
  7. n2=$[$RANDOM%10]
  8. n3=$[$RANDOM%10]
  9. n4=$[$RANDOM%10]
  10. n5=$[$RANDOM%10]
  11. n6=$[$RANDOM%10]
  12. n7=$[$RANDOM%10]
  13. n8=$[$RANDOM%10]
  14. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> $file
  15. done
  16. #!/bin/bash
  17. # random phonenum
  18. # 循环1000次产生电话号码并保存到文件
  19. for i in {1..1000}
  20. do
  21. n1=$[RANDOM%10]
  22. n2=$[RANDOM%10]
  23. n3=$[RANDOM%10]
  24. n4=$[RANDOM%10]
  25. n5=$[RANDOM%10]
  26. n6=$[RANDOM%10]
  27. n7=$[RANDOM%10]
  28. n8=$[RANDOM%10]
  29. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt
  30. done
  31. #!/bin/bash
  32. i=1
  33. while [ $i -le 1000 ]
  34. do
  35. n1=$[$RANDOM%10]
  36. n2=$[$RANDOM%10]
  37. n3=$[$RANDOM%10]
  38. n4=$[$RANDOM%10]
  39. n5=$[$RANDOM%10]
  40. n6=$[$RANDOM%10]
  41. n7=$[$RANDOM%10]
  42. n8=$[$RANDOM%10]
  43. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt
  44. let i++
  45. done
  46. continue:继续,跳过本次循环,执行下一次循环
  47. break:打断,执行循环体外的代码do..done
  48. exit:退出程序
  49. #!/bin/bash
  50. for i in {1..1000}
  51. do
  52. n1=$[$RANDOM%10]
  53. n2=$[$RANDOM%10]
  54. n3=$[$RANDOM%10]
  55. n4=$[$RANDOM%10]
  56. n5=$[$RANDOM%10]
  57. n6=$[$RANDOM%10]
  58. n7=$[$RANDOM%10]
  59. n8=$[$RANDOM%10]
  60. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> phonenum.txt
  61. done
  62. #!/bin/bash
  63. #create phone num file
  64. for ((i=1;i<=1000;i++))
  65. do
  66. n1=$[$RANDOM%10]
  67. n2=$[$RANDOM%10]
  68. n3=$[$RANDOM%10]
  69. n4=$[$RANDOM%10]
  70. n5=$[$RANDOM%10]
  71. n6=$[$RANDOM%10]
  72. n7=$[$RANDOM%10]
  73. n8=$[$RANDOM%10]
  74. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" |tee -a phonenum.txt
  75. done
  76. #!/bin/bash
  77. count=0
  78. while true
  79. do
  80. n1=$[$RANDOM%10]
  81. n2=$[$RANDOM%10]
  82. n3=$[$RANDOM%10]
  83. n4=$[$RANDOM%10]
  84. n5=$[$RANDOM%10]
  85. n6=$[$RANDOM%10]
  86. n7=$[$RANDOM%10]
  87. n8=$[$RANDOM%10]
  88. echo "139$n1$n2$n3$n4$n5$n6$n7$n8" |tee -a phonenum.txt && let count++
  89. if [ $count -eq 1000 ];then
  90. break
  91. fi
  92. done

㈡ 随机抽出5位幸运观众

具体需求:

  1. 在上面的1000个手机号里抽奖5个幸运观众,显示出这5个幸运观众。
  2. 但只显示头3个数和尾号的4个数,中间的都用*代替

① 思路

  1. 确定幸运观众所在的行 0-1000 随机找出一个数字 $[$RANDOM%1000+1]
  2. 将电话号码提取出来 head -随机产生行号 phonenum.txt |tail -1
  3. 显示前3个和后4个数到屏幕 echo 139****

② 落地实现

  1. #!/bin/bash
  2. #定义变量
  3. phone=/shell03/phonenum.txt
  4. #循环抽出5位幸运观众
  5. for ((i=1;i<=5;i++))
  6. do
  7. #定位幸运观众所在行号
  8. line=`wc -l $phone |cut -d' ' -f1`
  9. luck_line=$[RANDOM%$line+1]
  10. #取出幸运观众所在行的电话号码
  11. luck_num=`head -$luck_line $phone|tail -1`
  12. #显示到屏幕
  13. echo "139****${luck_num:7:4}"
  14. echo $luck_num >> luck.txt
  15. #删除已经被抽取的幸运观众号码
  16. #sed -i "/$luck_num/d" $phone
  17. done
  18. #!/bin/bash
  19. file=/shell04/phonenum.txt
  20. for i in {1..5}
  21. do
  22. file_num=`wc -l $file |cut -d' ' -f1`
  23. line=`echo $[$RANDOM%$file_num+1]`
  24. luck=`head -n $line $file|tail -1`
  25. echo "139****${luck:7:4}" && echo $luck >> /shell04/luck_num.txt
  26. done
  27. #!/bin/bash
  28. for ((i=1;i<=5;i++))
  29. do
  30. file=phonenum.txt
  31. line=`cat phonenum.txt |wc -l` 1000
  32. luckline=$[$RANDOM%$line+1]
  33. phone=`cat $file|head -$luckline|tail -1`
  34. echo "幸运观众为:139****${phone:7:4}"
  35. done
  36. 或者
  37. #!/bin/bash
  38. # choujiang
  39. phone=phonenum.txt
  40. for ((i=1;i<=5;i++))
  41. do
  42. num=`wc -l phonenum.txt |cut -d' ' -f1`
  43. line=`echo $[$RANDOM%$num+1]`
  44. luck=`head -$line $phone |tail -1`
  45. sed -i "/$luck/d" $phone
  46. echo "幸运观众是:139****${luck:7:4}"
  47. done

㈢ 批量创建用户(密码随机产生)

需求:批量创建5个用户,每个用户的密码为一个随机数

① 思路

  1. 循环5次创建用户
  2. 产生一个密码文件来保存用户的随机密码
  3. 从密码文件中取出随机密码赋值给用户

② 落地实现

  1. #!/bin/bash
  2. #crate user and set passwd
  3. #产生一个保存用户名和密码的文件
  4. echo user0{1..5}:itcast$[$RANDOM%9000+1000]#@~|tr ' ' '\n'>> user_pass.file
  5. #循环创建5个用户
  6. for ((i=1;i<=5;i++))
  7. do
  8. user=`head -$i user_pass.file|tail -1|cut -d: -f1`
  9. pass=`head -$i user_pass.file|tail -1|cut -d: -f2`
  10. useradd $user
  11. echo $pass|passwd --stdin $user
  12. done
  13. 或者
  14. for i in `cat user_pass.file`
  15. do
  16. user=`echo $i|cut -d: -f1`
  17. pass=`echo $i|cut -d: -f2`
  18. useradd $user
  19. echo $pass|passwd --stdin $user
  20. done
  21. #!/bin/bash
  22. #crate user and set passwd
  23. #产生一个保存用户名和密码的文件
  24. echo user0{1..3}:itcast$[$RANDOM%9000+1000]#@~|tr ' ' '\n'|tr ':' ' ' >> user_pass.file
  25. #循环创建5个用户
  26. while read user pass
  27. do
  28. useradd $user
  29. echo $pass|passwd --stdin $user
  30. done < user_pass.file
  31. pwgen工具产生随机密码:
  32. [root@server shell04]# pwgen -cn1 12
  33. Meep5ob1aesa
  34. [root@server shell04]# echo user0{1..3}:$(pwgen -cn1 12)
  35. user01:Bahqu9haipho user02:Feiphoh7moo4 user03:eilahj5eth2R
  36. [root@server shell04]# echo user0{1..3}:$(pwgen -cn1 12)|tr ' ' '\n'
  37. user01:eiwaShuZo5hi
  38. user02:eiDeih7aim9k
  39. user03:aeBahwien8co

二、嵌套循环

关键字:大圈套小圈

嵌套循环+随机数及综合案例 - 图4时钟:分针与秒针,秒针转⼀圈(60格),分针转1格。循环嵌套就是外层循环⼀次,内层循环⼀轮。

  1. 一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。
  2. 每次外部循环都会触发内部循环,直至内部循环完成,才接着执行下一次的外部循环。
  3. for循环、while循环和until循环可以相互嵌套。

1. 应用案例

㈠ 打印指定图案

  1. 1
  2. 12
  3. 123
  4. 1234
  5. 12345
  6. 5
  7. 54
  8. 543
  9. 5432
  10. 54321

㈡ 落地实现1

  1. X轴:
  2. for ((i=1;i<=5;i++));do echo -n $i;done
  3. Y轴:
  4. 负责打印换行
  5. #!/bin/bash
  6. for ((y=1;y<=5;y++))
  7. do
  8. for ((x=1;x<=$y;x++))
  9. do
  10. echo -n $x
  11. done
  12. echo
  13. done
  14. #!/bin/bash
  15. for ((y=1;y<=5;y++))
  16. do
  17. x=1
  18. while [ $x -le $y ]
  19. do
  20. echo -n $x
  21. let x++
  22. done
  23. echo
  24. done

㈢ 落地实现2

  1. Y轴:打印换行
  2. X轴:打印数字 5-1
  3. #!/bin/bash
  4. y=5
  5. while (( $y >= 1 ))
  6. do
  7. for ((x=5;x>=$y;x--))
  8. do
  9. echo -n $x
  10. done
  11. echo
  12. let y--
  13. done
  14. #!/bin/bash
  15. for (( y=5;y>=1;y--))
  16. do
  17. for (( x=5;x>=$y;x--))
  18. do
  19. echo -n $x
  20. done
  21. echo
  22. done
  23. #!/bin/bash
  24. y=5
  25. while [ $y -ge 1 ]
  26. do
  27. for ((x=5;x>=$y;x--))
  28. do
  29. echo -n $x
  30. done
  31. echo
  32. let y--
  33. done
  34. #!/bin/bash
  35. y=1
  36. until (( $y >5 ))
  37. do
  38. x=1
  39. while (( $x <= $y ))
  40. do
  41. echo -n $[6-$x]
  42. let x++
  43. done
  44. echo
  45. let y++
  46. done
  47. 课后打印:
  48. 54321
  49. 5432
  50. 543
  51. 54
  52. 5

2. 课堂练习

打印九九乘法表(三种方法)

  1. 1*1=1
  2. 1*2=2 2*2=4
  3. 1*3=3 2*3=6 3*3=9
  4. 1*4=4 2*4=8 3*4=12 4*4=16
  5. 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
  6. 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
  7. 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
  8. 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
  9. 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
  10. Y轴:循环9次,打印9行空行
  11. X轴:循环次数和Y轴相关;打印的是XY轴乘积 $[] $(())
  12. #!/bin/bash
  13. for ((y=1;y<=9;y++))
  14. do
  15. for ((x=1;x<=$y;x++))
  16. do
  17. echo -ne "$x*$y=$[$x*$y]\t"
  18. done
  19. echo
  20. echo
  21. done
  22. #!/bin/bash
  23. y=1
  24. while [ $y -le 9 ]
  25. do
  26. x=1
  27. while [ $x -le $y ]
  28. do
  29. echo -ne "$x*$y=$[$x*$y]\t"
  30. let x++
  31. done
  32. echo
  33. echo
  34. let y++
  35. done
  36. 或者
  37. #!/bin/bash
  38. for i in `seq 9`
  39. do
  40. for j in `seq $i`
  41. do
  42. echo -ne "$j*$i=$[$i*$j]\t"
  43. done
  44. echo
  45. echo
  46. done
  47. 或者
  48. #!/bin/bash
  49. y=1
  50. until [ $y -gt 9 ]
  51. do
  52. x=1
  53. until [ $x -gt $y ]
  54. do
  55. echo -ne "$x*$y=$[ $x*$y ]\t"
  56. let x++
  57. done
  58. echo
  59. echo
  60. let y++
  61. done

三、阶段性补充总结

1、变量定义

2. 流程控制语句

3. 循环语句

4. 影响shell程序的内置命令

  1. exit 退出整个程序
  2. break 结束当前循环,或跳出本层循环
  3. continue 忽略本次循环剩余的代码,直接进行下一次循环
  4. shift 使位置参数向左移动,默认移动1位,可以使用shift 2
  5. :
  6. true
  7. false

举例说明:

  1. 以下脚本都能够实现用户自定义输入数字,然后脚本计算和:
  2. [root@MissHou shell04]# cat shift.sh
  3. #!/bin/bash
  4. sum=0
  5. while [ $# -ne 0 ]
  6. do
  7. let sum=$sum+$1
  8. shift
  9. done
  10. echo sum=$sum
  11. [root@MissHou shell04]# cat for3.sh
  12. #!/bin/bash
  13. sum=0
  14. for i
  15. do
  16. let sum=$sum+$i
  17. done
  18. echo sum=$sum

4. 补充扩展expect

expect 自动应答 tcl语言

需求1:A远程登录到server上什么都不做

  1. #!/usr/bin/expect
  2. # 开启一个程序
  3. spawn ssh root@10.1.1.1
  4. # 捕获相关内容
  5. expect {
  6. "(yes/no)?" { send "yes\r";exp_continue }
  7. "password:" { send "123456\r" }
  8. }
  9. interact //交互
  10. 脚本执行方式:
  11. # ./expect1.sh
  12. # /shell04/expect1.sh
  13. # expect -f expect1.sh
  14. 1)定义变量
  15. #!/usr/bin/expect
  16. set ip 10.1.1.2
  17. set pass 123456
  18. set timeout 5
  19. spawn ssh root@$ip
  20. expect {
  21. "yes/no" { send "yes\r";exp_continue }
  22. "password:" { send "$pass\r" }
  23. }
  24. interact
  25. 2)使用位置参数
  26. #!/usr/bin/expect
  27. set ip [ lindex $argv 0 ]
  28. set pass [ lindex $argv 1 ]
  29. set timeout 5
  30. spawn ssh root@$ip
  31. expect {
  32. "yes/no" { send "yes\r";exp_continue }
  33. "password:" { send "$pass\r" }
  34. }
  35. interact

需求2:A远程登录到server上操作

  1. #!/usr/bin/expect
  2. set ip 10.1.1.1
  3. set pass 123456
  4. set timeout 5
  5. spawn ssh root@$ip
  6. expect {
  7. "yes/no" { send "yes\r";exp_continue }
  8. "password:" { send "$pass\r" }
  9. }
  10. expect "#"
  11. send "rm -rf /tmp/*\r"
  12. send "touch /tmp/file{1..3}\r"
  13. send "date\r"
  14. send "exit\r"
  15. expect eof

需求3:shell脚本和expect结合使用,在多台服务器上创建1个用户

  1. [root@server shell04]# cat ip.txt
  2. 10.1.1.1 123456
  3. 10.1.1.2 123456
  4. 1. 循环
  5. 2. 登录远程主机——>ssh——>从ip.txt文件里获取IP和密码分别赋值给两个变量
  6. 3. 使用expect程序来解决交互问题
  7. #!/bin/bash
  8. # 循环在指定的服务器上创建用户和文件
  9. while read ip pass
  10. do
  11. /usr/bin/expect <<-END &>/dev/null
  12. spawn ssh root@$ip
  13. expect {
  14. "yes/no" { send "yes\r";exp_continue }
  15. "password:" { send "$pass\r" }
  16. }
  17. expect "#" { send "useradd yy1;rm -rf /tmp/*;exit\r" }
  18. expect eof
  19. END
  20. done < ip.txt
  21. #!/bin/bash
  22. cat ip.txt|while read ip pass
  23. do
  24. {
  25. /usr/bin/expect <<-HOU
  26. spawn ssh root@$ip
  27. expect {
  28. "yes/no" { send "yes\r";exp_continue }
  29. "password:" { send "$pass\r" }
  30. }
  31. expect "#"
  32. send "hostname\r"
  33. send "exit\r"
  34. expect eof
  35. HOU
  36. }&
  37. done
  38. wait
  39. echo "user is ok...."
  40. 或者
  41. #!/bin/bash
  42. while read ip pass
  43. do
  44. {
  45. /usr/bin/expect <<-HOU
  46. spawn ssh root@$ip
  47. expect {
  48. "yes/no" { send "yes\r";exp_continue }
  49. "password:" { send "$pass\r" }
  50. }
  51. expect "#"
  52. send "hostname\r"
  53. send "exit\r"
  54. expect eof
  55. HOU
  56. }&
  57. done<ip.txt
  58. wait
  59. echo "user is ok...."

四、综合案例

1. 实战案例1

㈠ 具体需求

写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上

说明:主机和密码文件已经提供

10.1.1.1:123456

10.1.1.2:123456

㈡ 案例分析

  • 关闭防火墙和selinux
  • 判断ssh服务是否开启(默认ok)
  • 循环判断给定密码文件里的哪些IP是可以ping通
  • 判断IP是否可以ping通——>$?—>流程控制语句
  • 密码文件里获取主机的IP和密码保存变量
  • 判断公钥是否存在—>不存在创建它
  • ssh-copy-id 将跳板机上的yunwei用户的公钥推送到远程主机—>expect解决交互
  • 将ping通的主机IP单独保存到一个文件
  • 测试验证

㈢ 落地实现

① 代码拆分

  1. 1.判断yunwei用户的公钥是否存在
  2. [ ! -f /hoem/yunwei/.ssh/id_rsa ] && ssh-keygen -P '' -f ./id_rsa
  3. 2.获取IP并且判断是否可以ping
  4. 1)主机密码文件ip.txt
  5. 10.1.1.1:123456
  6. 10.1.1.2:123456
  7. 2) 循环判断主机是否ping
  8. tr ':' ' ' < ip.txt|while read ip pass
  9. do
  10. ping -c1 $ip &>/dev/null
  11. if [ $? -eq 0 ];then
  12. 推送公钥
  13. fi
  14. done
  15. 3.非交互式推送公钥
  16. /usr/bin/expect <<-END &>/dev/null
  17. spawn ssh-copy-id root@$ip
  18. expect {
  19. "yes/no" { send "yes\r";exp_continue }
  20. "password:" { send "$pass\r" }
  21. }
  22. expect eof
  23. END

② 最终实现

  1. 环境准备
  1. jumper-server yunwei用户
  2. yunwei用户sudo授权:
  3. visudo
  4. ## Allow root to run any commands anywhere
  5. root ALL=(ALL) ALL
  6. yunwei ALL=(root) NOPASSWD:ALL,!/sbin/shutdown,!/sbin/init,!/bin/rm -rf /
  7. 解释说明:
  8. 1)第一个字段yunwei指定的是用户:可以是用户名,也可以是别名。每个用户设置一行,多个用户设置多行,也可以将多个用户设置成一个别名后再进行设置。
  9. 2)第二个字段ALL指定的是用户所在的主机:可以是ip,也可以是主机名,表示该sudo设置只在该主机上生效,ALL表示在所有主机上都生效!限制的一般都是本机,也就是限制使用这个文件的主机;一般都指定为"ALL"表示所有的主机,不管文件拷到那里都可以用。比如:10.1.1.1=...则表示只在当前主机生效。
  10. 3)第三个字段(root)括号里指定的也是用户:指定以什么用户身份执行sudo,即使用sudo后可以享有所有root账号下的权限。如果要排除个别用户,可以在括号内设置,比如ALL=(ALL,!oracle,!pos)。
  11. 4)第四个字段ALL指定的是执行的命令:即使用sudo后可以执行所有的命令。除了关机和删除根内容以外;也可以设置别名。NOPASSWD: ALL表示使用sudo的不需要输入密码。
  12. 5)也可以授权给一个用户组
  13. %admin ALL=(ALL) ALL 表示admin组里的所有成员可以在任何主机上以任何用户身份执行任何命令
  1. 脚本实现
  1. #!/bin/bash
  2. #判断公钥是否存在
  3. [ ! -f /home/yunwei/.ssh/id_rsa ] && ssh-keygen -P '' -f ~/.ssh/id_rsa
  4. #循环判断主机是否ping通,如果ping通推送公钥
  5. tr ':' ' ' < /shell04/ip.txt|while read ip pass
  6. do
  7. {
  8. ping -c1 $ip &>/dev/null
  9. if [ $? -eq 0 ];then
  10. echo $ip >> ~/ip_up.txt
  11. /usr/bin/expect <<-END &>/dev/null
  12. spawn ssh-copy-id root@$ip
  13. expect {
  14. "yes/no" { send "yes\r";exp_continue }
  15. "password:" { send "$pass\r" }
  16. }
  17. expect eof
  18. END
  19. fi
  20. }&
  21. done
  22. wait
  23. echo "公钥已经推送完毕,正在测试...."
  24. #测试验证
  25. remote_ip=`tail -1 ~/ip_up.txt`
  26. ssh root@$remote_ip hostname &>/dev/null
  27. test $? -eq 0 && echo "公钥成功推送完毕"

2. 实战案例2

写一个脚本,统计web服务的不同连接状态个数

  1. #!/bin/bash
  2. #count_http_80_state
  3. #统计每个状态的个数
  4. declare -A array1
  5. states=`ss -ant|grep 80|cut -d' ' -f1`
  6. for i in $states
  7. do
  8. let array1[$i]++
  9. done
  10. #通过遍历数组里的索引和元素打印出来
  11. for j in ${!array1[@]}
  12. do
  13. echo $j:${array1[$j]}
  14. done

五、课后实战

1、将/etc/passwd里的用户名分类,分为管理员用户,系统用户,普通用户。
2、写一个倒计时脚本,要求显示离2019年1月1日(元旦)的凌晨0点,还有多少天,多少时,多少分,多少秒。
3、写一个脚本把一个目录内的所有空文件都删除,最后输出删除的文件的个数。