三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Linux用户管理实战:从/etc/passwd到getent命令的完整指南

Linux用户管理实战:从/etc/passwd到getent命令的完整指南

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
  1. 用户名 (sarah):用户登录时使用的名称。
  2. 密码占位符 (x):在早期,加密后的密码哈希值就放在这里。由于/etc/passwd默认对所有用户可读,为了安全,现在密码都被移到了/etc/shadow文件,这里只用x作为占位符。
  3. 用户ID (UID, 1001):系统内部识别用户的数字。这是关键:UID是内核识别用户的真正依据,用户名只是给人类看的友好标签。UID为0的用户是超级用户root。
  4. 主组ID (GID, 1001):用户所属的主要组的ID。
  5. GECOS 字段 (Sarah Connor):一个可选的注释字段,通常包含用户全名、办公室、电话等信息。可以为空。
  6. 家目录 (/home/sarah):用户登录后的初始工作目录。
  7. 登录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/passwd

2.lessmore分页查看由于用户可能很多,使用分页查看器更友好。

less /etc/passwd # 在 less 中,你可以使用 `/` 搜索用户名,按 `q` 退出。

3.2 专用工具:getent、awk 与 cut

这些工具能更结构化、更有针对性地获取信息。

1.getent passwdgetent(get entries) 命令是从名称服务切换库(NSS)获取数据库条目。在标准的本地文件系统中,getent passwdcat /etc/passwd结果一样。但它的优势在于,如果你的系统配置了LDAP、NIS等集中用户认证,getent可以透明地同时列出本地和远程的所有用户,而cat只能看本地文件。因此,在生产环境的异构系统中,getent是更可靠的选择。

getent passwd

2. 使用awkcut提取特定字段我们经常只关心用户名或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/passwd
    grep命令前的^表示行首,确保只匹配以“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,并且第七个字段不匹配nologinfalse

3.4 用户管理套件:lsloginschage

对于一些较新的系统,有更强大的专用工具。

lslogins命令这个命令专门用于列出用户登录信息,输出格式非常友好,且信息全面,包括UID、GID、家目录、Shell、最后登录时间等。它通常需要root权限。

sudo lslogins

要获得更简洁的用户名列表,可以:

sudo lslogins -o USER

lslogins的一个巨大优势是它原生支持强大的过滤选项。例如,直接列出所有普通用户:

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

脚本解析

  1. getent passwd:获取所有用户条目,兼容本地和远程用户。
  2. awk过滤条件:
    • $3 >= 1000:UID大于等于1000。
    • $3 < 65534:排除一些特殊的UID(如nobody用户的65534,在某些系统上它可能被错误地包含进来)。这是一个更严谨的做法。
    • $7 !~ /(\/usr\/sbin\/nologin|\/bin\/false)/:排除不可登录的Shell。
    • {print $1}:打印用户名。
  3. 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 问题排查速查表

问题现象可能原因排查命令与解决方案
使用awkcut过滤时输出为空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 高级技巧与心得

  1. getent是黄金标准:在编写需要跨环境(本地、LDAP等)运行的脚本时,始终坚持使用getent passwd而不是直接读取/etc/passwd。它的行为更一致,更可预测。

  2. 理解“登录Shell”过滤的局限性:我们常用Shell是否nologin来判断用户能否登录。但请注意,这只能判断能否通过Shell登录(如SSH、本地tty)。一个以/usr/sbin/nologin为Shell的用户,仍然可能通过其他方式“活动”,例如,Web服务(如Apache)以www-data用户身份运行。因此,在清理“不活动用户”时,不能仅凭此判断,还需结合lastloglast命令查看登录记录,以及进程查看命令如ps auxf | grep ^username

  3. UID冲突是灾难性的:两个不同的用户名拥有相同的UID,在系统看来是同一个用户。这会导致严重的权限混乱。在从备份恢复、或通过脚本批量创建用户时务必检查UID唯一性。可以使用以下命令检查:

    # 检查是否有重复的UID sudo getent passwd | cut -d: -f3 | sort -n | uniq -d

    如果该命令有输出,就表示存在重复UID,需要立即处理。

  4. 家目录不是用户的唯一标识/etc/passwd中的家目录字段只是一个指针。这个目录可能被删除、被移动,或者用户根本不需要家目录(如系统用户)。判断一个用户是否“活跃”,家目录的存在与否不是绝对依据。

  5. 组合命令进行深度分析:一个强大的管理命令往往由多个简单命令通过管道组合而成。例如,我想找出最近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,到后来学会用awkgetent进行精准过滤,再到在自动化脚本中可靠地集成,每一步都伴随着对系统更深一层的理解。我个人的体会是,不要死记硬背命令,而是去理解每条命令背后的“为什么”——为什么用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系统用户。

← 返回列表