如何列出所有用户名和/或主目录?

lak*_*rda 18 bash shell-script eval

我想列出机器上的所有用户目录。通常,我会这样做:

ls -l /home
Run Code Online (Sandbox Code Playgroud)

但是我在一个脚本中使用它,该脚本将部署在其他机器上,也许在他们不称之为家的机器上(例如 myHome)。所以我想把它概括为ls -l ~. 但它只列出我的用户主目录而不是所有用户主目录名称(基本上我想获得机器上用户名的列表)。我怎样才能概括它?

Sté*_*las 49

许多系统有一个getent对list命令或查询的内容名称服务数据库一样passwdgroupservicesprotocols...

getent passwd | cut -d: -f6
Run Code Online (Sandbox Code Playgroud)

将列出可以枚举的数据库中所有用户的主目录(第 6冒号分隔字段)。

用户名本身在第一个字段中,因此对于用户名列表:

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

(请注意,这并不意味着这些用户可以登录系统或他们的主目录已经创建,而是系​​统知道他们,他们可以转换为用户 ID)。

对于无法枚举的数据库,您可以尝试单独查询每个可能的用户 ID:

getent passwd {0..65535} | cut -d: -f1,6
Run Code Online (Sandbox Code Playgroud)

(这里假设 uids 停止在 65535(一些系统支持更多)和一个支持 zsh{x..y}形式的大括号扩展的 shell )。但是您不希望在用户数据库联网(并且本地缓存有限)的系统上经常这样做,例如 LDAP、NIS+、SQL...,因为这可能意味着大量的网络流量(并加载到目录服务器上) ) 进行所有这些查询。

这也意味着,如果有多个用户共享相同的 uid,则每个 uid 只能获得一个条目,因此会错过其他用户。

如果你没有getent,你可以求助于perl

perl -le 'while (@e = getpwent) {print $e[7]}'
Run Code Online (Sandbox Code Playgroud)

对于getent passwd$e[0]对于用户名),或:

perl -le 'for ($i=0;$i<65536;++$i) {
  if (@e = getpwuid $i) {print $e[0] ": " $e[7]}}'
Run Code Online (Sandbox Code Playgroud)

因为getent passwd {0..65535}有相同的警告。

在 shell 中,您可以使用~user来获取 的主目录user,但在大多数 shell 中,这仅适用于有限的用户名集(该~扩展运算符支持的用户名中允许的字符列表因 shell 不同而不同)并且使用几个外壳(包括bash),~$user将无法工作(eval当用户名存储在变量中时,您需要求助于)。而且您仍然需要找到一种方法来获取用户名列表。

一些 shell 内置支持获取用户名列表。

  • bash:compgen -u将返回可以枚举的数据库中的用户列表。
  • zsh:$userdirs关联数组将用户名映射到其主目录(也仅限于可以枚举的数据库,但如果您对~user不可枚举数据库中的用户进行扩展,则会向 中添加一个条目$userdirs)。所以你可以这样做:

    printf '%s => %s\n' "${(kv@)userdirs}"
    
    Run Code Online (Sandbox Code Playgroud)

    列出用户及其主目录。

    这仅在zsh交互式时才有效

  • tcsh,fish以及yash其他三个可以完成用户名的外壳程序(例如,在完成~<Tab>参数时),但它们看起来并不能让您以编程方式获取该用户名列表。


Eig*_*ony 17

列出用户主目录的更好方法是/etc/passwd从那里解析和提取它们,而不是对它们可能在哪里做出任何假设。

从你的第二条评论来看,你实际上想要的是用户名,而不是他们的主目录,在这种情况下,/etc/passwd无论如何都是更好的选择。请注意,某些 Linux / UNIX 机器将配置其他用户身份验证机制(例如 LDAP),因此最终,您的查询比您最初想象的要复杂,但这/etc/passwd是一个很好的起点。

  • 另请参阅支持它的系统上的“getent”。 (9认同)
  • 是的,`getent` 总是比解析 `/etc/passwd` 更好(它解决了“其他用户身份验证机制”的问题)。 (6认同)

小智 8

简短的回答

compgen -u
Run Code Online (Sandbox Code Playgroud)

中等答案:由于您正在使用bash,您可以列出所有可能的~使用完成compgen -A user。这是一个很常见的用法,可以缩写为compgen -u。作为内置的 shell,compgen没有自己的手册页。相反,请参阅bash(1)以获取文档并阅读有关Programmable Completion的部分。

更彻底的替代方案

如果您非常关心可移植性,您甚至可能无法依赖其他具有bash. 在这种情况下,请执行以下操作:

(getent passwd ||
    dscl . -ls /Users dsAttrTypeNative:homeDirectory || 
    nidump passwd  ||
    cat /etc/passwd) 2>/dev/null  |  cut -d: -f6
Run Code Online (Sandbox Code Playgroud)

解释 长答案会尝试一切,所以它几乎可以在任何 UNIX 系统上工作,无论它是否使用较新的 /etc/nsswitch.conf(GNU/Linux 和 BSD 都附带getent)、传统的 UNIX passwd 平面文件、MacOS 的目录服务¹ ( dscl),甚至是较旧的、以猫为主题的 MacOS X 版本和 NeXTSTEP ( nidump)。

简单 但是,您需要多大的便携性?Unix 有很多做事的方式,有时更简单的就足够了。如果您必须选择一个,我建议您getent passwd | cut -d: -f6使用 shell 脚本。²


脚注 ¹:我有一段时间没有使用过 MacOS,所以如果有人可以为我确认我的语法正确(并且输出不包含任何会弄乱的杂散冒号cut),那就太好了。谢谢。

脚注²:我推荐的和我做的可能不同。就个人而言,我会更频繁地cut -d: -f1 /etc/passwd在命令行中使用传统的。经过几十年的重复,我的手指可以打字,而我的大脑正在处理其他事情。

  • 所有 Mac OS X 机器几乎都保证有 bash,因为它是系统自带的。(与 zsh 和其他几个 shell 一样。) (2认同)