gopsutil/disk 无法直接读取分区 uuid,因其仅返回挂载信息而不解析文件系统 superblock;linux 下应通过 /dev/disk/by-uuid/ 符号链接结合挂载点反查,稳定可靠且无需额外依赖。

用 gopsutil/disk 读不到分区 UUID?先确认你查的是哪个设备
Linux 下磁盘分区的 UUID 是文件系统级属性,不是块设备本身自带的。很多同学直接对 /dev/sda1 调用 disk.Partitions(true) 后翻遍返回的 PartitionStat 字段,发现没有 UUID 字段——这很正常,gopsutil 默认只返回挂载点、文件系统类型、挂载选项等元信息,不主动读取文件系统 UUID。
- UUID 存在位置:实际存储在文件系统 superblock 中(如 ext4 的
e2fsprogs工具链可读),需额外调用外部命令或解析/proc/mounts+/dev/disk/by-uuid/符号链接 -
disk.Partitions()返回的Mountpoint字段才是关键入口,UUID 必须通过挂载点反查 - Windows/macOS 不提供标准 UUID 概念,
gopsutil在这些平台返回空字符串或错误,别硬套 Linux 逻辑
Linux 下最稳的 UUID 获取方式:走 /dev/disk/by-uuid/ 符号链接
绕过复杂解析和权限问题,直接利用内核已构建好的映射关系。只要挂载点存在且有对应 UUID,这个路径下必有指向真实设备的软链。
- 步骤:从
disk.Partitions(true)拿到挂载点(如"/"),拼出/dev/disk/by-uuid/*下所有链接,用os.Readlink()反解目标路径,再比对是否指向该挂载点对应设备 - 注意:同一个 UUID 可能对应多个挂载点(如 bind mount),但符号链接名就是 UUID 本身,所以只需提取链接名即可
- 示例片段:
for _, p := range partitions { if p.Mountpoint == "/" { files, _ := filepath.Glob("/dev/disk/by-uuid/*") for _, f := range files { target, err := os.Readlink(f) if err != nil || !strings.HasSuffix(target, p.Device) { continue } uuid := filepath.Base(f) // 就是 UUID 字符串 fmt.Println("root fs UUID:", uuid) break } } }
别用 blkid 命令封装 —— 权限、超时、环境依赖太坑
有人习惯 exec.Command("blkid", "-o", "value", "-s", "UUID", "/dev/sda1"),看似直接,但线上环境容易崩:
- 容器中常无
blkid命令,或权限被 seccomp 禁掉,报exec: "blkid": executable file not found in $PATH - 某些精简镜像(如 distroless)根本没
util-linux包,blkid不存在 - 设备忙时
blkid可能卡住几秒,没设cmd.Start()超时会拖垮整个健康检查 - 输出格式不稳定:加
-p参数换机器可能变,不同发行版默认行为也不同
Go 标准库能搞定的,就别碰 CGO 或第三方解析库
有方案建议用 github.com/diskfs/go-diskfs 解析 ext4 superblock,或开 CGO 调 libblkid —— 大可不必。
立即学习“go语言免费学习笔记(深入)”;
-
go-diskfs目前不支持 XFS/Btrfs,而生产环境常见;且需 root 权限读裸设备,违反最小权限原则 - CGO 开启后交叉编译失效,Docker 构建多平台镜像直接失败
-
/dev/disk/by-uuid/是内核保证的稳定 ABI,只要挂载了,它就在,无需额外依赖 - 唯一例外:未挂载的分区(如新磁盘未格式化/未挂载),此时确实无法通过该路径获取,但这种场景本就不该由运行时程序处理
真正麻烦的是那些挂载了却没生成 UUID 的文件系统(比如某些 FAT32 分区),或者用了 noauto 选项导致未自动挂载——这时候连 Partitions() 都列不出来,得先确认设备状态,而不是死磕 UUID。










