我经常想要获取与用户 ID 相关联的登录名,并且因为它已被证明是一个常见的用例,所以我决定编写一个 shell 函数来执行此操作。虽然我主要使用 GNU/Linux 发行版,但我尝试将我的脚本编写为尽可能可移植,并检查我所做的是否与 POSIX 兼容。
解析/etc/passwd
我尝试的第一种方法是解析/etc/passwd
(使用awk
)。
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
但是,这种方法的问题是登录可能不是本地的,例如,用户身份验证可能通过 NIS 或 LDAP。
使用getent
命令
使用getent passwd
比解析更便携,/etc/passwd
因为这也查询非本地 NIS 或 LDAP 数据库。
getent passwd "$uid" | cut -d: -f1
不幸的是,getent
POSIX 似乎没有指定该实用程序。
使用id
命令
id
是用于获取有关用户身份的数据的 POSIX 标准化实用程序。
BSD 和 GNU 实现接受用户 ID 作为操作数:
这意味着它可用于打印与用户 ID 关联的登录名:
id -nu "$uid"
但是,在 POSIX 中没有指定提供用户 ID 作为操作数;它只描述了使用登录名作为操作数。
结合以上所有
我考虑将上述三种方法组合成如下内容:
get_username(){
uid="$1"
# First try using getent
getent passwd "$uid" | cut -d: -f1 ||
# Next try using the UID as an operand to id.
id -nu "$uid" ||
# As a last resort, parse `/etc/passwd`.
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
}
然而,这很笨重、不优雅,而且——更重要的是——不够健壮;如果用户 ID 无效或不存在,它将以非零状态退出。在我编写一个更长更笨重的 shell 脚本来分析和存储每个命令调用的退出状态之前,我想我会在这里问:
是否有一种更优雅、更便携(POSIX 兼容)的方式来获取与用户 ID 关联的登录名?
一种常见的方法是测试您想要的程序是否存在并且是否可以从您的
PATH
. 例如:因为 POSIX
id
不支持 UID 参数,所以elif
for 子句id
不仅要测试是否id
在 PATH 中,还要测试它是否会无错误地运行。这意味着它可能会运行id
两次,幸运的是这不会对性能产生明显影响。也有可能同时运行id
和awk
,但对性能的影响同样可以忽略不计。顺便说一句,使用这种方法,无需存储输出。只有其中一个会运行,因此只有一个会打印输出以供函数返回。
POSIX 中除了
id
. 尝试id
并回退到解析/etc/passwd
可能与实践中一样可移植。BusyBox
id
不接受用户 ID,但带有 BusyBox 的系统通常是自治的嵌入式系统,解析/etc/passwd
就足够了。如果您遇到不接受用户 ID 的非 GNU 系统
id
,您也可以尝试getpwuid
通过 Perl 调用,如果它可用的话:或 Python:
POSIX 指定
getpwuid
作为标准 C 函数在用户数据库中搜索用户 ID,从而允许将 ID 转换为登录名。我下载了 GNU coreutils的源代码,并且可以看到在他们的实用程序实现中使用了这个函数,例如id
和ls
.作为一个学习练习,我编写了这个简单粗暴的 C 程序来简单地充当这个函数的包装器。请记住,自大学以来(多年前)我就没有用 C 编程过,我不打算在生产中使用它,但我想我会把它贴在这里作为概念证明(如果有人想编辑它,随意):
我没有机会使用 NIS/LDAP 对其进行测试,但我注意到如果同一用户中有多个条目
/etc/passwd
,它会忽略除第一个之外的所有条目。示例用法:
一般来说,我建议不要这样做。从用户名到 uid 的映射不是一对一的,并且您可以从 uid 转换回来以获取用户名的编码假设会破坏事情。例如,我经常通过使容器中的
passwd
和group
文件将所有用户和组名映射到 id 0 来运行完全无 root 的用户命名空间容器;这允许安装软件包工作而不会chown
失败。但是,如果某些东西试图将 0 转换回 uid 并且没有得到它所期望的,它将无缘无故地中断。因此,在本例中,您应该转换为 uid 并在该空间中进行比较,而不是转换回和比较用户名。如果您确实需要执行此操作,如果您是 root,则可以通过制作一个临时文件,
chown
将其写入 uid,然后使用ls
读取并解析所有者的名称来进行半便携操作。但是我只会使用一种众所周知的方法,这种方法不是标准化的,而是“在实践中可移植的”,就像您已经找到的一种方法一样。但同样,不要这样做。有时很难做的事情是向您发送消息。