首页 > 编程笔记 > Linux笔记 阅读:2

Shell for循环用法详解(附带实例)

for 循环是固定循环,也就是在循环时已经知道需要进行几次循环。有时也把 for 循环称为计数循环。

for 循环的语法有两种,接下来分别做详细讲解。

带列表的for循环

for 变量 in 值1 值2 值3 ...
    do
        程序
    done
for 循环的次数取决于 in 后面值的个数(以空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。

也就是说,假设 in 后面有三个值,for 就会循环三次,第一次循环会把值 1 赋予变量,第二次循环会把值 2 赋予变量,以此类推,如下图所示:


图 1 带列表的for循环

举个简单的例子:
[root@localhost ~]# vim /root/for.sh
#!/bin/bash
for time in morning noon afternoon evening
    do
        echo "This time is $time!"
    done
解释一下脚本思路:因为 in 值后面有四个字符串,并且这些字符串外侧并未使用双引号将其表示为整体,所以 for 会循环四次。每次循环会依次把字符串赋予变量 time,因此这个脚本会循环四次,并依次输出 morning、noon、afternoon、evening 全部四个字符串。

脚本执行之后,结果如下:
[root@localhost ~]# /root/for.sh
This time is morning!
This time is noon!
This time is afternoon!
This time is evening!

上一个例子非常简单,但是没有什么实际应用价值,下面来写一个批量解压缩脚本。

如果我们有很多压缩文件,那么手工逐一解压缩是非常烦琐的,此时可以通过脚本来实现所有文件的解压缩。假设我们把所有的压缩包复制到 /lamp/ 目录中,那么批量解压缩脚本就应该这样写:
[root@localhost ~]# /root/auto-tar.sh
#!/bin/bash
# 批量解压缩脚本
cd /lamp
# 进入压缩包目录
ls /lamp/*.tar.* > ls.log
# 把所有文件名包含tar的文件以覆盖的方式写入ls.log临时文件
for i in $(cat ls.log)
# 读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i
    do
        tar -zxf $i &>/dev/null
        # 解压缩,并丢弃所有输出
    done
rm -rf /lamp/ls.log
# 删除临时文件 ls.log
for…in… 循环更加贴近于系统管理,如在批量解压缩这个脚本中,如果是固定循环,就要先数有多少个压缩文件,再决定循环多少次。一旦压缩文件的个数发生变化,整个脚本就都需要修改。而使用 for…in… 的方式,压缩文件的个数可以随意变化,不用修改脚本。另外,如果存在 .zip 格式的压缩文件,那么还可以再进行一次 ls/lamp/*.zi 查询,然后继续执行脚本 for 循环进行解压缩。

带类的for循环

for ((初始值; 循环控制条件; 变量变化))
    do
         程序
    done
for 循环的次数可以在循环开始前指定,因此,通常在执行未知次数循环时使用第一种语法,如果已知需要循环的次数就使用第二种语法。

在第二种语法中,可以通过指定初始值、循环控制条件、变量变化来确定循环执行次数,如下图所示:


图 2 类C的for循环

在第二种语法中需要注意以下几点:
举一个简单的例子,具体如下:
#!/bin/bash
for ((i=1; i<=5; i=i+1))
    do
        echo "$i"
    done
这是一个循环五次的示例,如下图所示:


图 3 循环五次实例

解释一下脚本思路:
1) 类 C 的 for 循环先执行步骤 ①,使用初始值和控制条件做对比,判断是否执行成立。如果不成立,就不执行 do 和 done 之间语句;如果成立,就执行 do 和 done 之间的 echo 语句(步骤 ②)。

2) 在执行完 do 和 done 之间的语句后,执行变量变化,也就是 i=i+1,(步骤 ③)。在执行完变量变化之后,使用经过变化的 i 和控制条件做比较(步骤 ④)。如果判断成立,就执行 do 和 done 之间的语句(步骤 ⑤);如果不成立,就退出 for 循环。

3) 由于步骤 ① 和步骤 ② 只在第一次循环时使用,因此使用虚线来表示。

再看一个实例:
#!/bin/bash
# 从1加到100
s=0
for ((i=1; i<=100; i=i+1))
# 定义循环100次
    do
        s=$(($s+$i))
        # 每次循环都给变量s赋值
    done
echo "The sum of 1+2+...+100 is : $s"
# 输出从1加到100的和
解释一下脚本思路:在这个例子中,请注意“(())”是 Bash 的数字运算格式,必须这样写,才能进行数值运算。

不过,上面的例子仍然和实际工作相距甚远,我们利用 for 固定循环来写一个批量添加用户的脚本。如果需要使用脚本批量添加普通用户,那么这些用户的用户名一定要遵守同一个规则,并且顺序添加,而且用户的初始密码也是一致的,脚本如下:
[root@localhost ~]# vim /root/useradd.sh
#!/bin/bash
# 批量添加指定数量的用户
read -t 30 -p "请输入要创建用户数量:" num1
# 通过read语句接收创建用户数量
read -t 30 -p "请输入要创建用户名:" name1
# 通过read语句接收创建用户名
read -t 30 -s -p "请输入要创建用户的默认密码:" pass1 && echo ""
# 通过read语句接收创建用户默认密码
# 在使用了-s隐藏用户输入后,通常存在不能正确换行的问题。利用echo默认换行的特点为其换行
if [[ $num1 =~ ^[0-9]+$ && $num1 != 0 && -n $name1 && -n $pass1 ]]
# 使用[[分别对用户数量、用户名、用户密码进行判断
then
    echo "各参数取值正常,准备创建用户"
    for ((i=1; i<=$num1; i=i+1))
    # 使用类C的for循环,循环次数通过变量$num1来确定
    do
        useradd ${name1}$i
        # 用户名称为“名称”+“数字编号”的组合
        echo "$pass1" | passwd --stdin ${name1}$i &> /dev/null
        # 在创建用户结束后,将用户输入的默认密码以非交互的方式为新用户设置密码
    done
else
    echo -e "\e[1;31m存在异常参数,请正确输入用户数量、用户名\e[0m"
    # 经过if判断,如果不成立,就说明用户输入的用户数量、用户名或用户密码存在问题,不创建用户
fi
解释一下脚本思路:
1) 脚本执行后,让用户自己来决定添加的用户数量、用户名及用户的初始密码。通过使用 read 命令输出提示信息来引导用户进行用户数量、用户名、用户密码的赋值,并把输入分别赋予 num_1、name_1、pass_1。

2) 接下来,使用 if 语句对用户的输入进行判断。先判断“创建用户数量”是否赋值,赋值字符串是不是纯数字。可以使用“=~”的判断方式,因为在“[[]]”中的“=~”可以进行正则判断,随后我们使用“^[0-9]”表示开头为数字,使用“+”表示数字出现一次或任意多次,使用“$”表示用户输入要以数字为结尾。同时,为了避免在执行脚本时“用户数量”输入为“0”,我们判断在“$num_1”的值为 0 时判断不成立。

3) 接下来对“$name_1”进行判断,判断其是否成功取值。然后对“$pass_1”进行判断,判断其取值是否成功。在多个判断式之间使用“&&”作为连接符号,表示逻辑与。当所有判断都成立后,进行循环创建指定数量用户。假设判断不成立,那么执行 else 后的语句,提示输入错误,退出脚本。

在循环创建用户时,使用类 C 的 for 循环。依靠创建用户数量来对循环次数进行限制,每循环一次,就创建一个用户。每创建一个用户,就为相应的用户设置一次初始密码。

这个脚本的执行结果如下:
[root@localhost ~]# chmod +x /root/useradd.sh
# 赋予执行权限
[root@localhost ~]# /root/useradd.sh
请输入要创建用户数量:100
请输入要创建用户名:ls
请输入要创建用户的默认密码:
各参数取值正常,准备创建用户
[root@localhost ~]# tail -n 100 /etc/shadow
# 查看用户密码文件,ls1~ls100共100个用户添加完成,默认拥有密码
ls1:$6$sxYINs1/1BpdnXkBSp7HNU1m8A06ZXEgb8vhDdoi/MCGwN5zeXwYERIjEeVOS5mBKSCgzDOPs0LNul2vqm6rlb9/clq0LDoXneOT.1.:19794:0:99999:7.::
ls2:$6$oGfN7.1YB6j6WSa1Srts WmcEm10v6Bq2.98r/CjBfahtEARKpZriWUfg/oaxCXIpREZilB3LBUVHiCUeO32osZxLu/57LGvZDPx9ql/:19794:0:99999:7:
ls3:$6$bOzbOPZxouyZkafNSAd706kNMEzq/8gCIMrvwM6nZOjCmgt7GtmtutCEBUoUmt.YCQt2ns9qePmNgtyrg2fSQ22snX4WtpN7LwhR5X.:19794:0:99999:7.::
...(省略部分命令执行结果)

相关文章