- 灾备
- CLI
- 存储
【免费下载链接】bup
Very efficient backup system based on the git packfile format, providing fast incremental saves and global deduplication (among and within files, including virtual machine images). Please post problems or patches to the mailing list for discussion (see the end of the README below).
bup cat-file是 bup 备份套件中的一个低级(low-level)内容提取工具,它绕过高层的bup-restore/bup-join封装,按/branch/revision/path形式的路径直接从仓库归档中取出普通文件的数据、单条元数据条目或整个目录的.bupm元数据文件,并原样输出到标准输出。本文以 bup-cat-file.1.md 手册为核心,结合 cat_file.py、vfs.py 的实现与 test-cat-file 测试,完整讲解该命令的三种工作模式、路径语法、与bup meta/bup join的配合用法及其底层原理,让你能直接用它做数据审计、元数据比对与归档内部结构检查。
命令定位:归档内容提取的低层通道
在 bup 的命令体系中,bup cat-file属于“其他命令”(见 bup(1) 手册 的 OTHER COMMANDS 一节),官方定位是 “Extract archive content”(提取归档内容)。
从 cat_file.py 的 optspec 可以看出它的完整用法:
bup cat-file [--meta|--bupm] /branch/revision/[path] -- meta print the target's metadata entry (decoded then reencoded) to stdout bupm print the target directory's .bupm file directly to stdout它的设计目标很明确:
- 默认模式:把path指向的普通文件(regular file)的真实数据内容以原始字节流输出到标准输出,适合重定向到文件或管道交给其他程序处理;
--meta模式:输出与path关联的单条元数据条目(解码后重新编码的版本);--bupm模式:输出与path(必须是目录)关联的整个.bupm元数据文件原始字节。
需要注意,cat-file是面向脚本与底层操作的接口,它不做恢复路径映射、不重新组织目录树,只负责“把归档里的某个对象按路径取出来”。这也决定了它与bup-restore(完整恢复)和bup-join(按对象拼接内容)的差异。
路径语法:/branch/revision/[path]
bup cat-file的参数路径必须包含**分支(branch)与修订(revision)**两部分。源码中的校验逻辑在 cat_file.py:
if not re.match(br'/*[^/]+/[^/]+', target): o.fatal(f"path {pm(target)} doesn't include a branch and revision")也就是说,形如src/latest/src/foo的路径会被拆解为:
src:分支名(即bup save -n src创建的命名分支);latest:修订标识。latest是 git 风格引用,指向该分支最近一次 save;也支持mybackup~1这种 git 父提交记法(见 bup-join.1.md 的示例);src/foo:分支内的归档路径(相对路径段,可省略,省略时指向该修订的根)。
路径解析由 vfs.resolve() 完成,cat-file调用时传入了follow=False,即:如果路径最后一个元素是符号链接,不要跟随解析,直接返回该链接项。这与bup ls的默认行为一致,保证提取到的是归档中记录的原始条目。解析过程中的关键行为还包括:
- 路径中若有不存在的段,结果里对应项为
None,cat-file会报错cannot access ...并退出; - 沿路径遇到的符号链接(包括末尾段)默认仍会被解析,若符号链接层数过多会抛出类似 ELOOP 的 VFS IOError;
want_meta=True时每个结果项尽量携带详细元数据;如果项的元数据已丢失,item.meta会退化为一个整数 mode。
三种工作模式详解
--meta与--bupm互斥,cat_file.py 会直接拒绝同时使用:
if opt.bupm and opt.meta: o.fatal('--meta and --bupm are incompatible')默认模式:提取普通文件内容
不指定任何选项时,bup cat-file取出path指向文件的数据并写到 stdout。实现要点(cat_file.py):
if stat.S_ISREG(mode): with vfs.fopen(repo, leaf_item) as f: for b in chunkyreader(f): out.write(b) else: o.fatal(f'{pm(target)} is not a plain file')- 只有
S_ISREG(mode)(普通文件)才允许提取;若目标是目录、符号链接、fifo 等,会报is not a plain file错误; - 读取通过 vfs.fopen() 打开
_FileReader,再用chunkyreader分块写出——这样即使是经过 hashsplit 分块的超大型文件,也能以稳定内存流式输出; - 输出走
byte_stream(sys.stdout),保证在二进制数据下 stdout 不被文本包装破坏(见 io.py)。
由于内容会原样输出,典型用法是重定向:
$ bup cat-file /foo/latest/somefile > somefile-content--meta:提取单条元数据条目
--meta输出path关联的元数据条目,但文档特别提醒:它返回的不是归档中.bupm里记录的原始字节,而是“解码后重新编码”的版本(decoded and then re-encoded)。实现位于 cat_file.py:
augmented = vfs.augment_item_meta(repo, leaf_item, include_size=True) out.write(augmented.meta.encode())- vfs.augment_item_meta() 确保
item.meta是一个完整的Metadata实例:如果元数据只剩整数 mode,就构造一个兼容的“伪造”Metadata;当include_size=True时会顺带把缺失的文件大小计算出来补上; Metadata.encode()(见 metadata.py)再将其序列化为标准元数据归档格式,输出内容可被bup meta --list直接解析。
因此--meta的输出在字节层面未必与原始.bupm中的对应条目一致(例如重编码可能导致字段顺序、路径表示或时间戳细节的规范化差异),但语义等价。若确实需要原始字节,文档给出的替代方案是:用--bupm提取父目录的.bupm,再在输出中定位相关条目。
--bupm:提取目录的原始 .bupm 文件
.bupm是 bup 为每个被保存目录生成的元数据文件(bup metadata),存放在该目录对应的 git tree 中。--bupm直接把它以原始字节输出,前提是path必须指向目录:
if not stat.S_ISDIR(mode): o.fatal(f'{pm(target)} is not a directory') _, bupm_oid = vfs.tree_data_and_bupm(repo, leaf_item.oid) if bupm_oid: with vfs.tree_data_reader(repo, bupm_oid) as meta_stream: out.write(meta_stream.read())- vfs.tree_data_and_bupm() 展开 tree(若 oid 是 commit 会先取其 tree),在条目中查找名字恰为
.bupm的子树项并返回其 blob oid;若该树没有.bupm(例如较旧版本的 bup save,或非 bup 写入的 tree),bupm_oid为None,此时命令静默不输出任何内容; - vfs.tree_data_reader() 返回针对该 oid 的读取器,这里读出的是
.bupm的完整原始字节流。
元数据归档格式与 bup meta 的配合
bup cat-file --meta与--bupm的输出都是 bup 元数据归档格式,因此最常见的下游处理就是管道给bup meta做展示。bup meta的完整能力见 bup-meta.1.md:
bup meta --create (-c):为一批路径创建元数据归档;bup meta --list (-t):展示归档中的元数据信息,-vvf表示更详细输出并从 stdin 读取;bup meta --extract (-x)/--start-extract/--finish-extract:把元数据应用到文件系统;bup meta --edit:改写归档中的 uid/gid/user/group 等字段;- 常用辅助选项:
-R/--recurse(递归)、--numeric-ids、--symlinks/--no-symlinks、--paths/--no-paths、-f/--file("-" 表示 stdin/stdout)、-v、-q。
因此下面两条命令可以组合出“可读的元数据审计输出”:
# 查看 /foo/latest/something 这一项元数据的可读列表 $ bup cat-file --meta /foo/latest/something | bup meta -tvvf - # 查看 somedir 目录及其包含的所有条目(含子项)的元数据 $ bup cat-file --bupm /foo/latest/somedir | bup meta -tvvf -实战示例:完整的使用场景
场景一:提取某个历史版本中的文件内容
# 保存一次备份 $ bup init $ bup index src $ bup save -n src --strip-path "$(pwd)" src # 取回 latest 修订中 src/foo 的内容并落盘 $ bup cat-file src/latest/src/foo > cat-foo $ diff -u src/foo cat-foo # 应无差异这正是 test-cat-file 中验证的核心路径:cat-file的输出必须与原始文件逐字节一致。
场景二:提取并比对单条元数据
$ bup meta --create --no-paths src/foo > src-foo.meta $ bup cat-file --meta "src/latest/src/foo" > cat-foo.meta # 排除 atime 等易变字段后逐行比对 $ bup meta -tvvf src-foo.meta | grep -vE '^atime: ' > src-foo.list $ bup meta -tvvf cat-foo.meta | grep -vE '^atime: ' > cat-foo.list $ diff -u src-foo.list cat-foo.list测试用--no-paths创建参考元数据并与--meta输出比对,验证了--meta输出的语义等价性(见 test-cat-file)。
场景三:校验 .bupm 原始字节
$ bup cat-file --bupm "src/latest/src/" > bup-cat-bupm $ src_hash=$(bup ls -s "src/latest/" | cut -d' ' -f 1) $ bupm_hash=$(git ls-tree "$src_hash" | grep -F .bupm | cut -d' ' -f 3 | cut -d' ' -f 1) $ dev/git-cat-tree "$bupm_hash" > git-cat-bupm $ cmp git-cat-bupm bup-cat-bupmtest-cat-file 正是这样做的:用git ls-tree从目录 tree 里定位.bupm的 blob oid,再用仓库自带的 git-cat-tree 取出原始 blob,最后cmp确认与bup cat-file --bupm的输出完全一致——这证明了--bupm返回的是未经过任何重编码的原始字节。
与 bup join 的对比:何时用哪一个
bup cat-file与bup join都能从仓库取内容,但分工不同。根据 bup-join.1.md:
bup join是bup-split的逆向操作,接受任何 git 能识别的引用格式(分支名、commit id、tree id、blob id),把拆分的对象重新拼接成完整内容;也支持-r/--remote从远程仓库(默认 SSH)取数据。它不关心路径语义,只关心对象;bup cat-file需要/branch/revision/path的路径语义,内部走 vfs 解析,能区分文件/目录/符号链接,还提供元数据层面的--meta/--bupm能力。
一句话总结:按对象拿内容用bup join,按归档路径拿内容或拿元数据用bup cat-file。前者见 bup-join.1.md 的 tar 管道示例,后者就是本文的主线。
错误处理与使用约束
结合 cat_file.py 与 test-cat-file,bup cat-file的失败路径很明确:
| 触发条件 | 报错信息 |
|---|---|
| 未指定目标参数 | must specify a target |
| 指定了多个目标 | only one target file allowed |
同时使用--meta与--bupm | --meta and --bupm are incompatible |
| 路径缺少分支/修订 | path x doesn't include a branch and revision |
| 路径在归档中不存在 | cannot access /x in x/y(退出码 2) |
--bupm指向非目录 | ... is not a directory |
| 默认模式指向非普通文件 | ... is not a plain file |
运行前提:必须先有可用的 bup 仓库(bup init初始化过),因为 cat_file.py 一进来就会执行git.check_repo_or_die();仓库通过LocalRepo()打开。另外两点细节:
--meta输出是重编码结果,需要“字节级一致”时请改用--bupm方案;- 旧版 bup save 或非 bup 创建的 tree 没有
.bupm,此时--bupm静默输出空。
总结
bup cat-file是 bup 中面向脚本与深度审计的低层提取工具:默认模式以流式方式输出普通文件的原始数据,--meta输出解码重编码的单条元数据,--bupm输出目录的原始.bupm字节。结合 vfs.py 的路径解析与元数据增强逻辑、metadata.py 的归档格式,以及 test-cat-file 对三种模式(含字节级一致性)的完整验证,你可以放心地把它用于备份内容比对、元数据审计和归档结构检查。想继续深入,可阅读 bup-meta.1.md 了解元数据归档的完整操作面,或通过 bup-join.1.md 掌握按对象拼接内容的另一条路径。
- 灾备
- CLI
- 存储
【免费下载链接】bup
Very efficient backup system based on the git packfile format, providing fast incremental saves and global deduplication (among and within files, including virtual machine images). Please post problems or patches to the mailing list for discussion (see the end of the README below).
相关推荐
Nix 的 `nix nar cat` 命令完全指南:从 NAR 归档中提取文件内容
Nix 的 nix nar cat 命令完全指南:从 NAR 归档中提取文件内容 导读 nix nar cat 是 Nix 包管理器中用于 从 Nix Arch
包管理器开发工具CLI构建工具Haystack Extractors 组件深度指南:NER 实体抽取、LLM 元数据提取与图像文档内容抽取
Haystack Extractors 组件深度指南:NER 实体抽取、LLM 元数据提取与图像文档内容抽取 本文聚焦 Haystack 开源 AI 编排框架中
人工智能大模型RAGAI AgentNLPRufus数据提取终极指南:如何快速获取归档文件内容
Rufus数据提取终极指南:如何快速获取归档文件内容 Rufus作为一款可靠的USB格式化工具(The Reliable USB Formatting Util
桌面应用开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考