1 什么是循环
凡是重复执行一段代码, 都可以称之为循环。 循环体中三种专业的话术:遍历、迭代、递归 。
- 遍历(traversal) - 按规则访问非线性结构中的每一项。
- 迭代(iterate) - 按顺序访问线性结构中的每一项。
- 递归(recursion) - 在函数内调用自身, 将复杂情况逐步转化成基本情况。
通常有进入循环的条件和退出循环的条件。
循环次数分为两种:
- 循环次数事先已知
- 循环次数事先未知
常见的循环命令:for, while, until。
2 for循环语句
用法:
- 读取不同的变量值,用来逐个执行同一组命令。
- 使用场景:一般用于循环次数事先已知的场景。
- 执行机制:遍历。
格式:
for 变量名 in 取值列表
do
命令序列
done
复制代码
执行机制:
1.依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束。
2.如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@"
复制代码
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、打印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
复制代码
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
复制代码
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
复制代码
示例2:批量添加用户
要求:
- 用户名存放在users.txt文件中,每行一个
- 初始密码均设为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:所有的身份验证令牌已经成功更新。
复制代码
示例3:根据IP地址检查主机状态
要求:
- IP地址存放在ips.txt文件中,每行一个
- 使用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
复制代码
示例4:生成8位随机密码(使用字符串分片的方法)
实验要求:生成8位随机密码,密码范围为26个大小写英文字母和10个阿拉伯数字。
实验思路:
- 先定义密码取值范围:26个大小写英文字母和10个阿拉伯数字
- 每次取1个字符。
- 循环取8次。
实验步骤:
1、字符串分片(截取字符串)
#获取字符串长度
${#变量名}
#字符串下标值
字符串内下标值从0开始,0到字符串长度减1。
如果字符串长度为8,那么下标值就是 0到7。
#截取字符串
${变量名:开始的下标值:截取长度}
#字符串追加
变量一+=变量二
# 示例:
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"
复制代码
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
复制代码
4 for的三个默认分隔符
环境变量$IFS,是指内部字段分隔符, 默认为三个:空格、制表符 \t、换行符 \n。
使用set命令查看所有变量:
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
复制代码
原因:
变量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
复制代码