shell编程之循环语句 for

745 阅读3分钟

1 什么是循环

凡是重复执行一段代码, 都可以称之为循环。 循环体中三种专业的话术:遍历、迭代、递归 。

  • 遍历(traversal) - 按规则访问非线性结构中的每一项。
  • 迭代(iterate) - 按顺序访问线性结构中的每一项。
  • 递归(recursion) - 在函数内调用自身, 将复杂情况逐步转化成基本情况。

通常有进入循环的条件和退出循环的条件。

循环次数分为两种:

  • 循环次数事先已知
  • 循环次数事先未知

常见的循环命令:for, while, until。

2 for循环语句

用法:

  • 读取不同的变量值,用来逐个执行同一组命令。
  • 使用场景:一般用于循环次数事先已知的场景。
  • 执行机制:遍历。

格式:

 for 变量名 in 取值列表
 do
    命令序列
 done
复制代码

执行机制:

 1.依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束。
 2.如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"
复制代码

1.png

2.1 for语句的作用演示

1、打印1到10的三种方法:

 #方法一:
 [root@yuji ~]# for i in {1..10}
 > do
 > echo $i
 > done
 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 ​
 #方法二:
 [root@yuji ~]# for i in $(seq 1 10)
 > do
 > echo $i
 > done
 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 ​
 #方法三:
 [root@yuji ~]# for ((i=1;i<=10;i++))
 > do
 > echo $i
 > done
 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
复制代码

2.png

3.png

4.png

2、打印1到10之间的奇数:

 [root@yuji ~]# for i in {1..10..2};do echo $i;done   //分号可以在一行中执行多条命令
 1
 3
 5
 7
 9
 [root@yuji ~]# for i in $(seq 1 2 10);do echo $i;done
 1
 3
 5
 7
 9
 [root@yuji ~]# for ((i=1;i<=10;i+=2));do echo $i;done
 1
 3
 5
 7
 9
复制代码

5.png

3、打印1到10之间的偶数:

 [root@yuji ~]# for i in {2..10..2}; do echo $i; done
 2
 4
 6
 8
 10
 [root@yuji ~]# for i in $(seq 2 2 10);do echo $i;done
 2
 4
 6
 8
 10
 [root@yuji ~]# for ((i=2;i<=10;i+=2));do echo $i;done
 2
 4
 6
 8
 10
复制代码

6.png

2.2 for循环应用示例

示例1:求从1加到10的和。

创建脚本:

 [root@yuji ~]# vim for1.sh      //创建脚本
 #!/bin/bash
 #求从1加到10的和。
 ​
 sum=0
 for i in {1..10}
 do
     sum=$[sum+i]
 done
 echo "求和的结果是$sum"
 ​
 [root@yuji ~]# bash for1.sh     //执行脚本
 求和的结果是55
复制代码

7 求和1.png

7 求和2.png

示例2:批量添加用户

要求:

  1. 用户名存放在users.txt文件中,每行一个
  2. 初始密码均设为123456

创建脚本:

 [root@yuji ~]# vim users.txt
 [root@yuji ~]# cat users.txt
 liyi
 lisan
 liwu
 liliu
 ​
 [root@yuji ~]# vim for2.sh             //创建脚本
 #!/bin/bash
 #批量添加用户,用户名存在放users.txt文件中
 #初始密码123456
 ​
 for user in $(cat /root/users.txt)
 do
    useradd $user
    echo "123456" | passwd --stdin $user
 done
 ​
 [root@yuji ~]# bash for2.sh             //执行脚本
 更改用户 liyi 的密码 。
 passwd:所有的身份验证令牌已经成功更新。
 更改用户 lisan 的密码 。
 passwd:所有的身份验证令牌已经成功更新。
 更改用户 liwu 的密码 。
 passwd:所有的身份验证令牌已经成功更新。
 更改用户 liliu 的密码 。
 passwd:所有的身份验证令牌已经成功更新。
复制代码

8 创建用户1.png

8 创建用户2.png

8 创建用户3.png

示例3:根据IP地址检查主机状态

要求:

  1. IP地址存放在ips.txt文件中,每行一个
  2. 使用ping命令检测各主机的连通性

创建脚本:

 [root@yuji /data]# vim ips.txt
 [root@yuji /data]# cat ips.txt
 192.168.72.10
 192.168.72.22
 192.168.72.129
 192.168.72.200
 ​
 [root@yuji ~]# vim for3.sh       //写脚本
 #!/bin/bash
 #IP地址存放在ips.txt文件中,每行一个
 #使用ping命令检测各主机的连通性
 ​
 for host in $(cat /data/ips.txt)
 do
    ping -c 3 -i 0.5 -w 4 $host &> /dev/null
    if [ $? -eq 0 ]
    then
        echo "$host is up"
    else
        echo "$host is down"
    fi
 done
 ​
 [root@yuji ~]# bash for3.sh     //运行脚本
 192.168.72.10 is down
 192.168.72.22 is down
 192.168.72.129 is down
 192.168.72.200 is down
复制代码

9 主机1.png

9 主机2.png

9 主机3.png

示例4:生成8位随机密码(使用字符串分片的方法)

实验要求:生成8位随机密码,密码范围为26个大小写英文字母和10个阿拉伯数字。

实验思路:

  1. 先定义密码取值范围:26个大小写英文字母和10个阿拉伯数字
  2. 每次取1个字符。
  3. 循环取8次。

实验步骤:

1、字符串分片(截取字符串)

 #获取字符串长度
 ${#变量名}
 ​
 #字符串下标值
 字符串内下标值从0开始,0到字符串长度减1。
 如果字符串长度为8,那么下标值就是 07。
 ​
 #截取字符串
 ${变量名:开始的下标值:截取长度}
 ​
 #字符串追加
 变量一+=变量二
 ​
 # 示例:
 i="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 字符串长度:${#i}          //此时长度为62
 字符串下标值:0-61
 截取第一个字符:${i:0:1}  
 截取前三个字符:${i:0:3}
 截取最后一个字符:${i:61:1}
复制代码

2、生成随机数

 #变量RANDOM的取值范围是:0-32767
 ​
 #生成 0-61 之间的随机数
 $[RANDOM % 62]
 ​
 #生成符串长度以内的随机数(即 0-字符串长度减1)
 $[RANDOM % ${#i}]
 ​
 #随机截取字符串内任意一个字符
 num=$[RANDOM % ${#i}]      //生成字符串长度以内的随机数
 var=${i:$num:1}            //随机截取字符串内任意一个字符
复制代码

3、示例代码

 [root@yuji ~]# vim passran.sh
 #!/bin/bash
 # 要求生成8位随机密码,密码范围为26个大小写英文字母和10个阿拉伯数字。
 # ---------------------
 ​
 #定义密码取值范围
 string="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 ​
 #循环8次
 for i in {1..8}
 do
    #每次循环随机获取0到字符串长度减1的下标值
    num=$[RANDOM % ${#string}]
    #进行字符串截取,每次随机取出1个字符
    var=${string:$num:1}
    #每次追加生成的密码,passwd+=$var等同于passwd=$passwd$var
    passwd+=$var
 done
 echo "本次生成的密码为:$passwd"
复制代码

实验7 密码.png

实验7 随机密码2.png

3 多线程执行脚本(并行执行)

普通for循环是串行,一个一个执行。

在do和done中间增加 { } 符号,可以多线程同时执行。

多线程案例演示:

  • ping 192.168.72.0/24网段内的所有主机。
 [root@yuji ~]# vim for4.sh
 #!/bin/bash
 #多线程,同时ping多台主机
 ​
 for ip in 192.168.72.{1..254}
 do
   {
   ping -c3 -w2 $ip &>/dev/null
   if [ $? -eq 0 ]
   then
       echo "$ip is up" >> /opt/up.txt
   else
       echo "$ip is down" >> /opt/down.txt
   fi
   }&
 #{ }表示多线程同时执行
 #增加 & 符号,表示将进程放到后台运行,后台命令之间是不区分
 先来后到关系的
 done
 #wait的作用是等待上面所有后台进程都执行完毕后,才会去执行后
 面的语句。如果没有wait,后面的语句是不会等待后台进程的,会>造成一些需要有上下文关系的程序执行错误。
 wait
 cat /opt/up.txt
 cat /opt/down.txt
复制代码

8.png

4 for的三个默认分隔符

环境变量$IFS,是指内部字段分隔符, 默认为三个:空格、制表符 \t、换行符 \n。

使用set命令查看所有变量:

1.png

4.1 分隔符的作用演示:

示例:使用for读取文件内容,文件中有空格

演示:

文件中只有3行内容,使用for语句读取却显示为5行。

 [root@yuji sh]# vim fen1.sh     //写脚本
 #!/bin/bash
 ​
 #从文件中获取变量值,$()表示调用命令执行的结果
 for name in $(cat name.txt)
 do
    echo "$name"
 done
 ​
 [root@yuji sh]# cat name.txt    //查看文件内容,只有3行
 zhang san
 lisi
 wang wu
 [root@yuji sh]# bash fen1.sh    //运行脚本,显示5行内容
 zhang
 san
 lisi
 wang
 wu
复制代码

2.png

3.png

原因:

变量IFS定义的分隔符中有空格,for从文件中获取变量值时,以空格作为了分隔符,将"zhang san"分隔成了两行,"wang wu"也是如此。

解决方法:

在脚本中,先临时将变量IFS的值中的空格删除,使for语句不以空格作为分隔符,最后还原变量值。

 [root@yuji sh]# vim fen2.sh       //写脚本
 #!/bin/bash
 ​
 #借用一个临时变量先保存IFS的原始值
 IFS_OLD=$IFS
 #删除空格和制表符,只保留换行符作为分隔符
 IFS=$'\n'
 ​
 #从文件中获取变量值,$()表示调用命令执行的结果
 for name in $(cat name.txt)
 do
    echo "$name"
 done
 ​
 #还原变量IFS的原始值
 IFS=$IFS_OLD
 ​
 [root@yuji sh]# cat name.txt    //查看文件内容,只有3行
 zhang san
 lisi
 wang wu
 [root@yuji sh]# bash fen2.sh     //运行脚本,显示3行
 zhang san
 lisi
 wang wu
复制代码

4.png

5.png