Linux 中给 nobody 用户设置目录所有权:不要写死用户组

背景

最近有个程序需要以 nobody 用户访问 dirA。最开始我直接把目录的所有者和所属组都写成了 nobody

chown -R nobody:nobody dirA

这条命令在一部分 Linux 发行版上可以正常执行,但换到 Ubuntu 后却报错了:

chown: invalid group: 'nobody:nobody'

原因

不同 Linux 发行版对 nobody 用户的默认组命名并不一致。有些系统使用 nobody:nobody,Ubuntu 通常使用:

nobody:nogroup

可以使用下面的命令查看当前系统中 nobody 用户的 UID、GID 和组信息:

id nobody
getent passwd nobody

因此,把组名写死成 nobody 并不具备可移植性。

之前的解决办法

为了兼容不同发行版,我曾经在 Go 代码中通过 user.Lookup("nobody") 查询用户,再取出 UID 和 GID,最后调用 os.Chown

u, err := user.Lookup("nobody")
if err != nil {
    return err
}

uid, err := strconv.Atoi(u.Uid)
if err != nil {
    return err
}

gid, err := strconv.Atoi(u.Gid)
if err != nil {
    return err
}

if err := os.Chown(path, uid, gid); err != nil {
    return err
}

这个办法能用,但如果只是执行部署脚本,其实不需要为此增加一段 Go 代码。

更简单的办法

GNU chown 支持省略冒号后面的组名:

chown -R nobody: dirA

这里的 nobody: 表示:

  • 将所有者设置为 nobody
  • 将所属组设置为 nobody 用户的登录组。

也就是说,在默认组叫 nobody 的系统上,它会使用 nobody 组;在 Ubuntu 上,它会使用 nogroup 组。命令本身不需要关心具体的组名。

冒号不能省略。下面这条命令只修改所有者,原来的所属组会保持不变:

chown -R nobody dirA

验证

执行后可以检查目录的所有者和所属组:

ls -ld dirA

也可以直接查看名称和数字 ID:

stat -c '%U:%G %u:%g' dirA

总结

当目标是把目录递归设置为某个用户及其默认组时,可以使用:

chown -R 用户名: 目录

这次的命令就是:

chown -R nobody: dirA

这样可以避免写死 nobody 用户对应的组名,也不用在业务代码中手动查询 UID 和 GID。