Linux下使用正则表达式批量复制并重命名文件的实践指南


阅读 7 次

问题场景描述

在日常开发中,我们经常需要处理大量文件的批量操作。最近遇到一个典型需求:

原始文件名格式:prefix_12345678901_12345678901_suffix.ext
目标文件名格式:1234567890_1234567890.ext

要求将源目录下所有符合该模式的文件复制到目标目录,并完成重命名,同时避免覆盖目标目录已存在的文件。

常规解决方案的不足

很多开发者首先想到的是分两步操作:

# 第一步:复制所有文件
cp -n source/*.ext target

# 第二步:批量重命名
rename 's/.*([0-9]{10}_[0-9]{10}).*\.(.*$)/$1.$2/' *.ext

但这种方案存在明显缺陷:会先执行复制操作,之后才发现文件可能已存在,造成不必要的I/O操作。

优化的一步到位方案

更高效的做法是使用Bash循环结合正则表达式匹配,实现复制和重命名一步完成:

for i in /source/*.ext; do 
    if [[ "$i" =~ ([0-9]{10})_([0-9]{10}) ]]; then 
        target="/target/${BASH_REMATCH[1]}_${BASH_REMATCH[2]}.ext"
        [ -f "$target" ] || cp "$i" "$target"
    fi
done

方案解析

这个方案有几个关键改进点:

  • 使用BASH_REMATCH数组捕获正则分组
  • 先检查目标文件是否存在再决定是否复制
  • 精确匹配10位数字(原需求11位可能是笔误)

扩展应用场景

类似方法可以应用于多种文件处理场景,比如:

# 处理不同日期格式的文件
for file in data_*.log; do
    if [[ "$file" =~ ([0-9]{4})([0-9]{2})([0-9]{2}) ]]; then
        newname="${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}.log"
        mv "$file" "$newname"
    fi
done

注意事项

在实际使用时需要注意:

  • 确保目标目录存在且有写入权限
  • 正则表达式要严格测试,避免匹配错误
  • 大量文件处理时考虑使用xargs并行处理

性能优化建议

当文件量特别大时(10万+),可以考虑:

find /source -name "*.ext" -print0 | while IFS= read -r -d '' file; do
    if [[ "$file" =~ ([0-9]{10})_([0-9]{10}) ]]; then
        target="/target/${BASH_REMATCH[1]}_${BASH_REMATCH[2]}.ext"
        [ -f "$target" ] || cp "$file" "$target"
    fi
done

这种方案能更好地处理包含特殊字符的文件名。