Go archive/tar 怎样读取 tar 目录项的类型标志
Go 读取 tar 成员类型时,先调用 tar.Reader.Next() 取得 Header,再比较 hdr.Typeflag 与 tar.TypeDir、tar.TypeReg 等常量。如果这里只想判断“是不是目录”,可以使用 hdr.FileInfo().IsDir();需要区分普通文件、链接或其他类型时,直接 switch Typeflag 更合适。
这里的“目录项”指 tar 归档中的成员,不是本机目录扫描返回的 os.DirEntry。本文只生成归档清单,不解包、不创建目录,也不根据链接目标继续读取磁盘。
官方文档:https://pkg.go.dev/archive/tar
做归档清单时,先明确要区分几种成员
仅统计目录数量,比较 TypeDir 就够了;需要把归档内容交给后续业务,最好保留细分类别。普通文件、硬链接和符号链接都可能“不是目录”,但三者的数据和目标语义并不相同。把所有非目录项都当成普通文件,会在链接或特殊成员上做出错误选择。
// hdr必须来自一次成功的Next调用,不能忽略其错误。
if hdr.Typeflag == tar.TypeDir {
// 此处只处理归档内的目录成员,不触碰磁盘。
fmt.Printf("目录:%q\n", hdr.Name)
}
上面是需放进读取循环的片段,不是独立程序。TypeDir 的值是字符 '5',不是整数 5;应使用具名常量避免混淆。Typeflag 也不是 Mode 权限位,不要用 0755 这样的权限数字判断成员类型。

Typeflag、IsDir 与名称后缀怎样选择
| 方式 | 适合的任务 | 不足 |
|---|---|---|
| switch hdr.Typeflag | 区分目录、普通文件、硬链接、符号链接及其他成员 | 需要保留未知类型分支 |
| hdr.FileInfo().IsDir() | 只需要目录布尔值的通用文件信息接口 | false不能说明一定是普通文件 |
| 检查 Name 是否以 / 结尾 | 观察命名约定或研究旧格式 | 不是完整的tar类型判断方案 |
建议把 Typeflag 作为 tar 语义分类入口,FileInfo 作为需要 fs.FileInfo 时的适配。FileInfo 来源仍是 Header,不会去本地文件系统判断路径是否存在,也不会跟随归档里的符号链接检查目标是不是目录。
名称末尾的斜杠与目录有关联,但不要据此覆盖读取器给出的类别。即使某个生产工具始终在目录名后加斜杠,这也是该工具的输出习惯,不是你可以忽略 Typeflag 的理由。
一段只读清单程序,未知标志也不丢失
下面完整程序从标准输入读取未压缩 tar 流,逐项输出名称、类型、原始返回标志、Size 和 Linkname。代码未实际运行,展示的是接口使用方法,不含运行日志。
package main
import (
"archive/tar"
"fmt"
"io"
"os"
)
// entryKind 保留不同成员类型,不把非目录统称为普通文件。
func entryKind(flag byte) string {
switch flag {
case tar.TypeDir:
return "目录"
case tar.TypeReg, tar.TypeRegA:
// TypeRegA是旧式值,Reader通常已将它规范化。
return "普通文件"
case tar.TypeLink:
return "硬链接"
case tar.TypeSymlink:
return "符号链接"
case tar.TypeChar:
return "字符设备"
case tar.TypeBlock:
return "块设备"
case tar.TypeFifo:
return "FIFO"
default:
return "其他类型"
}
}
// listEntries 只读取元数据,不创建文件,不跟随链接。
func listEntries(r io.Reader, w io.Writer) error {
tr := tar.NewReader(r)
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil // 这是归档结束,不是单个成员内容结束。
}
if err != nil {
return fmt.Errorf("读取tar成员: %w", err)
}
// %q转义名称中的换行等字符;十六进制标志便于保留未知类型。
if _, err := fmt.Fprintf(w, "%q 类型=%s 标志=0x%02x 大小=%d 链接=%q\n",
hdr.Name, entryKind(hdr.Typeflag), hdr.Typeflag, hdr.Size, hdr.Linkname); err != nil {
return fmt.Errorf("输出清单: %w", err)
}
// 不读取正文;下一次Next会丢弃当前成员未读的数据。
}
}
func main() {
// 标准输入、输出由调用方管理;本程序不关闭它们。
if err := listEntries(os.Stdin, os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
读者可将完整程序保存为 main.go,并自行准备可信的 sample.tar。下面命令只用于读取,不会提取成员:
# 将未压缩tar送入清单程序,不会写出归档成员。 go run main.go
输出中的 0x35 对应字符 '5',0x30 对应字符 '0'。将标志打印成十六进制,比打印不可见的零字节更容易观察。名称和链接使用 %q,避免其中的换行或控制字符直接破坏清单展示。
Next 返回 io.EOF 表示归档已结束;其他错误直接返回,不能用 continue 掩盖。当前程序对包括 ErrInsecurePath 在内的非 EOF 错误采用停止策略,不使用带错误返回的 Header。输出端错误也会向上传递。

读取器返回的标志,不一定等于磁盘上的原始字节
旧格式的 TypeRegA 是零字节,当前文档已将该常量标为弃用,编写新 Header 应使用 TypeReg。Go 的读取器实现会处理这种旧式标志:名称以 / 结尾时改为 TypeDir,否则改为 TypeReg。因此,正常经过 Reader.Next 后,通常不需要依靠 TypeRegA 分支猜目录。
清单程序仍显式兼容 TypeRegA,是为了让 entryKind 对单独构造的 Header 值也有可读解释;它不意味着 Next 一定会原样暴露旧字节。如果任务是取证式逐字节查看归档头,本例这种逻辑清单接口并不适合。
PAX 扩展记录、GNU 长名称记录等也会由读取器处理,不能假定每次 Next 都对应一个完全未经解释的原始头块。这里不扩展实现二进制解析器,只依据 Next 提供的 Header 解释成员。
读取器实现:https://go.dev/src/archive/tar/reader.go
链接不是目标内容,目录也不是空普通文件
tar.TypeLink 表示硬链接,tar.TypeSymlink 表示符号链接,链接目标保存在 Linkname。目标字符串不是本程序应该打开的磁盘路径;只做清单时,保留它即可。只看 Size 是否为零无法区分目录、空普通文件与链接。
对于 TypeDir、TypeLink、TypeSymlink 等头信息类型,Reader.Read 不会因此提供目标文件内容。清单程序无需把正文读到内存;Next 会自动丢弃前一成员尚未读取的数据。但底层流仍可能发生读取与跳过,不能将“没复制正文”理解成零I/O,也不能据此宣称极大归档处理没有资源代价。
未知标志使用“其他类型”并保留标志值。这个输出策略只适合观察;如果未来增加解包功能,应明确允许哪些类别,不能把 default 自动变成普通文件写入。Typeflag 分类本身也不解决路径约束、链接处理和大小上限问题。
用几种小成员复现分类,再决定接口
下列是供读者自行构造样本时核对的预期,不是已执行测试报告:
| 输入成员语义 | 清单应关注的值 | 不能据此推断 |
|---|---|---|
| 显式TypeDir目录 | Typeflag为TypeDir | 磁盘上已创建目录 |
| 零长度TypeReg文件 | 普通文件,Size为0 | Size为0就是目录 |
| TypeSymlink成员 | 符号链接,保留Linkname | 链接目标存在或可访问 |
| TypeLink成员 | 硬链接,保留Linkname | 目标内容可以直接从该成员读取 |
| 损坏归档或输出端失败 | 返回错误并停止 | 已成功完整列出全包 |
为什么 IsDir 为 false 仍不能直接读成文件?
因为非目录包含链接、设备及其他类型。需要普通文件时应明确判断 TypeReg;对稀疏文件等扩展类别是否支持,应另外定义业务策略。
为什么循环里没有 io.Copy?
任务只是读取元数据,Next 会负责跨过剩余正文。需要消费成员内容时才在成功取得 Header 后读取当前 Reader,并处理该成员读取错误。
tar.gz 可以直接传给这个程序吗?
不可以,当前输入约定是未压缩 tar。压缩容器需要先由相应解压读取层提供 tar 字节流,本例不把压缩格式识别与成员分类混在一起。
最终选择可以很简短:只问“是不是目录”用 IsDir 或 TypeDir;需要完整 tar 类别用 Typeflag;名称后缀只作为观察信息。让分类、内容读取和落盘操作分别承担自己的职责,清单逻辑就不会被解包副作用拖复杂。
Go embed 怎样处理嵌入文件不存在的错误
- 上一篇
- Go embed 怎样处理嵌入文件不存在的错误
- 下一篇
- Go archive/tar Header.AccessTime 怎样保留文件访问时间
-
- Golang · Go教程 | 44分钟前 | Go教程 · Go 日志级别 log/slog slog.Handler LevelVar HandlerOptions.Level Enabled
- Go log/slog 怎样让 Handler 过滤低级别日志
- 272浏览 收藏
-
- Golang · Go教程 | 45分钟前 | 日志 · 标准库 · go · Go 结构化日志 ReplaceAttr log/slog HandlerOptions
- Go log/slog ReplaceAttr 怎样统一改写时间和来源字段
- 333浏览 收藏
-
- Golang · Go教程 | 47分钟前 |
- Go log/slog 怎样为 slog 添加请求上下文字段
- 252浏览 收藏
-
- Golang · Go教程 | 50分钟前 | 错误处理 · Go教程 · database/sql · Go commit 事务 rollback database/sql sql.Tx BeginTx
- Go database/sql 怎样让事务回滚覆盖提前返回路径
- 249浏览 收藏
-
- Golang · Go教程 | 52分钟前 | go · 数据库 · Go database/sql ColumnType Rows.ColumnTypes
- Go database/sql 怎样用 RowsColumnTypes 获取列元数据
- 171浏览 收藏
-
- Golang · Go教程 | 55分钟前 | Go教程 · database/sql · Go database/sql rows.Next Rows.Scan sql.RawBytes 二进制字段
- Go sql.RawBytes 为什么只能在下一次 Next 前使用
- 159浏览 收藏
-
- Golang · Go教程 | 59分钟前 |
- Go reflect 怎样区分 IsZero 和无效反射值
- 477浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go reflect 怎样判断反射值是否可寻址
- 485浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go reflect 怎样用 StructOf 构造临时结构体
- 335浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go runtime 怎样读取进程内存统计快照
- 366浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 513次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 572次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 598次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 550次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 368次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览
