当前位置: 首页 > news >正文

【Day 41】Shell脚本-循环

一、循环类型

Shell 脚本中常用的循环类型有两种:

  • for 循环:适合已知遍历范围的场景(如固定列表、序列、文件列表等)。
  • while 循环:适合未知循环次数,需通过条件判断终止的场景(如持续监听、逐行处理文件等)。

二、for 循环

1. for 循环的语法
# 语法1 用;分隔取值列表和do,适合操作简短(单条命令)的场景,代码更紧凑。
for 变量 in 取值列表; do执行的操作
done# 语法2 用换行分隔取值列表和do,适合操作复杂(多条命令)的场景,可读性更强
for 变量 in 取值列表
do执行的操作
done
2、执行逻辑
  1. 初始化遍历:从 “取值列表” 中取出第一个元素,赋值给 “变量”。
  2. 执行操作:运行do和done之间的所有命令(即 “执行的操作”)。
  3. 循环迭代:从 “取值列表” 中取出下一个元素,重复步骤 1 和 2。
  4. 终止循环:当 “取值列表” 中的所有元素都处理完毕后,退出循环,执行done之后的代码。
3、取值列表

        在 for 变量 in 取值列表; do ... done 结构中,取值列表可以是多种形式的 “元素集合”,变量则用于接收列表中的每个元素(每次循环取一个元素)。

取值列表本质是 “一系列元素的集合”,元素之间以 IFS(默认是空格、制表符、换行符)分隔。

取值列表的常见形式

(1)直接写死的字符串 / 数字列表(最基础)

直接列举多个元素,用空格分隔,适合已知固定值的场景。

# 示例:遍历字符串列表
for fruit in apple banana "cherry pie" date; doecho "水果:$fruit"
done
# 输出:
# 水果:apple
# 水果:banana
# 水果:cherry pie  (带空格的元素需用引号包裹,否则会被拆分成两个元素)
# 水果:date# 示例:遍历数字列表
for num in 1 3 5 7; doecho "奇数:$num"
done

(2)序列(连续的数字 / 字符范围)

通过 {起始..结束} 或 seq 命令生成连续序列,适合循环固定次数。

# 1. {起始..结束}:Bash 内置的序列扩展(无需调用外部命令,高效)
for i in {1..5}; do  # 数字序列:1-5echo "第$i次循环"
donefor c in {a..e}; do  # 字符序列:a-eecho "字母:$c"
donefor i in {10..1..2}; do  # 倒序+步长:10、8、6、4、2(Bash 4.0+支持)echo "倒序偶数:$i"
done# 2. seq 命令:生成序列(兼容更多老版本Shell)
for i in $(seq 1 2 10); do  # 从1开始,步长2,到10结束(1、3、5、7、9)echo "步长为2的数:$i"
done

// seq 是 Linux 系统中的一个工具,用于生成连续的整数序列,默认以换行分隔每个数字。

  • seq [起始数] [步长] [结束数]
  • seq 20    //只指定结束(默认从 1 开始,步长 1)即 seq 20,等价于 seq 1 1 20。。
  • seq 5 10       // 指定起始和结束。输出从 5 到 10 的整数:
  • seq 2 2 10   //指定步长。从 2 开始,每次加 2,直到不超过 10:

在脚本中的典型用途

seq 常与 for 循环结合,生成遍历范围,例如:

# 循环 20 次,创建 20 个文件
for i in $(seq 20); dotouch /tmp/test_$i.txt
done

这里 $(seq 20) 会展开为 1 2 ... 20,循环变量 i 依次取这些值,实现批量操作

(3)文件名通配符(匹配文件 / 目录)

通过通配符(*、?、[] 等)匹配符合条件的文件 / 目录名,适合批量处理文件。

# 示例1:遍历当前目录下所有.txt文件
for txtfile in *.txt; doecho "处理文本文件:$txtfile"
done# 示例2:遍历/tmp目录下所有以"log_"开头的文件
for logfile in /tmp/log_*; doecho "日志文件:$logfile"
done# 示例3:匹配数字结尾的文件(如file1、file2)
for file in file[0-9]; doecho "数字结尾的文件:$file"
done

(4)命令替换的输出($(命令) 或 \命令 )

将命令的输出作为取值列表,适合动态生成元素(如从文件读取、过滤结果等)。

# 示例1:遍历/etc/passwd中所有用户名(提取第1个字段)
for user in $(awk -F: '{print $1}' /etc/passwd); doecho "系统用户:$user"
done# 示例2:遍历当前目录下的子目录(ls -d */ 列出目录,再去掉末尾的/)
for dir in $(ls -d */ | sed 's:/$::'); doecho "子目录:$dir"
done# 示例3:遍历文件中的每行内容(需确保行内无空格,否则会拆分)
for line in $(cat /tmp/names.txt); do  # 若行内有空格,建议用while readecho "姓名:$line"
done

命令替换的输出$(命令) 是否会 “处理空格”,取决于是否用双引号包裹,核心是 Shell“分词机制”:

1、默认情况(不加双引号):
不会直接 “切掉” 空格,但会把空格(以及制表符、换行符,即默认的 IFS)当作 “分隔符”,将输出拆分成多个 “单词”。
例如:命令输出 a b c(含两个空格和一个空格),会被拆成 a、b、c 三个独立元素(多个连续空格会被视为一个分隔符)。
2、加双引号包裹(如 "$(命令)"):
会禁用分词,空格会被完整保留,输出作为一个整体。
例如:命令输出 a b c,用双引号包裹后,会被当作一个完整元素 a b c(空格原样保留)。

(5)数组元素(遍历数组)

遍历数组中的所有元素,适合需要先存储元素再处理的场景。

# 定义数组
fruits=("apple" "banana" "cherry pie" "date")# 遍历数组("${数组名[@]}" 表示所有元素,双引号避免空格拆分)
for fruit in "${fruits[@]}"; doecho "数组中的水果:$fruit"
done

(6)位置参数(脚本 / 函数的输入参数)

遍历脚本或函数的参数($@ 表示所有参数),适合处理动态输入。

# 脚本:接收用户传入的多个文件名,批量处理
# 用法:./process.sh file1.txt file2.txt file3.txt
for file in "$@"; do  # "$@" 会保留参数中的空格(如"my file.txt"作为一个元素)echo "处理参数文件:$file"
done

(7)变量展开(使用变量作为列表)

将变量的值作为取值列表(变量中存储多个元素,以空格分隔)。

# 变量存储多个元素(带空格的元素需提前用引号处理)
colors="red green 'blue sky'"  # 注意:'blue sky' 用单引号包裹,避免拆分# 遍历变量(需用eval解析引号,否则单引号会被当作普通字符)
for color in $(eval echo $colors); doecho "颜色:$color"
done
# 输出:
# 颜色:red
# 颜色:green
# 颜色:blue sky

示例 1:在 /tmp 目录创建 20 个测试文件(两种命名方式)

#!/bin/bash
# 创建20个随机名称的文件(使用openssl生成10位十六进制字符串)
for i in $(seq 20); dotouch /tmp/$(openssl rand -hex 10)
done# 或创建有序命名的文件(file1到file20)
for i in {1..20}; do  # {1..20}是bash的序列扩展,比seq更高效touch /tmp/file${i}
done

示例 2:批量创建用户(带存在性检查和初始密码)

#!/bin/bash
# 批量创建user1到user10,若已存在则提示,否则创建并设置初始密码
for i in {seq 10}; dousername="user${i}"if id "$username" &> /dev/null; thenecho "用户$username已存在,跳过"elseuseradd "$username"echo "123456" | passwd --stdin "$username" &> /dev/null  # 设置初始密码echo "用户$username创建成功,初始密码:123456"fi
done

示例 3:从文件读取用户列表批量创建

#!/bin/bash
# 从/tmp/userlist文件读取用户名(每行一个),批量创建用户
# 前提:/tmp/userlist格式为每行一个用户名,如:alice、bob
if [ ! -f "/tmp/userlist" ]; thenecho "错误:/tmp/userlist文件不存在"exit 1
fifor name in $(cat /tmp/userlist); doif id "$name" &> /dev/null; thenecho "用户$name已存在"elseuseradd "$name"echo "用户$name创建完成"fi
done

示例 4:多线程检测网段在线 IP(限制并发数)

#!/bin/bash
# 检测10.11.9.0/24网段在线IP,最多同时运行50个ping进程(避免系统负载过高)
subnet="10.11.9."
max_concurrent=50  # 最大并发数
current=0for i in {1..255}; do((current++))if [ $current -ge $max_concurrent ]; thenwait  # 等待当前批次进程结束current=0fi{ip="${subnet}${i}"if ping -c 1 -W 1 "$ip" &> /dev/null; then  # 发送1个包,超时1秒echo "Host $ip is up"fi}&  # 后台运行
done
wait  # 等待剩余后台进程结束
echo "检测完成"
#!/bin/bash
subnet=10.11.7.for i in $(seq 255); do{ip=$subnet$iif ping -c 2 -W 1 $ip &>/dev/null; thenecho "Host $ip is up"fi}&
done
wait

示例 5:检测目录文件差异(补充完整逻辑)

#!/bin/bash
# 对比/tmp/bj和/tmp/sh目录,检测缺失或不一致的文件(完善版)
src_dir="/tmp/bj"
dest_dir="/tmp/sh"# 检查源目录是否存在
if [ ! -d "$src_dir" ]; thenecho "错误:源目录$src_dir不存在"exit 1
fi# 遍历源目录所有文件,对比目标目录
for src_file in $(find "$src_dir" -type f); do# 替换路径中的"bj"为"sh",得到目标文件路径(字符串替换:${变量/旧值/新值})dest_file="${src_file/bj/sh}"if [ ! -e "$dest_file" ]; thenecho "文件缺失:$dest_file"else# 对比MD5值(忽略文件名,只比较内容)src_md5=$(md5sum "$src_file" | awk '{print $1}')dest_md5=$(md5sum "$dest_file" | awk '{print $1}')if [ "$src_md5" != "$dest_md5" ]; thenecho "内容不一致:$dest_file"fifi
done  # 补充原脚本缺失的done

示例6:用循环命令统计 /etc/passwd 中使用 /bin/bash 和 /sbin/nologin 作为登录 shell 的用户数量

①(按行号逐行提取)

#!/bin/bash
bash_count=0
nologin_count=0
# 先获取文件总行数,再按行号循环,逐行提取shell字段
number=$(wc -l /etc/passwd |awk '{print $1}')    #获取总行数
for i in $(seq $number); do      #循环行号(1到总行数)shell=$(head -n $i /etc/passwd | tail -n 1 | awk -F: '{print $7}')# 判断计数if [ "$shell" == "/bin/bash" ]; thenlet bash_count++elif [ "$shell" == "/sbin/nologin" ]; thenlet nologin_count++fi
done
echo "bash用户数量: $bash_count"

②(直接提取 shell 字段)更优

#!/bin/bash
bash_count=0
nologin_count=0
# 用awk一次性提取所有用户的shell字段(第7列),然后遍历这些字段
for shell in $(awk -F: '{print $7}' /etc/passwd); do# 判断shell类型并计数if [ "$shell" == "/bin/bash" ]; thenlet bash_count++elif [ "$shell" == "/sbin/nologin" ]; thenlet nologin_count++fi
done
echo "bash用户数量: $bash_count"
echo "nologin用户数量: $nologin_count"

1. 为什么 for line in $(cat /etc/passwd) 会出错?

  • cat /etc/passwd 会把文件内容全输出,但输出的是 “一堆字符串”(包含换行和行内空格)。

  • Shell 会按默认规则(空格、换行都算分隔符)把这堆字符串拆成 “小碎片”。

  • 比如有一行 Zhang San:...(中间有空格),会被拆成 Zhang 和 San:... 两个碎片,for 循环里的 line 拿到的是碎片,不是完整行。

  • 碎片没有足够的字段(比如原本 7 个字段,碎片可能只有 3 个),后续用 cut 取第 7 个字段自然会错。

2. 为什么 for shell in $(awk ...) 没问题?

  • awk -F: '{print $7}' 会先把 整行内容 按冒号拆分(不管行里有没有空格),正确提取第 7 个字段(比如 /bin/bash)。

  • 它输出的结果是 “一个个独立的字段值”(比如 /bin/bash、/sbin/nologin 等),这些值本身几乎没有空格,不会被 Shell 拆成碎片。

  • 所以 for 循环里的 shell 拿到的是完整的字段值,自然不会错。

示例7:/opt/data下的1.txt~10.txt会被重命名为1.html~10.html,

#!/bin/bash
for file_name in $(find /opt/data -name "*.txt"); do# 用echo输出文件名,通过管道给awk按"."分割,提取第一个字段(前缀)prefix=$(echo "$file_name" | awk -F. '{print $1}')new_file_name="$prefix.html"mv "$file_name" "$new_file_name"
done
#!/bin/bash
for file_name in $(find /opt/data -name "*.txt"); donew_file_name=${file_name/txt/html}mv "$file_name" "$new_file_name"
done

三、while 循环

1. 基本语法
while 条件判断; do执行的操作# (通常需要修改条件变量,避免死循环)
done

示例:用 while 创建 10 个随机文件

#!/bin/bash
i=1  # 初始化计数器
while [ $i -le 10 ]; do  # 条件:i小于等于10touch /tmp/$(openssl rand -hex 8)let i++  # 计数器自增(等价于 i=$((i+1)))
done
echo "10个文件创建完成"
2. while true 循环(无限循环)(死循环)

适用于需要持续运行的场景(如监控脚本、服务守护进程)。

#!/bin/bash
# 每2秒打印一次系统负载(按Ctrl+C终止)
while true; do  # true恒为真,循环永不结束echo "当前系统负载:$(uptime | awk '{print $10 $11 $12}')"sleep 2  # 休眠2秒,降低资源占用
done
3. while read line 循环(逐行处理)

通过read命令逐行读取输入(文件或管道输出),line变量代表当前行内容。

#!/bin/bash
while read line; do  执行操作
done < 文件名称

3.1 遍历文件内容

#!/bin/bash
# 从/etc/passwd读取系统用户,导入MySQL数据库(带错误处理)
# 前提:已创建数据库it和表passwd(字段:name,password,uid,gid,comment,home_dir,shell)
db_user="root"
db_pass="WWW.1.com"
db_name="it"# 检查MySQL连接
if ! mysql -u"$db_user" -p"$db_pass" -e "use $db_name" &> /dev/null; thenecho "错误:MySQL连接失败(用户名/密码/数据库错误)"exit 1
fi# 逐行读取/etc/passwd(格式:name:password:uid:gid:comment:home:shell)
while read line; doname=$(echo "$line" | awk -F: '{print $1}')password=$(echo "$line" | awk -F: '{print $2}')uid=$(echo "$line" | awk -F: '{print $3}')gid=$(echo "$line" | awk -F: '{print $4}')comment=$(echo "$line" | awk -F: '{print $5}' | sed "s/'/\\\'/g")  # 转义单引号,避免SQL错误home_dir=$(echo "$line" | awk -F: '{print $6}')shell=$(echo "$line" | awk -F: '{print $7}')# 插入数据库mysql -u"$db_user" -p"$db_pass" -e \"insert into $db_name.passwd values('$name','$password',$uid,$gid,'$comment','$home_dir','$shell')" &> /dev/nullif [ $? -eq 0 ]; thenecho "导入成功:$name"elseecho "导入失败:$name"fi
done < /etc/passwd  # 通过重定向指定输入文件

3.2 处理命令输出(管道方式)

命令|while read line;do执行操作done

例子:
# 检测磁盘使用率>20%的分区(兼容不同系统df输出格式

#!/bin/bash
# 检测磁盘使用率>20%的分区(兼容不同系统df输出格式)
# df -hT:显示文件系统类型、容量、使用率等(-h:人类可读单位,-T:显示类型)
df -hT | grep -vE "tmpfs|loop" | while read line; do  # 排除tmpfs和loop设备# 提取关键信息(不同系统列顺序可能不同,通过$NF定位挂载点)usage=$(echo "$line" | awk '{print $(NF-1)}' | sed 's/%//')  # 使用率(去掉%)disk_type=$(echo "$line" | awk '{print $2}')  # 文件系统类型total_size=$(echo "$line" | awk '{print $3}')  # 总容量free_size=$(echo "$line" | awk '{print $5}')  # 剩余容量mount_point=$(echo "$line" | awk '{print $NF}')  # 挂载点(最后一列)if [ "$usage" -gt 20 ]; then  # 比较数字(已去掉%)echo "磁盘预警:$mount_point($disk_type)"echo "  总容量:$total_size,剩余:$free_size,使用率:${usage}%"fi
done  # 补充原脚本缺失的done

例子:统计tcp端口状态netstat -antp处于listen状态的established建立连接的有几个

#!/bin/bash
listen_count=0
established_count=0
while read -r line; dostate=$(echo "$line" | awk '{print $6}')if [ "$state" = "LISTEN" ]; then((listen_count++))elif [ "$state" = "ESTABLISHED" ]; then((established_count++))fi
done < <(netstat -antp | grep '^tcp' )  # 只保留TCP连接(排除标题和其他协议)
echo "处于LISTEN状态的TCP端口数量是:$listen_count"
echo "处于ESTABLISHED状态的TCP端口数量是:$established_count"

四、中断循环的操作

1. break:终止整个循环

当满足条件时,直接退出当前循环,不再执行后续迭代。

示例:找到第 3 个文件后停止遍历

#!/bin/bash
# 遍历/tmp目录,找到3个文件后停止
count=0
for file in /tmp/*; doif [ -f "$file" ]; then  # 只统计文件(排除目录)echo "找到文件:$file"((count++))if [ $count -eq 3 ]; thenecho "已找到3个文件,停止遍历"break  # 终止整个循环fifi
done
2. continue:跳过本次循环

当满足条件时,跳过当前迭代的剩余操作,直接进入下一次循环。

示例:处理日志时跳过空

#!/bin/bash
# 遍历日志文件,跳过空行并打印非空行内容
log_file="/var/log/messages"
if [ ! -f "$log_file" ]; thenecho "错误:日志文件$log_file不存在"exit 1
fiwhile read line; doif [ -z "$line" ]; then  # 若行为空(-z:字符串长度为0)continue  # 跳过本次循环,不执行后续echofiecho "日志内容:$line"
done < "$log_file"

综合示例:显示前 5 个系统用户(UID 1-199)

#!/bin/bash
# 从/etc/passwd提取前5个系统用户(UID 1-199)
count=1  # 计数器:记录已找到的用户数量while read line; douid=$(echo "$line" | awk -F: '{print $3}')# 判断是否为系统用户(UID 1-199)if [ "$uid" -ge 1 ] && [ "$uid" -le 199 ]; thenusername=$(echo "$line" | awk -F: '{print $1}')echo "系统用户$count:$username(UID:$uid)"((count++))  # 计数器自增fi# 找到5个后终止循环if [ $count -gt 5 ]; thenbreakfi
done < /etc/passwd

总结

  • for 循环:优先用于已知范围的遍历(如序列、文件列表、固定集合),语法简洁。
  • while 循环:适合条件驱动的场景(如无限循环、逐行处理、动态条件判断)。
  • break/continue:灵活控制循环流程,break 终止整个循环,continue 跳过当前迭代。

需求一:查找硬盘使用率 >10% 的信息

#!/bin/bash
echo "硬盘使用率不超过70%的信息:"
df -hT | tail -n +2 | while read -r line; do# 按实际列顺序提取(以下为默认列顺序,若不同请修改$数字)name=$(echo "$line" | awk '{print $1}')       # 磁盘名称(第1列)total=$(echo "$line" | awk '{print $3}')      # 总容量(第3列)free=$(echo "$line" | awk '{print $5}')       # 可用容量(第5列)usage=$(echo "$line" | awk '{print $6}' | sed 's/%//')  # 使用率(第6列,去掉%)if [ "$usage" -gt  10 ]; thenecho "磁盘名称:$name"echo "总容量:$total"echo "可用容量:$free"echo "当前使用率:${usage}%"echo "-------------------"fi
done

需求二:找出近 10 分钟启动的进程

(1)

#!/bin/bash
# 获取当前时间的时间戳
now=$(date +%s)
for proc_id in $(ps -eo pid | sed '1d'); do
# 针对当前进程 ID(proc_id),获取其完整启动时间:= 表示去掉列标题(只保留时间值)。proc_start_time=$(ps -p $proc_id -o lstart=)proc_start_timestap=$(date -d "$proc_start_time" +%s)# 判断10分钟内启动的进程 let offset=now-proc_start_timestap if [ $offset -lt 600 ]; thenecho "进程ID:$proc_id, 进程名称: $(ps -p ${proc_id} -o comm=)"fi
done

(2)遍历所有进程→获取启动时间→转换为时间戳→计算与当前时间的差值

#!/bin/bash
# 获取当前时间戳(秒)
now=$(date +%s)
# 遍历所有进程ID(1d 表示删除第一行)逐行读取处理后的进程 ID,存到变量 id 中,循环处理每个进程。
ps -eo pid | sed '1d' | while read -r id; do# 通过进程ID获取启动时间(lstart= 去掉列标题)#-p "$id":指定只查看该 PID 的进程。#-o lstart=:lstart 表示输出完整启动时间,= 表示去掉列标题(只保留时间值)。start_time=$(ps -p "$id" -o lstart= 2>/dev/null)# continue:如果为空,跳过当前进程,直接处理下一个 PID。if [ -z "$start_time" ]; thencontinuefi# 将启动时间转换为时间戳start_timestamp=$(date -d "$start_time" +%s 2>/dev/null)# 如果 start_timestamp 为空(转换失败),跳过当前进程,处理下一个。if [ -z "$start_timestamp" ]; thencontinuefi#$((...)):bash 中的算术运算,计算与当前时间戳的差值(秒)offset=$((now - start_timestamp))# 筛选10分钟内(600秒)启动的进程if [ "$offset" -lt 600 ]; then# 获取进程名称comm=$(ps -p "$id" -o comm=)echo "进程ID:$id,进程名称:$comm,启动时间:$start_time"fi
done

需求三:将 /etc/passwd 文件内容按行写入数据库(以 MySQL 为例)

说明:需提前创建数据库和表:

CREATE TABLE passwd_info (username VARCHAR(50) NOT NULL PRIMARY KEY,  -- 用户名(主键,唯一)password VARCHAR(10) NOT NULL,             -- 密码占位符(通常为x)uid INT NOT NULL,                          -- 用户IDgid INT NOT NULL,                          -- 组IDcomment VARCHAR(255),                      -- 注释信息(可能包含用户全名等)home_dir VARCHAR(100) NOT NULL,            -- 家目录路径login_shell VARCHAR(100) NOT NULL          -- 登录shell路径
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
#!/bin/bash
# 将/etc/passwd内容按行写入MySQL数据库(需提前创建表)# 数据库配置(请根据实际情况修改)
db_user="root"
db_pass="your_password"
db_name="sys_db"
table_name="passwd_info"# 检查数据库连接
if ! mysql -u"$db_user" -p"$db_pass" -e "use $db_name" &>/dev/null; thenecho "错误:数据库连接失败,请检查账号密码"exit 1
fi# 逐行读取/etc/passwd并插入数据库
echo "开始写入/etc/passwd到数据库..."
while read -r line; do# 分割/etc/passwd字段(格式:name:password:uid:gid:comment:home:shell)name=$(echo "$line" | awk -F: '{print $1}')password=$(echo "$line" | awk -F: '{print $2}')uid=$(echo "$line" | awk -F: '{print $3}')gid=$(echo "$line" | awk -F: '{print $4}')comment=$(echo "$line" | awk -F: '{print $5}' | sed "s/'/\\\'/g")  # 转义单引号home=$(echo "$line" | awk -F: '{print $6}')shell=$(echo "$line" | awk -F: '{print $7}')# 插入数据库mysql -u"$db_user" -p"$db_pass" -e \"INSERT INTO $db_name.$table_name (name, password, uid, gid, comment, home, shell) VALUES ('$name', '$password', $uid, $gid, '$comment', '$home', '$shell')" &>/dev/nullif [ $? -eq 0 ]; thenecho "成功写入:$name"elseecho "写入失败:$name"fi
done < /etc/passwdecho "写入完成"

需求四:查找系统中前 3 个系统用户(UID 1-999 间)

#!/bin/bash
# 查找UID在1-999之间的前3个系统用户count=0  # 计数器:记录找到的系统用户数量echo "前3个系统用户(UID 1-999):"
echo "用户名 | UID"
echo "------------------------"# 逐行读取/etc/passwd,筛选符合条件的用户
while read -r line; do# 提取用户名和UIDusername=$(echo "$line" | awk -F: '{print $1}')uid=$(echo "$line" | awk -F: '{print $3}')# 判断UID是否在1-999之间if [ "$uid" -ge 1 ] && [ "$uid" -le 999 ]; then((count++))echo "$username | $uid"# 找到3个后停止循环if [ $count -eq 3 ]; thenbreakfifi
done < /etc/passwd

需求五:检测目录间文件差异(以 /src 和 /dest 为例)

#!/bin/bash
# 检测两个目录间的文件差异(缺失/内容不一致)
src="/Sylvia"    # 源目录(请修改为实际路径)
dest="/Angelina"  # 目标目录(请修改为实际路径)# 检查目录是否存在
if [ ! -d "$src" ]; thenecho "错误:源目录$src存在"exit 1
fi
if [ ! -d "$dest" ]; thenecho "错误:目标目录$dest不存在"exit 1
fi
# 遍历源目录所有文件,对比目标目录
find "$src" -type f | while read -r src_file; do# 转换为目标目录对应路径(替换/src为/dest)dest_file="${src_file/$src/$dest}"# 检查目标文件是否缺失if [ ! -f "$dest_file" ]; thenecho "缺失文件:$dest_file"continuefi# 比较文件内容(MD5值)src_md5=$(md5sum "$src_file" | awk '{print $1}')dest_md5=$(md5sum "$dest_file" | awk '{print $1}')if [ "$src_md5" != "$dest_md5" ]; thenecho "内容不一致:$dest_file"fi
doneecho "检测完成"
http://www.xdnf.cn/news/1422271.html

相关文章:

  • 802.11 和 802.1X
  • 谷歌-PCR-CA-联合训练并行小码本引入语义特征
  • wpf之WrapPanel
  • RAG-文本到SQL
  • 国别域名的SEO优势:是否更利于在当地搜索引擎排名?
  • Linux -- 进程间通信【System V共享内存】
  • 软考中级习题与解答——第二章_程序语言与语言处理程序(1)
  • vue社区网格化管理系统(代码+数据库+LW)
  • PRACH物理层详解
  • Flutter Container 阴影设置指南 2025版
  • 【技术选型】大型移动端跨平台应用开发 Flutter VS React Native
  • Web网络开发 -- Vue2基础语法,属性和生命周期
  • 大模型面试题剖析:全量微调与 LoRA 微调
  • TDengine 日期时间函数 DAYOFWEEK 使用手册
  • 特征增强方法【特征构建】
  • 太浅显数学常识暴露太重大数学真相:同样是有首项的无穷数列,此列的项可多于彼列的项
  • 车载卫星通信:让自动驾驶“永不掉线”?
  • STM32项目分享:基于单片机的图书馆座位监测系统
  • Git 版本管理工具基本操作汇总—命令总结
  • 液态神经网络(LNN)2:LTC改进成CFC详细推导过程
  • 使用 BayesFlow 神经网络简化贝叶斯推断的案例分享(二)
  • 液态神经网络:智能制造的新引擎
  • Android Framework打电话禁止播放运营商视频彩铃
  • FastLED库完全指南:打造炫酷LED灯光效果
  • 线程池发生了异常该怎么处理?
  • 多校区学校押金原路退回系统之免安装使用教程——东方仙盟
  • 本地部署开源临时文本分享服务 PrivateBin 并实现外部访问( Windows 版本)
  • AOSP 目录及其作用
  • Minecraft(我的世界)服务器信息查询免费API接口详解
  • golang 14并发编程