如何计算tar排除指定内容后待归档文件的总大小(解决特殊字符文件名统计缺失问题)
如何计算tar排除指定内容后待归档文件的总大小(解决特殊字符文件名统计缺失问题)
嘿,我完全懂你现在的困扰——想用tar做备份时精准预估归档大小,但碰到带空格、Unicode甚至换行的特殊文件名就统计不全,之前的方法要么慢到离谱,要么直接漏算部分文件。结合你已经尝试的思路,我给你整理了一个能完美解决这些问题的方案。
问题根源复盘
你之前遇到的核心问题有两个:
- 文件名转义导致漏统计:用
tar cvf /dev/null ...输出的文件名是转义后的(比如把Unicode字符ä转成\344),传给du时根本找不到这些转义后的文件,自然会漏算。 - 换行符作为文件名的坑:用行读取的方式处理文件列表时,碰到含换行符的文件名会直接把一个文件拆成多行,彻底打乱统计逻辑。
而Unix系统里唯一不会出现在文件名中的字符是空字符(\0),所以用空字符分隔文件名是处理所有特殊情况的黄金标准。
解决方案:让tar输出原始空分隔文件名,配合du精准统计
我们的思路是:让tar自己输出它要归档的所有文件的原始路径(无转义),并用空字符分隔,再传给du统计总大小。这样既保证和tar实际归档的文件完全一致,又能完美处理所有特殊文件名。
完整脚本示例
# 配置备份路径和排除规则 backup_files="/etc /home" excludes="--exclude-vcs --exclude-ignore-recursive=.tarignore" target_file="/path/to/your/backup.tar.gz" # 输出tar要归档的所有文件(原始路径,空字符分隔) list_files () { # 利用tar的--to-command特性,让每个待处理文件输出原始路径+空字符 printf '%s\n' "$excludes" "$backup_files" | tar -cSPf - --files-from - --to-command 'printf "%s\0" "$TAR_FILENAME"' > /dev/null } # 计算待归档文件的总大小 compute_size(){ # 用du读取空分隔的文件列表,直接统计总大小 list_files | du -csb --files0-from - | awk '{print $1}' | tail -n 1 } # 执行实际备份(带进度条) run_tar () { total_size=$(compute_size) echo "预估备份大小:$total_size 字节" # 用和统计完全一致的规则执行备份 printf '%s\n' "$excludes" "$backup_files" | tar -cSPf - --files-from - | pv -s "$total_size" | gzip > "$target_file" } # 启动备份流程 run_tar
关键细节解释
list_files函数:- 用
printf把排除规则和备份路径传给tar,保证和实际备份的参数完全一致。 --to-command是GNU tar的特性,会给每个待处理文件执行指定命令,$TAR_FILENAME是tar提供的环境变量,包含原始的、无转义的文件路径。- 用
printf "%s\0" "$TAR_FILENAME"输出路径+空字符,完美规避所有特殊字符的问题。
- 用
compute_size函数:- 用
du --files0-from -读取空分隔的文件列表,直接统计总大小,不需要任何循环,速度快且准确。 awk '{print $1}' | tail -n 1提取最后一行的总大小数值。
- 用
一致性保障:
- 统计和实际备份用的是完全相同的tar参数,确保统计的文件就是最终归档的文件,不会出现预估和实际偏差的问题。
注意事项
- 确保使用GNU版本的tar和du:
--to-command、--files0-from都是GNU工具的特性,BSD系统(比如macOS)默认的工具可能不支持,需要先安装GNU版本(比如用brew安装gnu-tar和coreutils)。 - 关于Unicode文件名的显示问题:如果系统locale不是UTF-8,可能会看到文件名显示乱码,但tar和du是按字节处理路径的,只要文件实际存在,就会正确统计,不影响结果。
- 速度优化:这个方法利用tar的内置逻辑输出文件列表,比之前的循环快很多,百万级文件几分钟就能处理完,和你之前优化后的速度相当,但准确性大幅提升。
备注:内容来源于stack exchange,提问作者Haem
相关产品推荐
相关产品推荐

