获取与用户 ID 关联的用户名的 POSIX 兼容方式

Ant*_*gan 28 users posix

我经常想获得与用户 ID 关联的登录名,并且因为它被证明是一个常见的用例,所以我决定编写一个 shell 函数来做到这一点。虽然我主要使用 GNU/Linux 发行版,但我尝试将脚本编写为尽可能可移植,并检查我所做的是否与 POSIX 兼容。

解析 /etc/passwd

我尝试的第一种方法是解析/etc/passwd(使用awk)。

awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
Run Code Online (Sandbox Code Playgroud)

但是,这种方法的问题在于登录可能不是本地的,例如,用户身份验证可能通过 NIS 或 LDAP。

使用getent命令

使用getent passwd比解析更具可移植性,/etc/passwd因为它还可以查询非本地 NIS 或 LDAP 数据库。

getent passwd "$uid" | cut -d: -f1
Run Code Online (Sandbox Code Playgroud)

不幸的是,该getent实用程序似乎并未由 POSIX 指定。

使用id命令

id 是 POSIX 标准化实用程序,用于获取有关用户身份的数据。

BSD 和 GNU 实现接受用户 ID 作为操作数:

这意味着它可用于打印与用户 ID 关联的登录名:

id -nu "$uid"
Run Code Online (Sandbox Code Playgroud)

但是,在 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
}
Run Code Online (Sandbox Code Playgroud)

然而,这很笨重、不优雅,而且——更重要的是——不够健壮;如果用户 ID 无效或不存在,它将以非零状态退出。在我编写一个更长更笨拙的 shell 脚本来分析和存储每个命令调用的退出状态之前,我想我会在这里问:

是否有更优雅和便携(POSIX 兼容)的方式来获取与用户 ID 关联的登录名?

cas*_*cas 15

一种常见的方法是测试您想要的程序是否存在并且是否可以从您的PATH. 例如:

get_username(){
  uid="$1"

  # First try using getent
  if command -v getent > /dev/null 2>&1; then 
    getent passwd "$uid" | cut -d: -f1

  # Next try using the UID as an operand to id.
  elif command -v id > /dev/null 2>&1 && \
       id -nu "$uid" > /dev/null 2>&1; then
    id -nu "$uid"

  # Next try perl - perl's getpwuid just calls the system's C library getpwuid
  elif command -v perl >/dev/null 2>&1; then
    perl -e '@u=getpwuid($ARGV[0]);
             if ($u[0]) {print $u[0]} else {exit 2}' "$uid"

  # As a last resort, parse `/etc/passwd`.
  else
      awk -v uid="$uid" -F: '
         BEGIN {ec=2};
         $3 == uid {print $1; ec=0; exit 0};
         END {exit ec}' /etc/passwd
  fi
}
Run Code Online (Sandbox Code Playgroud)

因为 POSIXid不支持 UID 参数,所以 eliffor 子句id不仅要测试是否id在 PATH 中,还要测试它是否会无误地运行。这意味着它可能会运行id两次,幸运的是这不会对性能产生明显影响。这也有可能是两个idawk将运行,用相同的可以忽略不计的性能影响。

顺便说一句,使用这种方法,无需存储输出。只有其中一个将运行,因此只有一个将打印输出以供函数返回。

  • 是的。为此,我经常使用 `command -v`:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html(尽管我只使用内置的 `dash` shell 对其进行了测试)。 (3认同)

Gil*_*il' 7

除了id. 尝试id并退回到解析/etc/passwd可能与实践中一样可移植。

BusyBoxid不接受用户 ID,但带有 BusyBox 的系统通常是自治的嵌入式系统,其中解析/etc/passwd就足够了。

如果您遇到id不接受用户 ID的非 GNU 系统,您也可以尝试getpwuid通过 Perl调用,如果它可用:

username=$(perl -e 'print((getpwuid($ARGV[0]))[0])) 2>/dev/null
if [ -n "$username" ]; then echo "$username"; return; fi
Run Code Online (Sandbox Code Playgroud)

或 Python:

if python -c 'import pwd, sys; print(pwd.getpwuid(int(sys.argv[1]))).pw_name' 2>/dev/null; then return; fi
Run Code Online (Sandbox Code Playgroud)

  • 解析 `/etc/passwd` 根本不可移植,并且不适用于 LDAP 等非 passwd 文件后端。 (3认同)
  • 作为最后的回退,检查系统上是否有 ac 编译器,然后编译提供的 getpwuid() 包装器 ... (2认同)

Ant*_*gan 6

POSIX 指定getpwuid为标准 C 函数,用于在用户数据库中搜索用户 ID,允许将 ID 转换为登录名。我下载了 GNU coreutils的源代码,并且可以看到这个函数正在他们的实用程序实现中使用,例如idls

作为学习练习,我编写了这个又快又脏的 C 程序来简单地充当此函数的包装器。请记住,我从大学(很多年前)开始就没有用 C 编程,我不打算在生产中使用它,但我想我会在这里发布它作为概念证明(如果有人想编辑它) ,随意):

#include <stdio.h>
#include <stdlib.h>  /* atoi */
#include <pwd.h>

int main( int argc, char *argv[] ) {
    uid_t uid;
    if ( argc >= 2 ) {
        /* NB: atoi returns 0 (super-user ID) if argument is not a number) */
        uid = atoi(argv[1]);
    }
    /* Ignore any other arguments after the first one. */
    else {
        fprintf(stderr, "One numeric argument must be supplied.\n");
        return 1;
    }

    struct passwd *pwd;
    pwd = getpwuid(uid);
    if (pwd) {
        printf("The login name for %d is: %s\n", uid, pwd->pw_name);
        return 0;
    }
    else {
        fprintf(stderr, "Invalid user ID: %d\n", uid);
        return 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

我没有机会使用 NIS/LDAP 对其进行测试,但我注意到,如果/etc/passwd.

用法示例:

$ ./get_user ""
The login name for 0 is: root

$ ./get_user 99
Invalid user ID: 99
Run Code Online (Sandbox Code Playgroud)