批量文件处理:重命名、转换、压缩 | Batch File Processing: Rename, Convert, Compress

章节概述

核心理念:批量文件处理是 Bash 最能体现效率优势的场景——用一行命令处理成百上千个文件,替代手动逐个操作。这类似于 C 语言中用循环遍历文件数组并调用处理函数,但更加简洁高效。


第1节:find + xargs 批量操作

1.1 find 与 xargs 的配合

find 负责定位文件,xargs 负责将结果传递给命令执行,两者配合是批量处理的基础。

# 查找并删除所有 .tmp 文件
find . -name "*.tmp" -type f | xargs rm -f
 
# 安全删除(处理文件名含空格的情况)
find . -name "*.tmp" -type f -print0 | xargs -0 rm -f
 
# 批量压缩日志文件
find /var/log -name "*.log" -mtime +1 -type f | xargs gzip
 
# 统计所有 .py 文件的总行数
find . -name "*.py" -type f | xargs wc -l
 
# 批量设置权限
find . -type f -name "*.sh" | xargs chmod +x
参数说明
-print0用 null 字符分隔输出(处理特殊文件名)
-0xargs 以 null 字符为分隔符
-I {}指定替换占位符
-n N每次传递 N 个参数
-P N并行执行 N 个进程

1.2 并行执行

# 并行压缩(使用 4 个 CPU 核心)
find . -name "*.log" -type f | xargs -P4 -I{} gzip {}
 
# 并行下载多个文件
cat urls.txt | xargs -P5 -I{} wget -q {}
 
# 并行处理图像(使用 ImageMagick)
find . -name "*.png" -type f | xargs -P4 -I{} convert {} -resize 50% small_{}

第2节:rename 批量重命名

2.1 rename 命令

rename 是批量重命名的利器,支持正则表达式。

# 将空格替换为下划线
rename 's/ /_/g' *.txt
 
# 将所有 .jpeg 改为 .jpg
rename 's/\.jpeg$/.jpg/' *.jpeg
 
# 添加前缀
rename 's/^/prefix_/' *.csv
 
# 添加后缀(在扩展名前)
rename 's/(\.[^.]+)$/_modified$1/' *.dat
 
# 转为小写
rename 'y/A-Z/a-z/' *.TXT
 
# 去除文件名中的特殊字符
rename 's/[^a-zA-Z0-9._-]//g' *

2.2 循环重命名

# 顺序编号重命名
i=1; for f in *.jpg; do mv "$f" "photo_$(printf '%03d' $i).jpg"; ((i++)); done
 
# 按日期重命名
for f in *.txt; do
    date_str=$(stat -c %y "$f" | cut -d' ' -f1)
    mv "$f" "${date_str}_${f}"
done
 
# 批量替换空格为下划线
for f in *\ *; do
    mv "$f" "${f// /_}"
done

第3节:批量转换

3.1 图片批量转换

# 使用 ImageMagick 批量转换格式
for f in *.png; do convert "$f" "${f%.png}.jpg"; done
 
# 批量调整大小
mogrify -resize 800x600 *.jpg
 
# 批量添加水印
for f in *.jpg; do
    composite -dissolve 30 -gravity southeast watermark.png "$f" "wm_$f"
done
 
# 批量压缩质量
mogrify -quality 80 *.jpg

3.2 文档批量转换

# 批量转换 Markdown 为 HTML(使用 pandoc)
find . -name "*.md" -type f | xargs -I{} pandoc {} -o "${md%.md}.html"
 
# 批量转换 CSV 为 JSON(使用 jq)
for f in *.csv; do
    header=$(head -1 "$f")
    tail -n +2 "$f" | awk -F',' -v h="$header" 'BEGIN {split(h,a,",")} {printf "{"; for(i=1;i<=NF;i++) printf "\"%s\":\"%s\",", a[i], $i; print "}"}' > "${f%.csv}.json"
done
 
# 批量转换编码
find . -name "*.txt" -exec file {} \; | grep -i "iso-8859" | cut -d: -f1 | xargs -I{} iconv -f ISO-8859-1 -t UTF-8 {} -o "utf8_{}"

第4节:压缩与归档

4.1 tar 归档

# 创建 tar.gz 归档
tar -czf archive.tar.gz /path/to/directory
 
# 创建 tar.bz2 归档(压缩率更高)
tar -cjf archive.tar.bz2 /path/to/directory
 
# 创建 tar.xz 归档(最高压缩率)
tar -cJf archive.tar.xz /path/to/directory
 
# 解压 tar.gz
tar -xzf archive.tar.gz
 
# 查看归档内容(不解压)
tar -tzf archive.tar.gz
 
# 解压到指定目录
tar -xzf archive.tar.gz -C /target/directory
 
# 增量归档
tar -czf backup_$(date +%Y%m%d).tar.gz --newer-mtime="2026-08-20" /data

4.2 gzip 与 bzip2

# gzip 压缩(原文件被替换)
gzip file.txt           # 生成 file.txt.gz
gzip -k file.txt        # 保留原文件
 
# gzip 解压
gunzip file.txt.gz
 
# bzip2 压缩(压缩率更高,速度更慢)
bzip2 file.txt          # 生成 file.txt.bz2
bzip2 -k file.txt       # 保留原文件
 
# xz 压缩(最高压缩率)
xz file.txt             # 生成 file.txt.xz
xz -k file.txt          # 保留原文件
 
# 批量压缩所有日志
find /var/log -name "*.log" -type f -exec gzip {} \;
工具压缩率速度适用场景
gzip中等日常使用、网络传输
bzip2需要高压缩率
xz最高最慢归档存储
zstd现代场景、备份

第5节:C 文件操作对比

Bash 的文件操作与 C 语言的文件 I/O 有直接的对应关系:

// C 语言文件操作
#include <stdio.h>
 
int main() {
    FILE *fp = fopen("data.txt", "r");
    char line[256];
    
    while (fgets(line, sizeof(line), fp)) {
        printf("%s", line);
    }
    
    fclose(fp);
    return 0;
}
# Bash 等价操作
while IFS= read -r line; do
    echo "$line"
done < data.txt
 
# 或使用 cat
cat data.txt
 
# 或使用 while + process substitution
while read -r line; do
    echo "$line"
done < <(cat data.txt)
C 文件操作Bash 等价操作
fopen()< filecat file
fgets()read -r line
fputs()echo "$line" > file
fprintf()printf "%s\n" "$line" > file
fclose()无需显式关闭
fseek()无需(流式处理)
rewind()重新读取重定向

第6节:实战案例

6.1 批量清理项目

#!/bin/bash
# 批量清理脚本:删除编译产物、压缩旧日志、重命名备份
 
# 删除所有 __pycache__ 目录
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null
 
# 删除 .pyc 文件
find . -name "*.pyc" -delete
 
# 压缩 7 天前的日志
find /var/log/myapp -name "*.log" -mtime +7 -exec gzip {} \;
 
# 重命名备份文件(添加日期前缀)
for f in backup_*.tar.gz; do
    mv "$f" "$(date +%Y%m%d)_$f"
done
 
echo "Cleanup completed at $(date)"

6.2 批量文件信息收集

#!/bin/bash
# 收集目录下所有文件的信息
 
echo "文件名,大小(bytes),修改时间,权限" > file_info.csv
 
find . -type f -printf "%f,%s,%T+,%m\n" | sort >> file_info.csv
 
# 统计各类型文件数量
echo ""
echo "=== 文件类型统计 ==="
find . -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn | head -10
 
# 统计总大小
echo ""
echo "=== 目录大小统计 ==="
du -sh */ 2>/dev/null | sort -rh

6.3 批量编码转换

#!/bin/bash
# 批量将 Windows 编码文件转换为 UTF-8
 
for f in *.txt; do
    encoding=$(file -bi "$f" | grep -oP 'charset=\K[^ ]+')
    if [ "$encoding" != "utf-8" ] && [ "$encoding" != "us-ascii" ]; then
        echo "Converting $f from $encoding to UTF-8"
        iconv -f "$encoding" -t UTF-8 "$f" > "${f}.utf8" && mv "${f}.utf8" "$f"
    fi
done
 
echo "All files converted to UTF-8"