问题场景描述
在日常开发中,我们经常需要处理大量文件的批量操作。最近遇到一个典型需求:
原始文件名格式: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
这种方案能更好地处理包含特殊字符的文件名。