硬链接计数只能通过 st_nlink 字段查看,即 os.stat() 后用 fi.sys().(*unix.stat_t) 获取 nlink 值,且需排除符号链接;windows 不支持,仅 unix/linux/macos 有效。

硬链接计数在哪看:st_nlink 是唯一线索
Go 里没有 os.ModeHardLink 这种标志位,os.FileInfo.Mode() 对硬链接和普通文件返回完全一样的值(比如 -rw-r--r--)。真正能反映“这个路径是否属于一个被多次硬链接的文件”的,只有系统 stat 结构里的 Nlink 字段——也就是 inode 的硬链接计数。
它不是“这个链接自己有多少个子链接”,而是“整个 inode 当前被多少个目录项引用”。只要 Nlink > 1,且该路径不是符号链接,那它就是某个硬链接集合中的一员。
-
os.Stat和os.Lstat都能拿到这个值(硬链接无穿透问题,二者结果一致) - 必须用
fi.Sys()取出底层结构,再类型断言为*syscall.Stat_t或*unix.Stat_t(推荐后者,跨平台更稳) - Windows 不支持硬链接,
Nlink值无意义;只在 Unix/Linux/macOS 下有效
为什么 os.ModeSymlink 存在,但 os.ModeHardLink 不存在
符号链接是独立的文件系统对象:它有自己独立的 inode、自己的元数据(大小、权限、mtime)、自己的内容(目标路径字符串)。所以 os.ModeSymlink 是合理的——它是对一种特殊文件类型的标记。
硬链接不是新对象:它只是同一个 inode 在另一个目录里多加了一条目录项(dirent),不占用额外 inode,也没有专属元数据。从文件系统视角看,“原始文件”和“硬链接”根本无法区分——它们就是同一个东西。
- 你删掉“原始文件”,只要还有其他硬链接存在,文件内容就还在
- 你改任意一个硬链接的内容,所有链接看到的都是新内容
- 因此 Go 标准库不提供“识别硬链接”的抽象,只暴露底层事实:
Nlink
获取 Nlink 的实操代码与常见坑
直接调 os.Stat 后对 Sys() 断言是最简路径,但要注意平台差异和错误处理:
fi, err := os.Stat(path)
if err != nil {
return false, err
}
s, ok := fi.Sys().(*unix.Stat_t)
if !ok {
return false, errors.New("not a unix stat struct")
}
isHardLinked := s.Nlink > 1 && fi.Mode()&os.ModeSymlink == 0
- 别用
*syscall.Stat_t:在 macOS 上字段名是Nlink,Linux 是nlink,unix.Stat_t统一了命名 - 必须先排除符号链接:
fi.Mode()&os.ModeSymlink == 0,否则软链接指向的文件若本身也被硬链接,会误判 - 如果
path是挂载点或特殊文件(如/proc下的条目),Nlink可能为 0 或无意义,需结合场景过滤
硬链接计数变化时,Go 程序感知不到自动更新
Nlink 是 stat 系统调用那一刻的快照。它不会监听变化,也不会缓存——每次调 os.Stat 都重新读取内核数据。
- 你用
os.Link新建一个硬链接后,立刻对新路径调os.Stat,Nlink就会 +1 - 但对旧路径再调一次,
Nlink也同步变大(因为指向同一个 inode) - 没有事件驱动机制:想监控硬链接数量变动,只能轮询 + 比对
Nlink,或监听目录变更后重 stat
最易忽略的一点:硬链接计数反映的是“当前有多少个路径名指向这个 inode”,而不是“这个路径是不是‘第一个’创建的”。没有所谓“原始路径”,只有“谁还活着”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











