1. 项目概述:从“列出用户”窥探Linux系统管理基本功
在Linux世界里,尤其是像Ubuntu这样的发行版中,系统管理员或者任何一个需要管理多用户环境的开发者,第一件需要掌握的事情往往不是写一个复杂的脚本,而是搞清楚“现在系统里都有谁”。这个“谁”,指的就是用户账户。听起来简单,不就是列个名单吗?但实际操作起来,你会发现这背后牵扯到用户账户的存储机制、不同的账户类型(系统用户、普通用户、服务账户)、以及账户信息的多种呈现维度。我见过不少新手,一上来就用cat /etc/passwd,然后对着那一长串以冒号分隔的、看起来像天书一样的行发懵。也见过一些有经验的朋友,在排查权限问题或者清理陈旧账户时,因为对用户列表工具的理解不够深入,走了不少弯路。
所以,今天我们就来彻底拆解一下在Ubuntu操作系统中“列出用户”这个看似基础,实则内涵丰富的操作。这不仅仅是执行几条命令,更是理解Linux用户和组管理体系的绝佳切入点。无论你是刚接触Ubuntu的新手,还是想巩固基础的中级用户,相信这篇从实战出发的梳理都能给你带来新的收获。我们会从最原始的系统文件讲起,到各种方便的命令行工具,再到如何过滤和格式化输出以满足特定需求,最后分享一些我在实际运维中总结出来的排查技巧和注意事项。
2. 核心原理:用户信息存储与解析
在动手敲命令之前,我们必须先弄明白Ubuntu(以及绝大多数Linux系统)把用户信息藏在了哪里,又是以什么格式存放的。理解了这些,你才能在任何工具都不奏效的极端情况下(比如单用户救援模式),依然能手动获取所需信息。
2.1 基石文件:/etc/passwd 与 /etc/shadow
Linux系统采用纯文本文件来存储用户账户的核心信息,这是其设计哲学中“一切皆文件”的典型体现。
/etc/passwd文件是用户信息的公开目录。每行代表一个用户,字段由冒号:分隔,共有7个字段。我们来看一个典型条目:
sarah:x:1001:1001:Sarah Connor:/home/sarah:/bin/bash- 用户名 (sarah):用户登录时使用的名称。
- 密码占位符 (x):在早期,加密后的密码哈希值就放在这里。由于
/etc/passwd默认对所有用户可读,为了安全,现在密码都被移到了/etc/shadow文件,这里只用x作为占位符。 - 用户ID (UID, 1001):系统内部识别用户的数字。这是关键:UID是内核识别用户的真正依据,用户名只是给人类看的友好标签。UID为0的用户是超级用户root。
- 主组ID (GID, 1001):用户所属的主要组的ID。
- GECOS 字段 (Sarah Connor):一个可选的注释字段,通常包含用户全名、办公室、电话等信息。可以为空。
- 家目录 (/home/sarah):用户登录后的初始工作目录。
- 登录Shell (/bin/bash):用户登录后自动启动的Shell程序。如果设置为
/usr/sbin/nologin或/bin/false,则该账户无法用于交互式登录(常用于系统服务账户)。
/etc/shadow文件则存储了真正的安全敏感信息,主要是加密后的密码哈希值,以及密码策略信息(如过期时间)。该文件只有root用户可读,确保了密码的安全性。它的字段同样由冒号分隔,包含用户名、加密密码、最后修改日期、最小密码年龄、最大密码年龄、警告期、不活动期、过期日期等。
注意:直接使用
cat查看/etc/passwd是安全的,但绝对不要用cat查看/etc/shadow,即使你是root。应使用sudo less /etc/shadow或专门的命令如sudo passwd -S username来获取密码状态信息。
2.2 用户分类:UID范围的秘密
不是所有用户都是平等的。通过UID范围,我们可以快速区分用户类型:
- 系统用户 (UID 0-999, 或 0-99 在某些系统):这些用户通常由软件包在安装时创建,用于运行系统服务(如
www-data用户运行Web服务器,mysql用户运行数据库)。他们的UID通常较小,家目录通常不在/home下(甚至没有家目录),登录Shell常被设置为/usr/sbin/nologin。 - 普通用户 (UID >= 1000):在Ubuntu桌面版上,第一个创建的真实用户UID通常从1000开始。后续添加的用户UID依次递增。这些用户拥有正常的家目录和登录Shell,用于日常交互。
- 超级用户 (UID 0):唯一的root用户,拥有系统最高权限。
理解这个分类对于“列出用户”至关重要,因为你经常需要过滤掉那些不用于登录的系统服务账户,只关注真实的人类用户。
3. 命令行工具实战:从基础到精通
知道了原理,我们来看看有哪些“趁手兵器”。我将这些工具分为三类:查看原始文件的“底层工具”、专为用户管理设计的“专用工具”,以及用于信息提取和过滤的“瑞士军刀”。
3.1 底层查看:直接读取系统文件
这是最直接,也是信息最全的方式。
1.cat /etc/passwd这是最原始的命令。它会列出所有用户,包括大量的系统用户。输出信息量大,但可读性差。通常需要配合其他命令进行过滤。
cat /etc/passwd2.less或more分页查看由于用户可能很多,使用分页查看器更友好。
less /etc/passwd # 在 less 中,你可以使用 `/` 搜索用户名,按 `q` 退出。3.2 专用工具:getent、awk 与 cut
这些工具能更结构化、更有针对性地获取信息。
1.getent passwdgetent(get entries) 命令是从名称服务切换库(NSS)获取数据库条目。在标准的本地文件系统中,getent passwd和cat /etc/passwd结果一样。但它的优势在于,如果你的系统配置了LDAP、NIS等集中用户认证,getent可以透明地同时列出本地和远程的所有用户,而cat只能看本地文件。因此,在生产环境的异构系统中,getent是更可靠的选择。
getent passwd2. 使用awk或cut提取特定字段我们经常只关心用户名或UID。
- 提取所有用户名列表:
# 使用 cut,以冒号分隔,取第一个字段 cut -d: -f1 /etc/passwd # 使用 awk,效果相同 awk -F: '{print $1}' /etc/passwd - 提取用户名和UID:
awk -F: '{print $1, $3}' /etc/passwd - 查找特定用户:
# 查找用户'sarah'的完整信息 getent passwd sarah # 或 grep '^sarah:' /etc/passwdgrep命令前的^表示行首,确保只匹配以“sarah:”开头的行,避免匹配到包含“sarah”的其他用户(如“sarah_admin”)。
3.3 高级过滤:区分系统用户与登录用户
这是日常管理中最常见的需求。我们结合UID范围和登录Shell来过滤。
1. 列出所有普通用户(UID >= 1000)
awk -F: '$3 >= 1000 {print $1}' /etc/passwd这个命令检查第三个字段(UID)是否大于等于1000,如果是,则打印第一个字段(用户名)。
2. 列出所有能登录的用户(拥有有效Shell)系统用户通常使用/usr/sbin/nologin或/bin/false作为Shell。我们可以反向过滤掉它们。
# 方法一:使用 grep 排除包含 nologin 或 false 的行 grep -vE '(/usr/sbin/nologin|/bin/false)$' /etc/passwd | cut -d: -f1 # 方法二:使用 awk 匹配有效的 bash/sh 等shell awk -F: '$7 ~ /(\/bin\/bash|\/bin\/sh|\/bin\/zsh)/ {print $1}' /etc/passwd实操心得:方法一更通用,因为它排除了所有“不可登录”的Shell。方法二需要你明确知道有哪些Shell是有效的,如果用户使用了
/bin/dash或/usr/bin/fish,可能会被漏掉。我通常使用方法一。
3. 结合两者:列出UID>=1000且能登录的用户(最常用的“真实用户”列表)
awk -F: '$3 >= 1000 && $7 !~ /(\/usr\/sbin\/nologin|\/bin\/false)/ {print $1}' /etc/passwd这个命令是上面两个条件的结合:UID大于等于1000,并且第七个字段不匹配nologin或false。
3.4 用户管理套件:lslogins与chage
对于一些较新的系统,有更强大的专用工具。
lslogins命令这个命令专门用于列出用户登录信息,输出格式非常友好,且信息全面,包括UID、GID、家目录、Shell、最后登录时间等。它通常需要root权限。
sudo lslogins要获得更简洁的用户名列表,可以:
sudo lslogins -o USERlslogins的一个巨大优势是它原生支持强大的过滤选项。例如,直接列出所有普通用户:
sudo lslogins -u # -u 选项专门列出普通用户列出所有系统用户:
sudo lslogins -s # -s 选项列出系统用户4. 图形界面与脚本化应用
虽然命令行是核心,但了解其他方式也有其应用场景。
4.1 图形化工具
在Ubuntu桌面版,你可以通过“设置”->“用户”来查看和添加用户。这是一个对新手友好的方式,但功能有限,通常只显示能登录的普通用户,且无法查看UID、GID等详细信息。对于系统管理,命令行仍是不可替代的。
4.2 在脚本中集成用户列表
在自动化脚本中,你需要可靠、明确地获取用户列表。以下是一些最佳实践:
场景一:获取所有普通用户名列表,并存入数组
#!/bin/bash # 使用 getent 确保兼容性,使用 awk 进行精确过滤 readarray -t normal_users < <(getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /(\/usr\/sbin\/nologin|\/bin\/false)/ {print $1}') echo "系统普通用户列表:" for user in "${normal_users[@]}"; do echo " - $user" done脚本解析:
getent passwd:获取所有用户条目,兼容本地和远程用户。awk过滤条件:$3 >= 1000:UID大于等于1000。$3 < 65534:排除一些特殊的UID(如nobody用户的65534,在某些系统上它可能被错误地包含进来)。这是一个更严谨的做法。$7 !~ /(\/usr\/sbin\/nologin|\/bin\/false)/:排除不可登录的Shell。{print $1}:打印用户名。
readarray -t ...:将命令输出的每一行读入Bash数组normal_users,-t选项会移除每行末尾的换行符。
场景二:检查特定用户是否存在
#!/bin/bash username="sarah" if getent passwd "$username" > /dev/null 2>&1; then echo "用户 '$username' 存在。" # 可以进一步获取用户信息 user_info=$(getent passwd "$username") uid=$(echo "$user_info" | cut -d: -f3) echo "其UID是:$uid" else echo "用户 '$username' 不存在。" fi这里利用getent passwd username的返回值。如果用户存在,命令成功执行(返回0);如果不存在,命令失败(返回非0)。我们将输出重定向到/dev/null以保持脚本输出整洁。
5. 常见问题排查与实战技巧
掌握了命令,在实际使用中你可能会遇到一些困惑或问题。下面是我在多年运维中总结的一些典型场景和技巧。
5.1 问题排查速查表
| 问题现象 | 可能原因 | 排查命令与解决方案 |
|---|---|---|
使用awk或cut过滤时输出为空 | 1. 过滤条件太严格(如UID范围不对)。 2. 字段分隔符指定错误(不是冒号)。 | 1. 先执行getent passwd | head -5确认数据格式和样例UID。2. 检查命令中的 -F:或-d:是否正确。 |
| 发现一个不认识的用户出现在列表中 | 1. 新安装的软件包创建的系统用户。 2. 可能的安全入侵迹象(需警惕)。 | 1. 使用lslogins -u <用户名>或getent passwd <用户名>查看其详细信息,特别是UID和Shell。2. 检查其家目录和最近活动: sudo ls -la /home/<用户名>(如果有),sudo lastlog | grep <用户名>。 |
lslogins命令未找到 | 该命令来自util-linux软件包,在某些最小化安装中可能不存在。 | 安装它:sudo apt update && sudo apt install util-linux。 |
| 想查看用户所属的组列表 | passwd文件只包含主组(GID)。要查看所有附属组,需查看/etc/group文件或使用groups命令。 | groups <用户名>或id <用户名>。id命令输出更丰富,包括UID、GID和所有组。 |
| 用户数量很多,如何统计? | 需要结合管道和行数统计命令。 | 统计所有用户:getent passwd | wc -l统计普通用户: getent passwd | awk -F: '$3 >= 1000' | wc -l |
5.2 高级技巧与心得
getent是黄金标准:在编写需要跨环境(本地、LDAP等)运行的脚本时,始终坚持使用getent passwd而不是直接读取/etc/passwd。它的行为更一致,更可预测。理解“登录Shell”过滤的局限性:我们常用Shell是否
nologin来判断用户能否登录。但请注意,这只能判断能否通过Shell登录(如SSH、本地tty)。一个以/usr/sbin/nologin为Shell的用户,仍然可能通过其他方式“活动”,例如,Web服务(如Apache)以www-data用户身份运行。因此,在清理“不活动用户”时,不能仅凭此判断,还需结合lastlog、last命令查看登录记录,以及进程查看命令如ps auxf | grep ^username。UID冲突是灾难性的:两个不同的用户名拥有相同的UID,在系统看来是同一个用户。这会导致严重的权限混乱。在从备份恢复、或通过脚本批量创建用户时务必检查UID唯一性。可以使用以下命令检查:
# 检查是否有重复的UID sudo getent passwd | cut -d: -f3 | sort -n | uniq -d如果该命令有输出,就表示存在重复UID,需要立即处理。
家目录不是用户的唯一标识:
/etc/passwd中的家目录字段只是一个指针。这个目录可能被删除、被移动,或者用户根本不需要家目录(如系统用户)。判断一个用户是否“活跃”,家目录的存在与否不是绝对依据。组合命令进行深度分析:一个强大的管理命令往往由多个简单命令通过管道组合而成。例如,我想找出最近90天内从未登录过的普通用户:
# 首先获取所有普通用户名 users=$(getent passwd | awk -F: '$3 >= 1000 {print $1}') # 遍历并检查最后登录时间 for user in $users; do lastlog -u $user | grep -q "Never logged in" if [ $? -eq 0 ]; then echo "$user: 从未登录" fi done这个例子结合了
getent,awk,lastlog,grep和 Shell 循环,完成了单个命令无法完成的复杂查询。
列出用户,这个操作贯穿了Linux系统管理的始终。从最初的cat /etc/passwd,到后来学会用awk和getent进行精准过滤,再到在自动化脚本中可靠地集成,每一步都伴随着对系统更深一层的理解。我个人的体会是,不要死记硬背命令,而是去理解每条命令背后的“为什么”——为什么用getent更稳妥?为什么过滤UID要用1000作为分界线?当你弄明白了/etc/passwd文件里每一个冒号的意义,这些命令就都成了你思维的自然延伸。
最后分享一个我常用的别名,放在~/.bashrc里,可以快速列出一个格式清晰的用户列表,包含用户名、UID和描述信息:
alias listusers="getent passwd | awk -F: '\$3 >= 1000 && \$3 < 65500 {printf \"%-15s %-10s %s\\n\", \$1, \$3, \$5}' | column -t"这样,只需要输入listusers,就能得到一个整洁的表格,在管理多用户系统时非常方便。希望这些经验能帮助你更自信地管理你的Ubuntu系统用户。