shell 循环遍历的详细用法

news2025/2/8 13:55:03

简介

    在 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1336082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware安装linux系统二

1、设置光驱 1.1、编辑虚拟机设置 1.2、设置虚拟机镜像 1.3、设置好后开机 2、安装Linux系统 2.1、等待安装 2.2、开始安装 2.3、选择语言&#xff0c;我选择中文 2.4、本地化不用改 2.5、软件选择一定要选&#xff0c;否则就会是默认最小安装 2.6、我这里选择的是带GUI的&am…

Java---IO流讲解(2)

文章目录 1. 字符流1.1 为什么出现字符流1.2 字符流写数据的5种方式1.3 字符流读数据的两种方式1.4 字符缓冲流1.5 字符缓冲流特有功能 2. IO流小结2.1 字节流2.2 字符流 1. 字符流 1.1 为什么出现字符流 由于字节流操作中文时不是特别方便&#xff0c;因此Java提供了字符流。…

分包zip压缩,解压报错:invalid zip file with overlapped components (possible zip bomb)

背景 在生产环境中&#xff0c;需要把安装包从本地传到服务器上&#xff0c;传输过程中网络抖动的原因造成大文传输失败。可以将文件分包压缩成200M或500M大小的文件&#xff0c;然后分批传输到服务器。最近生产环境传输了starrocks的安装包&#xff0c;分包压缩之后上传服务器…

uniapp中如何使用image图片

当在UniApp中使用图片时&#xff0c;可以通过<image>标签将图片显示在页面上。这个标签可以指定src属性来引用图片&#xff0c;并且可以通过mode属性来设置图片的显示模式。除此之外&#xff0c;还可以利用click事件来实现图片的点击事件。在编写代码时&#xff0c;要注意…

鸿蒙项目二—— 注册和登录

此部分和上篇文章是连续剧 &#xff0c;如果需要&#xff0c;请查看 一、注册 import http from ohos.net.http; Entry Component struct Reg {// 定义数据&#xff1a;State username: string "";State userpass: string "";State userpass2: string …

Java@RequestParam注解和@RequestBody注解接收参数

目录 Java后端接收数据 第一章、后端不写任何注解情况下接收参数1.1&#xff09;后端不写注解postman发出get请求1.2&#xff09;后端不写注解postman发出post请求 第二章、后端写RequestParam注解接收参数2.1&#xff09;postman发出post请求2.2&#xff09;postman发出get请求…

腾讯云上mysql连接不上

腾讯云服务器默认没开放&#xff0c;3306端口。 1.去腾讯云控制台 2.找到自己的服务器 3选择防火墙 4.添加规则 至此完事了。

Redis数据结构(常用5+4种特殊数据类型)

1、Redis 数据类型以及使用场景分别是什么&#xff1f; Redis 提供了丰富的数据类型&#xff0c;常见的有五种数据类型&#xff1a;String&#xff08;字符串&#xff09;&#xff0c;Hash&#xff08;哈希&#xff09;&#xff0c;List&#xff08;列表&#xff09;&#xff…

面试复盘2——测试开发——一面+二面

前言 本文主要用于个人复盘学习&#xff0c;因此为保障公平&#xff0c;所以本文不指出公司名&#xff0c;题目编号只是为了自己区别而已。对待面经&#xff0c;望读者还是更多从其中学习总结&#xff0c;而不是去碰原题。 面试岗位信息 测试开发工程师&#xff0c;秋招但需…

Spring Boot3 Web开发技术

前期回顾 springboot项目常见的配置文件类型有哪些&#xff1f;哪种类型的优先级最高 yml properties yaml 读取配置文件里的数据用什么注解&#xff1f; value restful风格 RESTful 风格与传统的 HTTP 请求方式相比&#xff0c;更加简洁&#xff0c;安全&#xff0c;能隐…

基于AR+地图导航的景区智慧导览设计

随着科技的飞速发展&#xff0c;智慧旅游已经成为现代旅游业的一个重要趋势。在这个背景下&#xff0c;景区智慧导览作为智慧旅游的核心组成部分&#xff0c;正逐渐受到越来越多游客的青睐。本文将深入探讨地图导航软件在景区智慧导览中的应用&#xff0c;并分析其为游客和景区…

pytorch中池化函数详解

1 池化概述 1.1 什么是池化 池化层是卷积神经网络中常用的一个组件&#xff0c;池化层经常用在卷积层后边&#xff0c;通过池化来降低卷积层输出的特征向量&#xff0c;避免出现过拟合的情况。池化的基本思想就是对不同位置的特征进行聚合统计。池化层主要是模仿人的视觉系统…

整数规划-割平面法

整数规划-割平面法 割平面法思想Gomorys割平面法原理实例 谨以此博客作为学习期间的记录。 割平面法思想 在之前&#xff0c;梳理了分支定界法的流程:分支定界法 除了分支定界法&#xff0c;割平面法也是求解整数规划的另一个利器。 我们已经知道&#xff0c;线性规划的可行域…

XStream 反序列化漏洞 CVE-2021-39144 已亲自复现

XStream 反序列化漏洞 CVE-2021-39144 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建 修复建议总结 漏洞名称 漏洞描述 在Unmarshalling Time处包含用于重新创建前一对象的类型信息。XStream基于这些类型的信息创建新实例。攻击者可以控制输入流并替换或注入对象&am…

LeetCode-回文链表(234)

题目描述&#xff1a; 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 因为这一题是受到876题求链表中间节点的启发&#xff0c;所以在这里也加一下。 876.链表的中间结点…

机器视觉系统选型-避免畸变

在定位及高精度测量的系统中&#xff0c;镜头畸变的影响尤其重要 • 使用远心镜头 • 进行系统标定

二维码智慧门牌管理系统:提升社区管理智能化水平

文章目录 前言一、全方位信息录入与查询二、公安权限账户访问的公安大数据后台三、社区工作人员申请权限安装录入软件四、业主通过移动终端扫描标准地址二维码门牌自主申报录入五、系统的价值 前言 在数字化时代&#xff0c;社区管理面临着更新流动人口信息、准确录入六实相关…

119. 杨辉三角 II(Java)

给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出: [1,1]提示…

xlua源码分析(四) lua访问C#的值类型

xlua源码分析&#xff08;四&#xff09; lua访问C#的值类型 上一节我们主要探讨了C#是如何使用interface和delegate访问lua层的table和function的&#xff0c;本节我们跟着Examples 05_NoGc&#xff0c;来看看xlua是如何实现lua层无gc访问C#的值类型的。 首先例子中用到的lua…

LH7904D 太阳能警示灯 0.4W×2

应用范围: 可安装在电线杆&#xff0c;路灯&#xff0c;围挡&#xff0c;交 通护栏及各种杆式固体等场所起警示作用。 产品特点&#xff1a; 采用进口PS材质; 光控无开关&#xff0c;白天不闪&#xff0c;昏暗环境自动闪烁&#xff0c;无需手动操作&#xff0c;省时省事; …