简介

    在 shell 脚本中,循环结构用于重复执行一组代码块,包括 for 循环、while 循环,可以用于遍历数字、字符串、数组、文件等。这篇文章会详细介绍这两种遍历方式,以及各种实例场景。

        

文章目录结构如下

1. 循环遍历的特点

2. 循环的方式

2.1. for循环

① 遍历整数

② 遍历数组

③ 遍历字符串

④ 遍历命令

⑤ 无限循环

⑥ 单行写法

2.2. while循环

① 基础用法

② 实例用法

2.3. 跳出循环

① continue 跳出当前循环

② break 跳出整个循环

③ 跳出多嵌套循环

3. 实际应用场景

3.1. while 按行读取内容

3.2. while 交互读取用户输入的变量

3.3. while 输出倒计时

3.4. for 输出进度条

        

1. 循环遍历的特点

刚开始接触编程的同学能理解什么是循环,但对 "遍历" 这个词可能有些陌生。遍历就是逐个访问集合中的每个元素,或逐个执行某个操作。那么循环遍历通俗来说就是:将多个数据循环读取出来,再对这些被读取出来的数据做各种操作。

那么循环遍历方法可以用在什么地方呢?举个例子:

后端需要一部分数据导入数据库,这些数据需要人工构造。

先使用 echo 来构造一行符合需求的数据

echo "1,zhang_san,18,man"

如果数据库需要3行数据,我们还是可以使用 echo

echo "1,zhang_san,18,boy"

echo "2,li_si,20,boy"

echo "3,wang_wu,16,girl"

如果需要1w行、10w行呢,还能使用手动敲10w行代码吗,这显然很耗费人力,所以循环的作用就体现出来了。代码示例

# 循环10w次

for i in {1..100000};do

echo "${i},zhang_san,18,boy"

done

3行代码就能实现10w 行字符,是不是很方便呢。当然了,实际需求是很复杂的,但仍然可以使用循环方式解决。下面就带大家由浅到深慢慢学习。

        

2. 循环的方式

在 shell 中常见的循环方式有 2 种,它们的作用分别是:

for 循环:按次数循环某些字符串、数组、文件等。while 循环:按条件循环,当条件为 True 即循环,条件为 False 则不循环。

这两种方式分别作用到不同的地方,下面就一起来学习吧!

2.1. for循环

① 遍历整数

遍历整数是最常见的方法,其中有2种写法,分别来看看他们怎么用吧

【写法一】按固定整数循环

for i in {1..5};do

echo "循环第${i}次"

done

for  in 是固定语法,不能修改。{1..5} 花括号是固定的,但里面的值可以修改 {开始值..结束值}。i 不是固定的,可以随意修改,它的作用是读取花括号中的值。do 是固定语法,不能修改,它表示循环开始。中间代码 echo 是自定义的,可以写其他代码。done 是固定语法,不能修改,它表示循环结束。

我们来看看执行结果:

总共循环了 5 次,这 5 次是从花括号 {1..5} 读取的。

        

那么我们希望循环 3~7 怎么写呢?直接修改花括号的值

for i in {3..7};do

echo "当前变量i的值是:${i}"

done

花括号 {3..7}:3表示开始,7表示结束

        

代码块中的 echo 命令是为了让我们了解循环的过程,来看一个简单的实例,向文件夹 ./tmp 下面创建 10 个文件。

for i in {1..10};do

touch ./tmp/file${i}.txt

done

创建了 file1 ~ 10 的文件,共10个。 

        

【写法二】按判断循环

for ((i=1; i<=5; i+=1));do

echo "当前i的值是:${i}"

done

for (()) 是固定语法,不能修改。

括号中的 i=1 表示 i 的开始值为 1。括号中的 i<=5 表示只循环 i 小于等于 5,若大于5则退出循环。括号中的 i+=1 表示每循环一次后 i 的值都 +1。do 是固定语法,不能修改,它表示循环开始。中间代码 echo 是自定义的,可以写其他代码。done 是固定语法,不能修改,它表示循环结束。

执行结果与《写法一》差不多

        

《写法二》与《写法一》相比,有2个优势。

优势一、中间的数值可以跳跃,代码如下(i+=2)

for ((i=1; i<=10; i+=2));do

echo "当前i的值是:${i}"

done

设置 i+=2 表示每次 i 的值 +2,这样就可以循环奇数。

指定 +2 并不是固定的,在实际中还可以 +3、+4、+n 等。

        

优势二、可以读取变量

# 定义一个变量为5

var=5

# 读取变量的值做循环

for ((i=1; i<=var; i+=2));do

echo "当前i的值是:${i}"

done

使用变量不仅仅可以指定最大值,最小值和跳跃值也可以指定

min=1

max=10

jump=3

# 读取变量的值做循环

for ((i=min; i<=max; i+=jump));do

echo "当前i的值是:${i}"

done

        

② 遍历数组

由于数组中包含不同的字符串或数字,上述《目录 ①遍历整数》的语法将无法使用,所以我们需要换一种写法

# 定义一个数组

arr=('AAA' 123 'BBB' 789)

# 遍历数组

for i in ${arr[@]};do

echo "当前数组的值为:${i}"

done

语法还是和《目录 ① 遍历整数》差不多,需要注意的是:遍历全部数组需要将变量这样写

${变量[@]}

按元素遍历,数组的分隔符默认空格。 

        

如果不需要变量数组中前2个元素,这样写 ${变量[@]:2}

# 定义一个数组

arr=('AAA' 123 'BBB' 789)

# 遍历数组

for i in ${arr[@]:2};do

echo "当前数组的值为:${i}"

done

只遍历前面3个元素:${变量[@]:0:3}只遍历后面3个元素:${变量[@]:(-3)}遍历中间 3~5 个元素:${变量[@]:2:3}

详细的数组用法见另一篇文章:https://blog.csdn.net/m0_61066945/article/details/135070671

        

除了指定数组的值,我们还可以通过命令向数组赋值,并使用 for 循环遍历

# 定义一个数组

arr=(`ls /tmp/`)

# 遍历数组

for i in ${arr[@]};do

echo "当前数组的值为:${i}"

done

        

③ 遍历字符串

遍历字符串的方法和遍历数组的方式差不多,只是变量不同

# 定义一个字符串变量

var="AAA BBB CCC"

# 循环遍历这个变量

for i in ${var};do

echo "当前变量的值为:${i}"

done

我们定义了一个变量 var,使用 for 循环将变量中的值读取出来。

注意:for 循环遍历变量时,in 后面的变量不能加引号,如果加引号就表示这是1个字符

for i in "${var}";do

echo "当前变量的值为:${i}"

done

加上引号后,for 会认为这是一个字符,所以值循环一次。

我们不加引号可以将它理解成这样(直接遍历字符串)

for i in AAA BBB CCC;do

echo "当前变量的值为:${i}"

done

        

还有一点需要注意,变量中默认分隔符是空格,这里列举了几种修改分隔符的写法:

IFS=$"," # 将分隔符指定为逗号

IFS=$"abc" # 将分隔符指定为abc

IFS=$",: \n" # 将分隔符指定为逗号、冒号、空格、换行

举个例子(将分隔符指定为逗号)

# 定义一个变量

var="AAA BBB,CCC"

OLD_IFS=${IFS} # 读取当前分隔符

IFS=$"," # 指定分隔符为逗号

# 循环遍历这个变量

for i in ${var};do

echo "当前变量的值为:${i}"

done

IFS=${OLD_IFS} # 将分隔符修改会原来的值

结果如下

分隔符指定为逗号后,空格就不再生效。如果需要同时使用空格和逗号为分隔符,即设置为:IFS=$" ," 引号中同时包含一个空格和逗号。

        

④ 遍历命令

上面3种方法主要遍历固定的字符,在实际的场景中遍历命令的方式也是非常普遍的。

【案例一】遍历某个目录下以 .txt 结尾的文件

# find查找文件,for循环遍历

for file in $(find ./tmp -type f -name '*.txt');do

echo "当前变量的值为:${file}"

done

语法与前面差不多,命令放在 $( ) 中即可。

        

【案例二】遍历某个命令输出的结果

# seq输出一些数字

for i in $(seq 2 5);do

echo "当前变量的值为:${i}"

done

        

⑤ 无限循环

无限循环的语法与上面不同,不需要读取啥,这样写

for ((;;));do

echo "这是一个无限循环"

done

注意:这个方法会消耗一个cpu的资源,慎用!无限循环中最好加上 sleep

for ((;;));do

echo "这是一个无限循环"

sleep 1

done

        

⑥ 单行写法

单行写法语法如下

for i in {1..5};do 代码块1; 代码块2; done

单行写法与上面标准写法差不多,就是将换行符改为分号。如果将单行理解为4个部分的话,那么就是:(注意使用分号分隔)

【for 语法】;【do 开始循环】;【代码块】;【done 结束循环】

        

【案例1】创建10个文件

for i in {1..10};do touch tmp/file${i}.txt ;done

        

【案例二】给数组赋值 1~100 的奇数

# 定义一个空数组

arr=()

# 给数组赋值

for ((i=1; i<=100; i+=2));do arr+=(${i}) ;done

# 输出这个数组的值

echo ${arr[@]}

        

2.2. while循环

① 基础用法

while 不同于 for,for 是去读取某个值,while 是判断。如果判断结果为 True 则循环,如果判断结果为 False 则退出循环。

【案例一】直接给 while 加 true 表示无限循环

while true;do

echo "我是一个while循环"

sleep 1

done

        

【案例二】直接给 while 加 false 无法循环

while false;do

echo "我是一个while循环"

sleep 1

done

        

【案例三】在 false 前面加 !表示非 false,同 true

while ! false;do

echo "我是一个while循环"

sleep 1

done

        

通过上面3个案例总结出一个结论:只要判断的结果是正常的就可以循环,判断的结果是异常的则不循环。我们来判断一下数学运算。

# 定义一个变量

w=0

# 循环判断这个变量,只循环小于等于10的值

while [ ${w} -le 10 ];do

echo "我是一个while循环, 当前w的值是:${w}"

# 每循环一次,w的值+1

(( w += 1 ))

done

        

除了判断某个变量,我们还可以判断命令是否正确

# 循环判断PID为4549的进程是否存在

while ps u -p 4549;do

sleep 1

done

当进程存在时,一直循环;进程不存在时,退出循环。

        

② 实例用法

【案例一】判断端口是否被占用

while netstat -anpt |grep 3306; do

echo "端口3306已被占用"

sleep 1

done

端口号未被占用后,则退出循环。

        

【案例二】判断文件是否存在

file='./tmp/file.txt'

while [ ! -f ${file} ]; do

echo "没有 ${file} 文件, 那么创建这个文件"

touch ${file}

done

文件存在后,则退出循环 

        

【案例三】判断网络 192.118.168.254 是否通畅

while ! timeout 3 ping 192.118.168.254; do

echo "192.118.168.254 无法ping通"

done

如果 IP 没有 ping 通,那么会一直去 ping,直到能够 ping 通。 

        

2.3. 跳出循环

在实际应用中,经常会出现循环到某个地方时,希望循环停止而不退出脚本,那么需要用到以下两个命令:

① continue 跳出当前循环

举个例子,当变量 i  = 3 时跳出当前循环,i 等于其他值时正常运行

# 循环1~5

for i in {1..5};do

# 判断:如果i=3,跳出当前循环

if [ ${i} -eq 3 ];then

continue

fi

# 执行其他命令

echo "当前变量i的值是:${i}"

done

只跳出当前循环,后面的循环继续 

        

② break 跳出整个循环

当变量 i = 3 时直接跳出整个循环

# 循环1~5

for i in {1..5};do

# 判断:如果i=3,跳出当前循环

if [ ${i} -eq 3 ];then

break

fi

# 执行其他命令

echo "当前变量i的值是:${i}"

done

跳出整个循环后,后面的 4、5 都不会循环

        

③ 跳出多嵌套循环

当遇到多层嵌套时,希望跳过某个循环时应该怎么指定呢?

我们先写一个3层嵌套的例子

for i in {1..2};do

for j in {11..12};do

for k in {21..22};do

echo "当前各个变量结果:i=${i}, j=${j}, k=${k}"

done

done

done

每层输出2次,3层嵌套为 2³=8 次

        

将每层嵌套都打印字符,方便后面理解

for i in {1..2};do

echo "第一层嵌套: ${i}"

for j in {11..12};do

echo "第二层嵌套: ${j}"

for k in {21..22};do

echo "第三层嵌套: ${k}"

done

done

done

        

【案例一】跳出当前层级的嵌套:break 1

for i in {1..2};do

echo "第一层嵌套: ${i}"

for j in {11..12};do

echo "第二层嵌套: ${j}"

for k in {21..22};do

# 跳出当前嵌套

break 1

echo "第三层嵌套: ${k}"

done

done

done

对当前 break 的位置来说,当前层级就是第 1 层,所以跳出第 1 层后,就只剩下外面 2 层循环了。

        

【案例二】跳出上一层级的嵌套:break 2

for i in {1..2};do

echo "第一层嵌套: ${i}"

for j in {11..12};do

echo "第二层嵌套: ${j}"

for k in {21..22};do

# 跳出上面一层嵌套

break 2

echo "第三层嵌套: ${k}"

done

done

done

对当前 break 的位置来说,当前层级就是第 1 层,上一层是第 2 层。所以在上一层执行到 break 时就会跳出,而第 2 层 break 上面的代码是可以正常执行的。

        

 【案例三】跳出上上层级的嵌套:break 3

for i in {1..2};do

echo "第一层嵌套: ${i}"

for j in {11..12};do

echo "第二层嵌套: ${j}"

for k in {21..22};do

# 跳出上上层嵌套

break 3

echo "第三层嵌套: ${k}"

done

done

done

对当前 break 的位置来说,当前层级就是第 1 层,上上层是第 3 层。所以在上上层执行到 break 时就会跳出,而第 3 层 break 上面的代码是可以正常执行的。 

        

总结

不论是 continue 还是 break,如果不指定跳出的嵌套层级,那么就是跳出当前的嵌套。如果指定层级,那么当前层算是第1层,往上一层算低2层 ,再往上一层算是第3层,以此类推。

        

3. 实际应用场景

3.1. while 按行读取内容

虽然 for 循环也可以做到按行读取内容,但是需要修改分隔符(挺麻烦的),使用 while 就简单多了。代码如下:

while read str; do # 通过read读取文件内容,赋值为变量str(str可以自定义)

echo "当前文件内容:${str}"

done < ./file.txt # 这里需要指定文件路径

        

3.2. while 交互读取用户输入的变量

# 定义一个普通数组

declare -a arr

while [ ${#arr[@]} -lt 2 ]; do # 判断数组的个数小于等于2

read -p "请输入一个整数: " input # 交互读取变量

arr+=(${input}) # 将变量追加到数组

done

# 计算数组

result=$(( ${arr[0]} + ${arr[1]} ))

echo "${arr[0]} + ${arr[1]} = ${result}"

        

3.3. while 输出倒计时

# 设置倒计时初始值

count=10

# 循环实现数字每秒自动变化

while [ ${count} -ge 0 ];do

printf "\r倒计时: %2d" $count

sleep 1

(( count-- ))

done

echo -e "\n========== 结束 =========="

结果为动态,只能截几张静态图

        

3.4. for 输出进度条

# 设置总进度

total=100

# 循环输出进度条和百分比

for ((i=1; i<=total; i++)); do

# 计算百分比

percent=$((i * 100 / total))

# 输出进度条和百分比

printf "\r当前进度:%-$((total+1))s%2d%%" "$(perl -E "say '=' x ${i}")" $percent

# 休眠0.1秒

sleep 0.1

done; echo

精彩内容

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。