Linux 用户与组管理是权限体系的地基。不管是给同事开账号、给服务建专属运行用户,还是排查"这个目录为什么没权限",绕不开的就是 useradd、usermod、groupadd、passwd 这几条命令。这一篇把它们的语法、常用参数和容易踩的坑一次讲清楚。
一、先看懂四个配置文件
所有用户和组的信息都落在四个文本文件里。改命令之前先知道它们记什么,出问题时才知道去哪查。
| 文件 | 存什么 | 谁能读 |
|---|---|---|
/etc/passwd |
每个用户一行:用户名、UID、主组 GID、家目录、登录 Shell | 所有用户 |
/etc/shadow |
加密后的密码、密码过期策略 | 只有 root |
/etc/group |
每个组一行:组名、GID、组成员列表 | 所有用户 |
/etc/gshadow |
组密码、组管理员(很少用) | 只有 root |
/etc/passwd 里每一行用冒号切成 7 段:
bash
root:x:0:0:root:/root:/bin/bash
依次是:用户名、密码占位符(永远是 x,真密码在 shadow)、UID、主组 GID、注释信息、家目录、登录 Shell。普通用户的 UID 通常从 1000 开始(Ubuntu/Debian/Rocky 一致),1~999 是系统保留账号。
二、groupadd:创建组
语法格式
bash
groupadd [选项] 组名
常用参数
| 参数 | 说明 |
|---|---|
-g GID |
手动指定 GID,不写则自动取下一个可用值 |
-r |
创建系统组(GID 落在系统号段内,常用于服务账号) |
-f |
组已存在时不报错(脚本里用) |
示例与预期输出
bash
sudo groupadd developers
sudo groupadd -g 3000 devops
确认是否建成功:
bash
getent group developers devops
预期输出:
developers:x:1001:
devops:x:3000:
用 getent group 而不是 cat /etc/group,是因为它在 NIS/LDAP 环境下也能查到组,写脚本更通用。
三、useradd:创建用户
语法格式
bash
useradd [选项] 用户名
常用参数
| 参数 | 说明 |
|---|---|
-m |
自动创建家目录(Ubuntu 默认行为,CentOS 上常需要显式加) |
-d 路径 |
指定家目录位置,默认 /home/用户名 |
-s Shell |
指定登录 Shell,如 /bin/bash、/sbin/nologin |
-g 主组 |
指定主组(组名或 GID),不写则默认建一个同名私有组 |
-G 组1,组2 |
加入附加组(注意会覆盖,不是追加) |
-c 注释 |
写真实姓名或用途,如 -c "Zhang San" |
-u UID |
手动指定 UID |
-e YYYY-MM-DD |
账号过期日期 |
-p 加密串 |
直接给密码(要传加密后的串,不要传明文,交互用 passwd) |
示例与预期输出
示例 1:建一个能登录的普通用户
bash
sudo useradd -m -s /bin/bash -c "Zhang San" zhangsan
sudo passwd zhangsan
第二条会提示输入两次密码。建完看一下:
bash
getent passwd zhangsan
预期输出:
zhangsan:x:1001:1001:Zhang San:/home/zhangsan:/bin/bash
示例 2:建一个不能登录、专给服务用的用户
跑 Nginx、MySQL 这类服务时,官方包通常自带 www-data、mysql 账号。自己起服务时也建议建一个无登录 Shell 的运行账号:
bash
sudo useradd -r -m -s /usr/sbin/nologin apprunner
-r 建系统账号,/usr/sbin/nologin 让它不能交互式登录,但服务进程仍能以它的身份跑。
示例 3:把用户加进附加组
bash
sudo useradd -m -G developers,docker lisi
lisi 的主组是自己的同名组,同时属于 developers 和 docker 两个附加组。
⚠️ 常见错误
- CentOS/Rocky 上漏
-m:很多最小化安装的系统默认不建家目录,用户登录后直接掉进/。养成useradd -m的习惯。 -G当成追加用 :usermod -G是覆盖式赋值。想"追加"必须用usermod -aG,少写那个a会把用户已有的其他附加组全部清掉。-p传明文 :useradd -p 123456会把123456当成加密后的密码串写进 shadow,结果永远登不上。设密码用passwd。
四、usermod:修改已有用户
语法格式
bash
usermod [选项] 用户名
常用参数
| 参数 | 说明 |
|---|---|
-l 新名 |
修改登录名(家目录和文件不会自动改名) |
-g 主组 |
修改主组 |
-aG 组 |
追加 附加组(-a 必须和 -G 一起用) |
-d 新路径 -m |
新家目录并把旧家目录内容迁过去 |
-L |
锁定账号(在密码前加 !,禁止登录) |
-U |
解锁账号 |
-s Shell |
修改登录 Shell |
-c 注释 |
修改 GECOS 注释 |
示例与预期输出
改 Shell 并迁移家目录:
bash
sudo usermod -s /bin/zsh -m -d /home/zhangsan_v2 zhangsan
这条命令同时干了三件事:换 Shell、搬家目录、把旧目录里的文件一起搬过去。
追加一个附加组而不覆盖:
bash
sudo usermod -aG sudo zhangsan # Ubuntu/Debian,加入 sudo 组获得提权
sudo usermod -aG wheel zhangsan # CentOS/Rocky,对应 wheel 组
注意:用户重新登录后组权限才生效,已经开着的 SSH 会话不会自动刷新,用 groups zhangsan 确认。
临时锁掉一个账号:
bash
sudo usermod -L zhangsan
sudo passwd -S zhangsan
预期输出(第二列 L 表示已锁定):
zhangsan L 09/16/2026 0 99999 7 -1
五、passwd:设置和管理密码
语法格式
bash
passwd [选项] [用户名]
普通用户不带用户名改自己的密码;root 可以改任何人的。
常用参数
| 参数 | 说明 |
|---|---|
-S 用户名 |
查看密码状态(是否锁定、最后修改时间) |
-l 用户名 |
锁定(root 专用,等价 usermod -L) |
-u 用户名 |
解锁 |
-d 用户名 |
删掉密码,变成空密码(生产环境慎用) |
-e 用户名 |
强制下次登录改密码 |
示例与预期输出
让新用户第一次登录就改密码:
bash
sudo passwd --expire zhangsan
下次 zhangsan 登录时会被强制跳到改密码流程。批量给同事开账号时常用这招。
查密码状态:
bash
sudo passwd -S zhangsan
输出里第二个字段:P 表示有可用密码,L 锁定,NP 无密码。
⚠️ 注意事项
- 密码复杂度由 PAM 模块(
pam_pwquality)控制,不是passwd本身。设太短太简单会被拒绝,报错信息在/var/log/auth.log(Debian/Ubuntu)或/var/log/secure(RHEL 系)。 - 不要把密码写进命令行历史。
passwd是交互式提示输入,比echo 密码 | passwd --stdin安全------后者会把密码留在 shell history 里。
六、配套命令:userdel、groupdel、su
userdel 用户名:删用户;加-r连家目录和邮件队列一起删。先确认没有进程还在用这个身份跑 ,ps -u 用户名看一眼再删。groupdel 组名:删组。如果这个组是某个用户的主组,删不掉,得先把那个用户的主组改掉或删掉。su - 用户名:切到另一个用户。中间的横杠很关键,它会加载目标用户的完整登录环境;不带横杠切过去,家目录、环境变量都还是原来的。
七、知识扩展:useradd 的默认值从哪来
在 Ubuntu 上敲 sudo useradd tom,家目录、Shell、UID 是怎么定的?答案在两个文件:
/etc/default/useradd:控制是否建家目录、默认 Shell、过期日期等。/etc/login.defs:控制 UID/GID 起止范围、密码最长有效期、UMASK等。
用 useradd -D 可以直接看当前生效的默认值:
bash
useradd -D
预期输出(节选):
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
其中 SKEL=/etc/skel 是个容易被忽略的细节:新建用户的家目录是从 /etc/skel 整个拷贝过来的。想让所有新用户默认带 .bashrc 别名、vimrc 之类的配置,把文件放进 /etc/skel 就行,不用每个用户单独配。
另一个高频困惑:为什么我明明把用户加进了 docker 组,却还是要 sudo docker?因为组权限在登录那一刻快照进会话,加完组后必须退出重新登录(或 newgrp docker)才生效。