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

文件检查与拷贝-简化版

本篇继续来学习shell脚本,对上一篇的文件检查与拷贝脚本进行简化修改。

1 功能说明

在Linux系统中,通过一个shell脚本,实现将一个目录中的所有文件(包括子目录中的),拷贝到顶一个指定的目录,要求:

  • 在拷贝前,先检查两个目录中文件是否一样,不一样的才拷贝
  • 执行拷贝时,打印详细的拷贝信息

2 脚本实现

下面分功能模块来讲解脚本。

2.1 源目录与目标目录

这部分和上次的脚本一样,不再详细介绍

2.2 统计源目录的总文件数和总目录数

# 源目录的所有文件
mapfile -t FILES < <(find "$SRC_DIR" -type f)

解释:

  • find "$SRC_DIR" -type f:在 $SRC_DIR 目录及其子目录下查找所有普通文件,并输出它们的路径
  • <(find "$SRC_DIR" -type f):进程替换(process substitution),将 find 命令的输出作为一个临时文件(或管道)提供给其他命令使用
  • mapfile -t FILES < ...:读取文件
    • mapfile 是一个读取文件内容到数组的命令
    • -t 选项表示去除每行末尾的换行符(trim trailing newline)
    • FILES 是要创建的数组变量名
    • < 表示从前面的输入源读取数据

2.3 对比文件是否一样并拷贝

上篇文章,是通过MD5来比较文件是否相同的,会比较耗时,这次通过cmp指令来比较

copyed_count=0
processed_count=0# 递归遍历源目录下所有文件
for src_file in "${FILES[@]}"; do# 计算相对路径rel_path="${src_file#$SRC_DIR/}"dest_file="$DEST_DIR/$rel_path"# 创建目标子目录dest_dir=$(dirname "$dest_file")mkdir -p "$dest_dir"# 比较文件并执行拷贝if [[ ! -f "$dest_file" ]] || ! cmp -s "$src_file" "$dest_file"; then# 执行复制cp -pv "$src_file" "$dest_file" || echo "警告:复制 $rel_path 失败!"echo "copy: $rel_path"(( copyed_count++ ))fi(( processed_count++ ))process=$(( (processed_count * 100 + TOTAL_FILES - 1) / TOTAL_FILES ))printf "\rprocess: [%d/%d] (%d%%)" "$processed_count" "$TOTAL_FILES" "$process"
doneecho -e "\n=== 操作完成,实际需拷贝 $copyed_count 个文件 ==="

2.3.1 遍历每个文件

前面通过mapfile将文件读到了FILES中,然后就可以通过for循环来依次处理文件了

for src_file in "${FILES[@]}"; do#...
done

解释含义:

  • FILES:是数组的名称(在之前的 mapfile 命令中,这个数组被用来存储文件路径)
  • [@]:是数组的扩展标志,作用是展开数组中的所有元素,并且每个元素会被视为一个独立的参数
  • "${FILES[@]}":(带双引号)时,会保留每个元素的原始格式,包括元素中包含的空格

2.3.2 计算路径并创建子目录

从源目录文件的完整路径中提取出相对路径,然后根据目标位置,组成目标文件的路径

# 计算相对路径
rel_path="${src_file#$SRC_DIR/}"
dest_file="$DEST_DIR/$rel_path"# 创建目标子目录
dest_dir=$(dirname "$dest_file")
mkdir -p "$dest_dir"

解释一下:

  • ${变量#模式},从变量值的开头开始匹配 “模式”,并删除最短的匹配部分

    • src_file,存储着文件的完整路径,例如 /home/user/src/docs/readme.txt
    • $SRC_DIR/,作为匹配的前缀模式,例如 /home/user/src/
    • rel_path ,最终得到 docs/readme.txt
  • $DEST_DIR/$rel_path,拼接成目标文件的路径,例如/home/user/src2/docs/readme.txt

  • dirname ,从文件路径中提取其所在的目录部分,例如/home/user/src2/docs

  • mkdir -p "$dest_dir":用于创建目录

    • mkdir:是 “make directory” 的缩写,用于创建新目录的基础命令。
    • -p:是一个常用选项,全称是 “parent”(父目录),作用是递归创建目录
      当需要创建的目录路径中包含不存在的父目录时,-p 会自动创建所有缺失的父目录,而不会报错。
    • "$dest_dir":是要创建的目标目录路径(通常是一个变量),双引号用于处理路径中包含空格或特殊字符的情况

2.3.3 复制文件

# 比较文件并执行拷贝
if [[ ! -f "$dest_file" ]] || ! cmp -s "$src_file" "$dest_file"; then# 执行复制cp -pv "$src_file" "$dest_file" || echo "警告:复制 $rel_path 失败!"echo "copy: $rel_path"(( copyed_count++ ))
fi(( processed_count++ ))
process=$(( (processed_count * 100 + TOTAL_FILES - 1) / TOTAL_FILES ))
printf "\rprocess: [%d/%d] (%d%%)" "$processed_count" "$TOTAL_FILES" "$process"

解释下

  • [[ ! -f "$dest_file" ]] :如果目标文件 $dest_file不存在,则此部分条件为真
    • [[ ... ]] 是 Bash 的高级条件判断语法
    • ! 表示否定
    • -f 是文件测试运算符,用于检查指定路径是否为普通文件
  • ||:逻辑或运算符
  • ! cmp -s "$src_file" "$dest_file"如果源文件 $src_file 与目标文件 $dest_file 内容不同,则此部分条件为真
    • cmp 是用于比较两个文件内容的命令
    • -s:选项表示 “silent”(静默模式),比较结果不同时不输出具体差异,只通过退出状态码表示
      • 若两文件内容完全相同,cmp -s 退出状态码为 0(表示成功)
      • 若内容不同,退出状态码为非 0(表示失败)
    • ! 否定了 cmp -s 的结果

3 完整的脚本

#!/bin/bashSRC_DIR="./curl-8.15.0"   # 源目录路径
DEST_DIR="./curl-8.15.0-test2"  # 目标目录路径# 确保目录路径不以斜杠结尾,避免路径处理问题
SRC_DIR=${SRC_DIR%/}
DEST_DIR=${DEST_DIR%/}# 检查源目录是否存在
if [ ! -d "$SRC_DIR" ]; thenecho "错误:源目录 $SRC_DIR 不存在!"exit 1
fi# 检查目标目录,不存在则创建
if [ ! -d "$DEST_DIR" ]; thenecho "目标目录 $DEST_DIR 不存在,正在创建..."mkdir -p "$DEST_DIR" || { echo "创建目标目录失败!"; exit 1; }
fiecho "源目录: $SRC_DIR"
echo "目标目录: $DEST_DIR"# 统计源目录的总文件数和总目录数
TOTAL_FILES=$(find "$SRC_DIR" -type f | wc -l)
TOTAL_DIRS=$(find "$SRC_DIR" -type d | wc -l)
# 减去源目录本身
TOTAL_DIRS=$((TOTAL_DIRS - 1))echo "源目录总文件数: $TOTAL_FILES"
echo "源目录总目录数: $TOTAL_DIRS"# 源目录的所有文件
mapfile -t FILES < <(find "$SRC_DIR" -type f)copyed_count=0
processed_count=0# 递归遍历源目录下所有文件
for src_file in "${FILES[@]}"; do# 计算相对路径rel_path="${src_file#$SRC_DIR/}"dest_file="$DEST_DIR/$rel_path"# 创建目标子目录dest_dir=$(dirname "$dest_file")mkdir -p "$dest_dir"# 比较文件并执行拷贝if [[ ! -f "$dest_file" ]] || ! cmp -s "$src_file" "$dest_file"; then# 执行复制cp -pv "$src_file" "$dest_file" || echo "警告:复制 $rel_path 失败!"echo "copy: $rel_path"(( copyed_count++ ))fi(( processed_count++ ))process=$(( (processed_count * 100 + TOTAL_FILES - 1) / TOTAL_FILES ))printf "\rprocess: [%d/%d] (%d%%)" "$processed_count" "$TOTAL_FILES" "$process"
doneecho -e "\n=== 操作完成,实际需拷贝 $copyed_count 个文件 ==="

4 测试结果

这里用curl的源码目录进行测试,拷贝一份到curl-8.15.0-test2目录,然后删除一些文件,进行测试:

可以看到有检查到两个目录存在不一样的文件,在确认拷贝后,执行了拷贝。

再次执行脚本,可以看到文件都完全一样了

5 总结

本篇通过一个文件检查与拷贝的实例,介绍了shell脚本的一些语法,并通过实际测试来验证脚本的功能。


文章转载自:

http://QDuguWr2.hmnhp.cn
http://47tXvPYN.hmnhp.cn
http://0cVhsR1K.hmnhp.cn
http://dTGWqJjA.hmnhp.cn
http://k3fH18am.hmnhp.cn
http://HiYeilhr.hmnhp.cn
http://Hun7Ke3e.hmnhp.cn
http://Kw3ebiT9.hmnhp.cn
http://cnc2gYWm.hmnhp.cn
http://7ZBL5lAi.hmnhp.cn
http://z9pbgf7v.hmnhp.cn
http://LTjpNcU0.hmnhp.cn
http://e8OUuLXb.hmnhp.cn
http://9pL7xtZX.hmnhp.cn
http://4iD79UL9.hmnhp.cn
http://SN2HyYev.hmnhp.cn
http://RWuNZoNp.hmnhp.cn
http://toT0M0lf.hmnhp.cn
http://uzpU41uo.hmnhp.cn
http://KpNtbgLb.hmnhp.cn
http://k14qxWF1.hmnhp.cn
http://CR89B2sO.hmnhp.cn
http://MZBg4jTx.hmnhp.cn
http://G6F7GANr.hmnhp.cn
http://Q4ww7mSc.hmnhp.cn
http://9ykuhbyL.hmnhp.cn
http://KifvZcwe.hmnhp.cn
http://Hkj8yatz.hmnhp.cn
http://KOffbwCg.hmnhp.cn
http://qMG6KBWu.hmnhp.cn
http://www.dtcms.com/a/381328.html

相关文章:

  • 电容式原理检测微小位移的技术方案以及芯片方案
  • 嵌入式系统内存分段核心内容详解
  • AI生成内容检测的综合方法论与技术路径
  • 材料基因组计划(MGI)入门:高通量计算与数据管理最佳实践
  • 系统地总结一下Python中关于“遍历”的知识点
  • Android面试指南(九)
  • Halcon编程指南:符号与元组操作详解
  • 嵌入式第五十二天(GIC,协处理器,异常向量表)
  • 嵌入式学习day48-硬件-imx6ul-key、中断
  • 查找算法和递推算法
  • Webman 微服务集成 RustFS 分布式对象存储
  • 基于51单片机的太阳能锂电池充电路灯
  • 【人工智能通识专栏】第十三讲:图像处理
  • 滚动分页查询-通俗解释
  • 电缆工程量计算-批量测量更轻松
  • UDS NRC速查
  • L2-【英音】地道语音语调--语调
  • 13.渗透-.Linux基础命令(五)-用户管理(修改用户密码)
  • 解决串口数据乱序问题
  • 智能化集成系统(IBMS):构建智慧建筑 “中枢大脑” 的全方案
  • 基于游标(Cursor)的方式来实现滚动分页
  • 30.线程的互斥与同步(四)
  • 《没有架构图?用 netstat、ss、tcpdump 还原服务连接与数据流向》
  • 仓颉语言编程入门:第一个 Windows 下的仓颉应用程序
  • 台达A2E
  • 【操作系统核心考点】进程调度算法全面总结:高频题型与易错点解析
  • ethercat在线调试工具
  • python base core partment-day07-异常、模块、包(对零基础小白友好)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘vaex’问题
  • Acrobat JavaScript 代码中的颜色