useradd 命令详解与参数实测(Ubuntu 22.04)
以 Ubuntu 22.04(shadow 4.8.1)实测逐参数讲解 useradd:-m/-M 家目录、-s/-u/-g/-G/-d/-b/-k/-e/-f/-c/-r/-o/-p/-K/-D 等全部常用参数与真实回显,覆盖 /etc/skel 骨架、系统账号、报错退出码及 useradd 与 adduser 差异。
useradd 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 useradd 的每一个常用参数——
-m/-M家目录创建、-s登录 shell、-u指定 UID、-g/-G主组与附加组、-d自定义家目录、-b基准目录、-k骨架目录、-e/-f账号过期、-c账号注释、-r系统用户、-o重复 UID、-p加密密码、-K覆盖 login.defs、-D默认值管理、--badnames/--extrausers等全部参数,以及报错行为与退出码,全部命令与回显已在 Ubuntu 22.04(shadow 4.8.1)实测通过。
一、环境信息
本文所有命令均在以下环境实测,回显逐字照抄:
- 操作系统:Ubuntu 22.04.5 LTS(
PRETTY_NAME="Ubuntu 22.04.5 LTS") - 内核架构:x86_64
- useradd 所属软件包:passwd 1:4.8.1-2ubuntu2.2(即 shadow-utils 4.8.1)
- 对照工具 adduser:3.118ubuntu5(独立包 adduser)
- 执行身份:root
- 演示数据目录:
/root/demo/useradd/(含自定义骨架目录custom-skel、train-skel) - 演示用户:以
u4e为前缀(u4e1、u4e2、u4e3、u4e4、u4e5、u4e6、u4e7、u4e8、u4esvc 等),演示组:u4egrp、u4edev、u4edevs
💡 说明:本机已有若干普通用户(UID 1000~1004 被占用),因此后文 useradd 自动分配的 UID 从 1005 起、组 GID 从 1006 起,属于正常的「取当前最大值 +1」分配策略,全新系统上会从 1000/1001 开始,不影响结论。
二、命令概览
2.1 语法与测试数据
useradd 用于在系统中创建新的用户账号,同时可创建同名主组、家目录,并把账号信息写入 /etc/passwd、/etc/shadow、/etc/group 等账号数据库文件。语法有两种形态(来自实测 useradd --help 首两行):
Usage: useradd [options] LOGIN
useradd -D
useradd -D [options]
useradd [options] LOGIN:创建名为 LOGIN 的普通(或系统)用户;useradd -D:不带其他参数时打印当前默认值;useradd -D [options]:带参数时修改默认值(写入/etc/default/useradd)。
本文测试数据约定:演示用户统一使用 u4e 前缀;涉及组演示时先用 groupadd 建立 u4egrp、u4edev;自定义骨架目录放在 /root/demo/useradd/custom-skel。每个参数小节均可独立执行复现。
💡 技术拓展:在 Linux 上「创建用户」不止 useradd 一个工具,同类主要选项对比——
工具 所属软件包 定位 家目录 默认 shell 交互方式 useradd passwd(shadow 4.8.1) 低层通用工具,全发行版行为一致 需显式 -m才创建/bin/sh(Ubuntu 默认配置) 非交互,参数精确可控 adduser adduser(3.118ubuntu5) Debian/Ubuntu 的高层包装脚本 默认创建 /bin/bash( DSHELL)交互式,提示输入密码、全名等 newusers passwd(shadow 4.8.1) 批量建号,读取 /etc/passwd 格式文本 按 login.defs 配置 按 login.defs 配置 非交互,适合脚本批量导入 本文实际采用 useradd:它是脚本化与自动化场景的最低公共层,参数语义在其他发行版上也一致;Ubuntu 交互式运维可用 adduser,两者差异详见 3.24 节。
2.2 --help 全量输出(逐行照抄)
先看完整帮助,是逐参数实测的地图。以下为 useradd --help 的完整实测输出:
$ useradd --help
Usage: useradd [options] LOGIN
useradd -D
useradd -D [options]
Options:
--badnames do not check for bad names
-b, --base-dir BASE_DIR base directory for the home directory of the
new account
--btrfs-subvolume-home use BTRFS subvolume for home directory
-c, --comment COMMENT GECOS field of the new account
-d, --home-dir HOME_DIR home directory of the new account
-D, --defaults print or change default useradd configuration
-e, --expiredate EXPIRE_DATE expiration date of the new account
-f, --inactive INACTIVE password inactivity period of the new account
-g, --gid GROUP name or ID of the primary group of the new
account
-G, --groups GROUPS list of supplementary groups of the new
account
-h, --help display this help message and exit
-k, --skel SKEL_DIR use this alternative skeleton directory
-K, --key KEY=VALUE override /etc/login.defs defaults
-l, --no-log-init do not add the user to the lastlog and
faillog databases
-m, --create-home create the user's home directory
-M, --no-create-home do not create the user's home directory
-N, --no-user-group do not create a group with the same name as
the user
-o, --non-unique allow to create users with duplicate
(non-unique) UID
-p, --password PASSWORD encrypted password of the new account
-r, --system create a system account
-R, --root CHROOT_DIR directory to chroot into
-P, --prefix PREFIX_DIR prefix directory where are located the /etc/* files
-s, --shell SHELL login shell of the new account
-u, --uid UID user ID of the new account
-U, --user-group create a group with the same name as the user
-Z, --selinux-user SEUSER use a specific SEUSER for the SELinux user mapping
--extrausers Use the extra users database
关键差异解读:-m 与 -M 是一对反义参数;-N 与 -U 是「是否创建同名主组」的一对反义参数;-R/-P/-Z/--btrfs-subvolume-home/--extrausers 属于特定场景(chroot 维护、SELinux、Btrfs、Ubuntu Core 只读根)的低频参数,第三章统一实测或说明。
2.3 版本信息
Ubuntu 22.04 的 useradd 属于 passwd 软件包(shadow 工具集),实测版本信息如下:
$ dpkg -l passwd | tail -1
ii passwd 1:4.8.1-2ubuntu2.2 amd64 change and administer password and group data
$ apt-cache policy passwd | head -3
passwd:
Installed: 1:4.8.1-2ubuntu2.2
Candidate: 1:4.8.1-2ubuntu2.2
⚠️ 注意:shadow 4.8.1 的 useradd 不支持 --version 选项,直接执行会报「unrecognized option」并打印用法,这也是一个真实的踩坑点:
$ useradd --version
useradd: unrecognized option '--version'
Usage: useradd [options] LOGIN
useradd -D
useradd -D [options]
确认版本请用 dpkg -l passwd 或 apt-cache policy passwd(1:4.8.1 即 shadow-utils 4.8.1)。
三、参数逐项实测
3.1 最简创建:useradd 用户名(不加 -m 没有家目录)
本节验证最简调用形态:只给用户名,其余全部默认。预期结果是账号创建成功,但不会创建家目录。
useradd u4e1 # 最简创建,无任何参数
tail -1 /etc/passwd # 查看账号记录
tail -1 /etc/shadow # 查看影子口令记录
ls -ld /home/u4e1 # 验证家目录是否存在
实测回显:
$ useradd u4e1
$ tail -1 /etc/passwd
u4e1:x:1004:1006::/home/u4e1:/bin/sh
$ tail -1 /etc/shadow
u4e1:!:20741:0:99999:7:::
$ ls -ld /home/u4e1
ls: cannot access '/home/u4e1': No such file or directory
结果解读(以 /etc/passwd 的 7 个字段对照):
| 字段 | 实测值 | 含义 |
|---|---|---|
| 1 用户名 | u4e1 | 登录名 |
| 2 密码位 | x | 真实密码在 /etc/shadow |
| 3 UID | 1004 | 自动分配(当前最大普通 UID +1) |
| 4 GID | 1006 | 自动创建了同名主组 u4e1 |
| 5 GECOS | (空) | 注释未填写 |
| 6 家目录 | /home/u4e1 | 仅是记录值,目录并未创建 |
| 7 shell | /bin/sh | Ubuntu 默认(来自 /etc/default/useradd 的 SHELL) |
💡 要点:
- 不加
-m就没有家目录,/home/u4e1只存在于 passwd 记录里,登录后会提示无家目录并落到/。这是 useradd 与 adduser 最大的行为差异(adduser 默认建家目录)。 - shadow 记录
u4e1:!:20741:0:99999:7:::中:!表示密码已锁定(创建后必须passwd设置密码才能登录);20741是「自 1970-01-01 起的天数」,即最近一次改密日期 2026-10-15;99999为密码最长有效期(来自/etc/login.defs的PASS_MAX_DAYS);7为过期前警告天数。 - 虽然没指定
-g,useradd 仍自动创建了同名主组 u4e1(GID 1006),这是 Ubuntu 的默认行为(/etc/login.defs中USERGROUPS_ENAB yes)。
3.2 -m:创建家目录并复制 /etc/skel 骨架文件
-m(--create-home)让 useradd 创建 passwd 记录中的家目录,并把骨架目录 /etc/skel 下的文件复制进去。先看骨架源目录内容,再创建用户验证:
ls -la /etc/skel # 查看骨架源目录
useradd -m u4e2 # 创建用户并建家目录
tail -1 /etc/passwd # 确认账号记录
ls -la /home/u4e2 # 检查家目录内容与属主
ls -ld /home/u4e1 /home/u4e2 # 与 3.1 的 u4e1 对比
实测回显:
$ ls -la /etc/skel
total 24
drwxr-xr-x 2 root root 4096 Sep 24 14:12 .
drwxr-xr-x 1 root root 4096 Oct 15 08:42 ..
-rw-r--r-- 1 root root 220 Jan 6 2022 .bash_logout
-rw-r--r-- 1 root root 3771 Jan 6 2022 .bashrc
-rw-r--r-- 1 root root 807 Jan 6 2022 .profile
$ useradd -m u4e2
$ tail -1 /etc/passwd
u4e2:x:1005:1007::/home/u4e2:/bin/sh
$ ls -la /home/u4e2
total 20
drwxr-x--- 2 u4e2 u4e2 4096 Oct 15 08:42 .
drwxr-xr-x 1 root root 4096 Oct 15 08:42 ..
-rw-r--r-- 1 u4e2 u4e2 220 Jan 6 2022 .bash_logout
-rw-r--r-- 1 u4e2 u4e2 3771 Jan 6 2022 .bashrc
-rw-r--r-- 1 u4e2 u4e2 807 Jan 6 2022 .profile
$ ls -ld /home/u4e1 /home/u4e2
ls: cannot access '/home/u4e1': No such file or directory
drwxr-x--- 2 u4e2 u4e2 4096 Oct 15 08:42 /home/u4e2
💡 要点:
- 家目录内容与
/etc/skel完全一致(.bash_logout220 字节、.bashrc3771 字节、.profile807 字节),属主自动改为新用户,这就是「骨架文件」机制——新用户的 shell 初始化环境来自这里。 - 家目录权限是
drwxr-x---(0750),由/etc/login.defs的HOME_MODE 0750决定(Ubuntu 22.04 起新增该键),而非传统上由 umask 022 推出的 0755。 - 对比 3.1:
u4e1无家目录、u4e2有完整骨架,-m是给「真人用户」建号时的必备参数。
3.3 -s:指定登录 shell
-s(--shell)指定用户登录后使用的 shell,写入 passwd 第 7 字段。默认值是 /etc/default/useradd 里的 SHELL=/bin/sh,给真人账号一般显式指定 /bin/bash:
useradd -m -s /bin/bash u4e3
grep u4e3 /etc/passwd
实测回显:
$ useradd -m -s /bin/bash u4e3
$ grep u4e3 /etc/passwd
u4e3:x:1006:1008::/home/u4e3:/bin/bash
💡 要点:服务账号常用 -s /usr/sbin/nologin 禁止交互登录(见 3.11);可用的 shell 列表登记在 /etc/shells 中,指定不在列表内的路径也会被记录,但登录行为取决于该程序是否存在。
3.4 -u:指定 UID
-u(--uid)手动指定用户 ID。需要与既有应用约定 UID(如 NFS 跨机一致性)时使用:
useradd -m -u 1601 u4e4
id u4e4
grep u4e4 /etc/passwd
实测回显:
$ useradd -m -u 1601 u4e4
$ id u4e4
uid=1601(u4e4) gid=1601(u4e4) groups=1601(u4e4)
$ grep u4e4 /etc/passwd
u4e4:x:1601:1601::/home/u4e4:/bin/sh
💡 要点:UID 冲突时直接报错(见 3.22);确需两个用户共用一个 UID 要加 -o(见 3.15)。UID 分配范围由 /etc/login.defs 决定,实测本机为 UID_MIN 1000、UID_MAX 60000。
3.5 -g:指定主组 与 -G:指定附加组
-g(--gid)指定主组(passwd 第 4 字段),-G(--groups)指定附加组(写入 /etc/group 的成员列表),两者都可接组名或 GID。先用 groupadd 准备两个组再创建用户:
groupadd u4egrp # 准备主组
groupadd u4edev # 准备附加组
useradd -m -g u4egrp -G u4edev u4e5
id u4e5
groups u4e5
grep u4egrp /etc/group; grep u4edev /etc/group
实测回显:
$ groupadd u4egrp
$ groupadd u4edev
$ useradd -m -g u4egrp -G u4edev u4e5
$ id u4e5
uid=1602(u4e5) gid=1602(u4egrp) groups=1602(u4egrp),1603(u4edev)
$ groups u4e5
u4e5 : u4egrp u4edev
$ grep u4egrp /etc/group; grep u4edev /etc/group
u4egrp:x:1602:
u4edev:x:1603:u4e5
💡 要点:
id输出三段:uid、gid(主组)、groups(主组+全部附加组);groups 用户名是更简短的只读形态。- 附加组的落点是
/etc/group第 4 字段:u4edev:x:1603:u4e5表示 u4e5 是 u4edev 的成员;主组 u4egrp 的成员列表为空,因为主组关系记录在/etc/passwd。 -G可以一次给多个组:-G grp1,grp2,grp3(逗号分隔,中间不能有空格)。新建用户之前这些组必须已存在,否则报错。
3.6 -d:自定义家目录路径
-d(--home-dir)把家目录指到任意路径,常用于把数据盘挂载点(如 /data、/srv)作为用户主目录。注意:-d 只改记录值,实际建目录同样要配 -m:
useradd -d /srv/u4e6 -m u4e6
grep u4e6 /etc/passwd
ls -ld /srv/u4e6
ls -la /srv/u4e6
实测回显:
$ useradd -d /srv/u4e6 -m u4e6
$ grep u4e6 /etc/passwd
u4e6:x:1603:1604::/srv/u4e6:/bin/sh
$ ls -ld /srv/u4e6
drwxr-x--- 2 u4e6 u4e6 4096 Oct 15 08:42 /srv/u4e6
$ ls -la /srv/u4e6
total 20
drwxr-x--- 2 u4e6 u4e6 4096 Oct 15 08:42 .
drwxr-xr-x 1 root root 4096 Oct 15 08:42 ..
-rw-r--r-- 1 u4e6 u4e6 220 Jan 6 2022 .bash_logout
-rw-r--r-- 1 u4e6 u4e6 3771 Jan 6 2022 .bashrc
-rw-r--r-- 1 u4e6 u4e6 807 Jan 6 2022 .profile
💡 要点:-d 与 -m 连用时目录被创建并复制骨架;只写 -d 不写 -m 时仅改记录(同 3.1 现象)。父目录 /srv 必须已存在,否则创建失败。
3.7 -b:指定家目录基准目录
-b(--base-dir)在「没有用 -d 指定绝对路径」时,把家目录放到基准目录下(默认 /home,来自 useradd -D 的 HOME 值)。实测把基准目录改为 /tmp:
useradd -m -b /tmp u4ebase
grep u4ebase /etc/passwd
ls -ld /tmp/u4ebase
实测回显:
$ useradd -m -b /tmp u4ebase
$ grep u4ebase /etc/passwd
u4ebase:x:1706:1706::/tmp/u4ebase:/bin/bash
$ ls -ld /tmp/u4ebase
drwxr-x--- 2 u4ebase u4ebase 4096 Oct 15 08:48 /tmp/u4ebase
💡 要点:passwd 第 6 字段变成了 /tmp/u4ebase(基准目录 + 用户名拼接)。-b 只在 -d 缺省时生效,-d 给出绝对路径时 -b 被忽略。
3.8 -k:指定骨架目录
-k(--skel)把家目录模板从默认的 /etc/skel 换成自定义目录,适合批量创建带预置文件(说明文档、SSH 密钥、环境变量)的账号。先自建骨架目录再建号:
mkdir -p /root/demo/useradd/custom-skel
echo 'custom skeleton file' > /root/demo/useradd/custom-skel/README.demo
ls -A /root/demo/useradd/custom-skel
useradd -m -k /root/demo/useradd/custom-skel u4ek
ls -A /home/u4ek
cat /home/u4ek/README.demo
实测回显:
$ mkdir -p /root/demo/useradd/custom-skel
$ echo 'custom skeleton file' > /root/demo/useradd/custom-skel/README.demo
$ ls -A /root/demo/useradd/custom-skel
README.demo
$ useradd -m -k /root/demo/useradd/custom-skel u4ek
$ ls -A /home/u4ek
README.demo
$ cat /home/u4ek/README.demo
custom skeleton file
💡 要点:家目录里只有 README.demo,默认的 .bashrc 等文件不再出现——-k 是替换而不是追加。生产上常用「复制 /etc/skel 再补充业务文件」的目录作为 -k 参数,兼顾两者。
3.9 -e:账号过期日期 与 -f:密码宽限天数
-e(--expiredate)设置账号失效日期(格式 YYYY-MM-DD),到期后该账号无法登录;-f(--inactive)设置密码过期后再过多少天账号彻底停用,0 表示密码一过期立即停用,-1 表示不启用该机制。用 chage -l 验证落库结果:
useradd -m -e 2027-12-31 -f 5 u4e7
chage -l u4e7
实测回显:
$ useradd -m -e 2027-12-31 -f 5 u4e7
$ chage -l u4e7
Last password change : Oct 15, 2026
Password expires : never
Password inactive : never
Account expires : Dec 31, 2027
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
💡 要点:Account expires: Dec 31, 2027 即 -e 的落库结果。-f 5 对应 shadow 数据库的倒数第 2 个字段(chage -l 里在密码过期后才会体现)。临时外包、实习账号建号时带上 -e 是最省心的账号生命周期管理手段,事后可用 chage -E 修改。
3.10 -c:账号注释(GECOS 字段)
-c(--comment)写入 passwd 第 5 字段(GECOS),通常放真实姓名、部门、用途等说明。含逗号、空格时建议整体加引号:
useradd -m -c 'deploy account, ops team' u4e8
grep u4e8 /etc/passwd
实测回显:
$ useradd -m -c 'deploy account, ops team' u4e8
$ grep u4e8 /etc/passwd
u4e8:x:1605:1606:deploy account, ops team:/home/u4e8:/bin/sh
💡 要点:GECOS 字段内部可以含逗号(传统上逗号分隔姓名/办公室/电话等子字段),所以整串用单引号包住最稳妥。该字段会被 finger、getent passwd 等工具展示;事后可用 usermod -c 或 chfn 修改。
3.11 -r:创建系统用户(系统账号)
-r(--system)创建服务运行专用的系统账号:UID 取自系统段(100~999,本机实测从 999 往下取),默认不创建家目录,shadow 里密码位同样锁定:
useradd -r -s /usr/sbin/nologin u4esvc
id u4esvc
grep u4esvc /etc/passwd
ls -ld /home/u4esvc
实测回显:
$ useradd -r -s /usr/sbin/nologin u4esvc
$ id u4esvc
uid=999(u4esvc) gid=999(u4esvc) groups=999(u4esvc)
$ grep u4esvc /etc/passwd
u4esvc:x:999:999::/home/u4esvc:/usr/sbin/nologin
$ ls -ld /home/u4esvc
ls: cannot access '/home/u4esvc': No such file or directory
💡 要点:
- 系统账号 UID 落在
SYS_UID_MIN999)区间,且从大到小分配,与普通账号从小到大分配互不侵占,实测取到 999。SYS_UID_MAX(默认 100 -s /usr/sbin/nologin与-r是服务账号的标准组合:有身份(进程属主)但不能交互登录。- 即使
-r,若确实需要家目录(某些服务要写家目录),可以-r -m联用。
3.12 -M:显式不创建家目录
-M(--no-create-home)是 -m 的反义参数,即使未来默认值被改成「建家目录」,也强制不建。本机默认本来就不建,-M 是显式声明:
useradd -M u4em
grep u4em /etc/passwd
ls -ld /home/u4em
实测回显:
$ useradd -M u4em
$ grep u4em /etc/passwd
u4em:x:1703:1704::/home/u4em:/bin/bash
$ ls -ld /home/u4em
ls: cannot access '/home/u4em': No such file or directory
💡 要点:passwd 记录里仍是 /home/u4em,只是目录没建。脚本里显式写 -M 可以把意图写死,避免依赖 /etc/default/useradd 的当前状态。
3.13 -N:不创建同名主组
-N(--no-user-group)跳过「自动创建与用户同名的组」,主组改由 -g 指定(缺省时落到 useradd -D 的 GROUP 值)。实测挂到系统自带组 users(GID 100):
grep '^users' /etc/group
useradd -m -N -g users u4en
id u4en
实测回显:
$ grep '^users' /etc/group
users:x:100:
$ useradd -m -N -g users u4en
$ id u4en
uid=1704(u4en) gid=100(users) groups=100(users)
💡 要点:-N 必须配 -g 使用(或接受 GROUP 默认值),否则报「primary group 缺失」类错误。集中式用户组管理(多人共享一个 staff 组)会用到该模式。
3.14 -U:显式创建同名主组
-U(--user-group)是 -N 的反义参数,显式要求创建同名主组——这正是 Ubuntu 的默认行为,写出来同样是为了把意图固化:
useradd -m -U u4eu
id u4eu
grep '^u4eu' /etc/group
实测回显:
$ useradd -m -U u4eu
$ id u4eu
uid=1705(u4eu) gid=1705(u4eu) groups=1705(u4eu)
$ grep '^u4eu' /etc/group
u4eu:x:1705:
💡 要点:USERGROUPS_ENAB yes(/etc/login.defs)+ 无 -N/-g 时等价于默认加 -U。「每用户一个私有组 + 附加组协作」是 Debian/Ubuntu 的推荐模型,默认 umask 002 时同组可写、他人不可写。
3.15 -o:允许重复 UID
-o(--non-unique)允许创建与现有用户相同 UID 的账号(多用户名共享同一身份)。先确认 UID 1701 已被 u4eb 占用,再用 -o 强行复用:
useradd -m -o -u 1701 u4esame
id u4esame
实测回显:
$ useradd -m -o -u 1701 u4esame
$ id u4esame
uid=1701(u4eb) gid=1701(u4eb) groups=1701(u4eb)
💡 要点:注意 id u4esame 显示的是 uid=1701(u4eb)——按 UID 反查时返回 /etc/passwd 里先出现的那个用户名。两个账号文件权限完全互通,属于特殊需求(如历史系统兼容),日常运维应避免,审计工具也会把重复 UID 标记为告警项。
3.16 -p:写入已加密密码
-p(--password)把已经 crypt 加密的密码串直接写入 shadow,适合脚本无人值守建号。密码串用 openssl passwd -6 生成(SHA-512),创建后用 passwd -S 验证状态:
useradd -m -p "$(openssl passwd -6 'q1w2e3r4')" u4ep
passwd -S u4ep
grep u4ep /etc/shadow | cut -c1-40
实测回显:
$ useradd -m -p "$(openssl passwd -6 'q1w2e3r4')" u4ep
$ passwd -S u4ep
u4ep P 10/15/2026 0 99999 7 -1
$ grep u4ep /etc/shadow | cut -c1-40
u4ep:$6$kifdRNfx.vVGmCA0$yuM6W4Tgd6jzDmH
💡 技术拓展:
-p接受的加密串由哈希算法标识符开头,常见算法对比——
标识 算法 说明 $1$MD5-crypt 已不安全,仅兼容旧系统 $5$SHA-256-crypt shadow 工具支持 $6$SHA-512-crypt 本文实测所用,兼容性好 $y$yescrypt Ubuntu 22.04 交互式 passwd/chpasswd的默认算法本文因需要与
openssl passwd -6配合做无人值守演示,实际采用$6$;交互场景直接passwd 用户名更简单,不用关心算法选择。
💡 要点:passwd -S 第二列 P 表示已设置有效密码(对比 3.1 新建账号的 L/锁定态 !)。⚠️ 千万不要把明文传给 -p——明文会被当作加密串原样写入,结果等于密码永远无法匹配;命令行里传密码会留在 shell history 与 ps 快照中,批量场景更推荐 echo '用户名:密码' | chpasswd。
3.17 -K:临时覆盖 /etc/login.defs 默认值
-K(--key)在本次创建动作里覆盖 /etc/login.defs 的任意键值,只影响这一个新用户,不改全局配置。实测把该用户的密码最长有效期从默认 99999 天改为 30 天:
useradd -m -K PASS_MAX_DAYS=30 u4eK
chage -l u4eK | grep -E 'Maximum|Minimum'
实测回显:
$ useradd -m -K PASS_MAX_DAYS=30 u4eK
$ chage -l u4eK | grep -E 'Maximum|Minimum'
Minimum number of days between password change : 0
Maximum number of days between password change : 30
💡 要点:对比 3.9 的同款输出(Maximum ... : 99999),-K 只覆盖了本用户。可重复多个 -K 键=值。需要全局生效时应直接改 /etc/login.defs 而不是每次加 -K。
3.18 -l:不写 lastlog/faillog 数据库
-l(--no-log-init)跳过 lastlog(最后登录时间库)与 faillog(失败登录计数库)的初始化,用于用户基数极大的系统(如 LDAP/NIS 全域共享账号)避免逐个改写大文件。实测效果是 lastlog 里该用户保持「从未登录」的干净初始态:
useradd -m -l u4el
lastlog -u u4el
实测回显:
$ useradd -m -l u4el
$ lastlog -u u4el
Username Port From Latest
u4el **Never logged in**
💡 要点:普通服务器感知不到差别;只有当 /var/log/lastlog 是稀疏大文件、批量建号导致 I/O 抖动时,-l 才有实际收益。
3.19 --badnames:允许默认校验拒绝的用户名
shadow 4.8.1 对用户名的默认校验已经比老版本宽松(实测大写、数字开头、含点、含斜杠的名字都能直接创建):
useradd '9u4etest'; echo 退出码=$?
useradd 'u4e.test'; echo 退出码=$?
useradd 'u4e:test'; echo 退出码=$?
useradd 'u4e test'; echo 退出码=$?
实测回显:
$ useradd '9u4etest'; echo 退出码=$?
退出码=0
$ useradd 'u4e.test'; echo 退出码=$?
退出码=0
$ useradd 'u4e:test'; echo 退出码=$?
useradd: invalid user name 'u4e:test'
退出码=3
$ useradd 'u4e test'; echo 退出码=$?
useradd: invalid user name 'u4e test'
退出码=3
也就是说,默认只有含冒号或空格的名字会被拒(退出码 3)。--badnames 可以越过这道校验,实测允许带空格的名字建号:
useradd -m --badnames 'u4e test'
grep 'u4e test' /etc/passwd
ls -ld '/home/u4e test'
userdel -r 'u4e test'
实测回显:
$ useradd -m --badnames 'u4e test'; echo 退出码=$?
退出码=0
$ grep 'u4e test' /etc/passwd
u4e test:x:1706:1706::/home/u4e test:/bin/sh
$ ls -ld '/home/u4e test'
drwxr-x--- 2 u4e test u4e test 4096 Oct 15 08:50 /home/u4e test
$ userdel -r 'u4e test'; echo 退出码=$?
userdel: u4e test mail spool (/var/mail/u4e test) not found
退出码=0
💡 要点:⚠️ --badnames 是「我知道后果」的开关:含冒号的名字一旦写入 /etc/passwd 会破坏 7 字段结构、直接污染账号库,无论如何都不要创建含冒号的用户名;空格名虽然能存进去,但所有下游工具(脚本、NFS、LDAP 同步)都要额外加引号处理,生产环境应坚持小写字母/数字/下划线/连字符的命名规范。
3.20 --extrausers:写入附加用户库
--extrausers 把账号写入 /var/lib/extrausers/ 下的独立 passwd/group/shadow 文件,而不是系统主库——这是 Ubuntu Core / 只读根文件系统上追加用户的机制。先准备库文件(空文件即可),再实测:
mkdir -p /var/lib/extrausers && touch /var/lib/extrausers/passwd /var/lib/extrausers/group /var/lib/extrausers/shadow && chmod 644 /var/lib/extrausers/*
useradd --extrausers u4exu
grep u4exu /var/lib/extrausers/passwd
getent passwd u4exu
userdel --extrausers u4exu
grep u4exu /var/lib/extrausers/passwd
实测回显:
$ mkdir -p /var/lib/extrausers && touch /var/lib/extrausers/passwd /var/lib/extrausers/group /var/lib/extrausers/shadow && chmod 644 /var/lib/extrausers/*
$ useradd --extrausers u4exu
$ grep u4exu /var/lib/extrausers/passwd
u4exu:x:1006:1008::/home/u4exu:/bin/bash
$ getent passwd u4exu
$ userdel --extrausers u4exu
$ grep u4exu /var/lib/extrausers/passwd
💡 要点:账号确实写进了 /var/lib/extrausers/passwd,但 getent passwd 查不到——因为标准 Ubuntu 没安装/配置 libnss-extrausers NSS 模块,getent 不读这个库。完整启用需要安装该模块并在 /etc/nsswitch.conf 的 passwd/group/shadow 行追加 extrausers 数据源。普通服务器请忽略此参数。
3.21 -D:查看与修改默认值
-D 不带参数时打印当前默认值;带 -s、-b、-e、-f、-g 时把这些值写进 /etc/default/useradd 成为以后所有 useradd 的默认值。先看默认值全貌与其配置文件:
useradd -D
cat /etc/default/useradd
实测回显(默认值 7 项):
$ useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
/etc/default/useradd 实测内容(Ubuntu 22.04 原始状态,大量解释以注释给出):
# Default values for useradd(8)
#
# The SHELL variable specifies the default login shell on your
# system.
# Similar to DSHELL in adduser. However, we use "sh" here because
# useradd is a low level utility and should be as general
# as possible
SHELL=/bin/sh
#
# The default group for users
# 100=users on Debian systems
# Same as USERS_GID in adduser
# This argument is used when the -n flag is specified.
# The default behavior (when -n and -g are not specified) is to create a
# primary user group with the same name as the user being added to the
# system.
# GROUP=100
#
# The default home directory. Same as DHOME for adduser
# HOME=/home
#
# The number of days after a password expires until the account
# is permanently disabled
# INACTIVE=-1
#
# The default expire date
# EXPIRE=
#
# The SKEL variable specifies the directory containing "skeletal" user
# files; in other words, files such as a sample .profile that will be
# copied to the new user's home directory when it is created.
# SKEL=/etc/skel
#
# Defines whether the mail spool should be created while
# creating the account
# CREATE_MAIL_SPOOL=yes
逐项说明:GROUP=100(-N 且无 -g 时的主组,Ubuntu 默认不启用此行,改为每用户私有组);HOME=/home(-b 的默认基准目录);INACTIVE=-1(密码过期后不启用宽限停用);EXPIRE=(账号默认不过期,即 3.9 的 -e 默认值);SHELL=/bin/sh(默认登录 shell);SKEL=/etc/skel(骨架目录,3.8 的 -k 默认值);CREATE_MAIL_SPOOL=no(不在 /var/mail 建邮箱文件)。
再用 -D -s 实测修改默认 shell:
useradd -D -s /bin/bash
cat /etc/default/useradd | head -8
实测回显:
$ useradd -D -s /bin/bash
$ cat /etc/default/useradd | head -8
# Default values for useradd(8)
#
# The SHELL variable specifies the default login shell on your
# system.
# Similar to DSHELL in adduser. However, we use "sh" here because
# useradd is a low level utility and should be as general
# as possible
SHELL=/bin/bash
⚠️ 实测踩坑:useradd -D -s 改写 /etc/default/useradd 时,会把原本注释掉的 GROUP=100、HOME=/home、INACTIVE=-1、EXPIRE=、SKEL=/etc/skel、CREATE_MAIL_SPOOL=no 以激活状态追加到文件尾部,其中 GROUP=100 一旦生效,此后不带 -g/-N 的 useradd 主组行为会从「私有组」变成「共享 users 组」。改完默认值后务必 cat 复核整个文件,不需要的激活行应删掉或注释回去(本次实测后已还原为 SHELL=/bin/sh 原始状态,useradd -D 复核输出与最初一致)。
3.22 报错行为与退出码实测
同名用户重复创建、UID 冲突是脚本里最常见的两类失败,实测退出码便于脚本判断:
useradd -m u4ea1701
useradd -m u4ea1701; echo 退出码=$?
useradd -m -u 1601 u4e9
useradd -m -u 1999 u4euid; useradd -u 1999 u4euid2; echo 退出码=$?
实测回显:
$ useradd -m u4ea1701
$ useradd -m u4ea1701; echo 退出码=$?
useradd: user 'u4ea1701' already exists
退出码=9
$ useradd -m -u 1601 u4e9
useradd: UID 1601 is not unique
$ useradd -m -u 1999 u4euid; useradd -u 1999 u4euid2; echo 退出码=$?
useradd: UID 1999 is not unique
退出码=4
💡 要点:退出码 9 = 用户名已存在,4 = UID 已被占用,3 = 用户名非法(3.19)。CI/脚本里用 $? 区分失败原因比解析 stderr 更稳。错误发生时账号不会被创建一半——shadow 的写库是事务式的(先加锁、校验、再提交)。
3.23 其他低频参数速览
以下参数依赖特定环境(chroot 维护盘、SELinux、Btrfs),在标准 Ubuntu 22.04 上不常用,按 --help 原文与手册语义速览:
| 参数 | 语义(摘自 --help 实测原文) | 适用场景 |
|---|---|---|
-R, --root CHROOT_DIR | directory to chroot into | 挂载了目标系统根的救援环境里,对目标盘建号 |
-P, --prefix PREFIX_DIR | prefix directory where are located the /etc/* files | 只重定位 /etc 等目录前缀,不进 chroot 的根文件系统构建场景 |
-Z, --selinux-user SEUSER | use a specific SEUSER for the SELinux user mapping | SELinux 系统(Ubuntu 默认 AppArmor,不用此参数) |
--btrfs-subvolume-home | use BTRFS subvolume for home directory | 根文件系统为 Btrfs 时家目录建为子卷,支持快照回滚 |
-h, --help | display this help message and exit | 查看用法(2.2 节全量输出) |
💡 说明:表中前三项需要对应环境才能产生有意义的结果,本文未做实测,语义以
useradd --help原文为准;--btrfs-subvolume-home在非 Btrfs 文件系统上执行会报错退出。
3.24 拓展:useradd 与 adduser 在 Ubuntu 上的差异
Ubuntu 同时提供 useradd(shadow)与 adduser(Debian 系独立包),实测两者关键差异。先看 adduser 的配置基线与包归属:
grep -E '^(DSHELL|DHOME|FIRST_UID|LAST_UID|ADD_EXTRA_GROUPS)' /etc/adduser.conf
dpkg -S /usr/sbin/adduser /usr/sbin/useradd
dpkg -l adduser | tail -1
实测回显:
$ grep -E '^(DSHELL|DHOME|FIRST_UID|LAST_UID|ADD_EXTRA_GROUPS)' /etc/adduser.conf
DSHELL=/bin/bash
DHOME=/home
FIRST_UID=1000
LAST_UID=59999
$ dpkg -S /usr/sbin/adduser /usr/sbin/useradd
adduser: /usr/sbin/adduser
passwd: /usr/sbin/useradd
$ dpkg -l adduser | tail -1
ii adduser 3.118ubuntu5 all add and remove users and groups
对照表(差异点均来自前文实测):
| 维度 | useradd(本文主角) | adduser |
|---|---|---|
| 软件包 | passwd(shadow 4.8.1) | adduser 3.118ubuntu5(Perl 脚本) |
| 交互 | 完全非交互 | 交互式:询问密码、GECOS,带确认 |
| 家目录 | 不加 -m 不创建 | 默认创建并复制 /etc/skel |
| 默认 shell | /bin/sh(/etc/default/useradd) | /bin/bash(adduser.conf 的 DSHELL) |
| UID 起点 | UID_MIN=1000 起自动分配 | FIRST_UID=1000 起自动分配 |
| 密码 | 创建后锁定(!),需 passwd/chpasswd | 创建过程中交互设置 |
| 适用 | 脚本、Ansible、系统盘构建 | 管理员手工建号 |
⚠️ 说明:adduser 是交互式命令,无法在纯自动化会话中完整走完其问答流程,故本节只实测其配置基线与包归属;行为差异表中交互相关的描述以 adduser --help(2.1 节拓展表中已列)与上表实测配置项为准。
四、实战示例
4.1 场景一:标准开发账号一条龙(组 + 用户 + 家目录 + shell + 注释 + 密码)
最常用的真人账号开通流程:建附加组 → 建号(家目录/bash/注释/附加组一次到位)→ chpasswd 设密 → 三项验证。
groupadd u4edevs
useradd -m -s /bin/bash -c 'zhangwei, dev team' -G u4edevs u4ezw
echo 'u4ezw:q1w2e3r4' | chpasswd
id u4ezw
passwd -S u4ezw
grep u4ezw /etc/passwd
ls -A /home/u4ezw
实测回显:
$ groupadd u4edevs
$ useradd -m -s /bin/bash -c 'zhangwei, dev team' -G u4edevs u4ezw
$ echo 'u4ezw:q1w2e3r4' | chpasswd
$ id u4ezw
uid=1005(u4ezw) gid=1008(u4ezw) groups=1008(u4ezw),1007(u4edevs)
$ passwd -S u4ezw
u4ezw P 10/15/2026 0 99999 7 -1
$ grep u4ezw /etc/passwd
u4ezw:x:1005:1008:zhangwei, dev team:/home/u4ezw:/bin/bash
$ ls -A /home/u4ezw
.bash_logout
.bashrc
.profile
验证闭环:id 确认主组+附加组、passwd -S 确认密码已生效(P)、ls -A 确认骨架文件就位。chpasswd 从 stdin 读「用户名:明文」并用系统默认算法(Ubuntu 22.04 为 yescrypt)写库,比拼 -p 更安全省事。
4.2 场景二:应用服务专用系统账号
给常驻服务(如自研 daemon)建运行身份:系统账号 + nologin + 专属数据目录。
useradd -r -s /usr/sbin/nologin -d /opt/u4eapp u4eapp
mkdir -p /opt/u4eapp && chown u4eapp:u4eapp /opt/u4eapp
ls -ld /opt/u4eapp
id u4eapp
实测回显:
$ useradd -r -s /usr/sbin/nologin -d /opt/u4eapp u4eapp
$ mkdir -p /opt/u4eapp && chown u4eapp:u4eapp /opt/u4eapp
$ ls -ld /opt/u4eapp
drwxr-xr-x 2 u4eapp u4eapp 4096 Oct 15 08:52 /opt/u4eapp
$ id u4eapp
uid=999(u4eapp) gid=999(u4eapp) groups=999(u4eapp)
要点:-r 不建家目录,服务的运行目录自己 mkdir + chown,职责清晰;-d 只是让 passwd 记录与实际目录一致(部分工具会读 HOME 环境变量)。
4.3 场景三:临时外包账号(带过期时间与宽限期)
外包/实习账号建号时直接写死生命周期,到期自动失效,无需人工注销:
useradd -m -s /bin/bash -e 2026-12-31 -f 3 u4etemp
chage -l u4etemp
chage -E 2027-02-28 u4etemp
chage -l u4etemp | grep 'Account expires'
实测回显:
$ useradd -m -s /bin/bash -e 2026-12-31 -f 3 u4etemp
$ chage -l u4etemp
Last password change : Oct 15, 2026
Password expires : never
Password inactive : never
Account expires : Dec 31, 2026
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
$ chage -E 2027-02-28 u4etemp
$ chage -l u4etemp | grep 'Account expires'
Account expires : Feb 28, 2027
要点:-e 管账号生死,-f 管密码过期后的宽限;续期用 chage -E 改日期即可,本例已把账号延长到 2027-02-28。
4.4 场景四:统一模板家目录(-b + -k 组合批量开班)
给培训/沙箱用户统一预置说明文件:先做模板骨架目录,再逐个建号:
mkdir -p /root/demo/useradd/train-skel
echo 'training env, do not store prod data' > /root/demo/useradd/train-skel/WELCOME.txt
useradd -m -b /home -k /root/demo/useradd/train-skel -s /bin/bash u4estudent1
useradd -m -b /home -k /root/demo/useradd/train-skel -s /bin/bash u4estudent2
ls -A /home/u4estudent1 /home/u4estudent2
cat /home/u4estudent2/WELCOME.txt
实测回显:
$ mkdir -p /root/demo/useradd/train-skel
$ echo 'training env, do not store prod data' > /root/demo/useradd/train-skel/WELCOME.txt
$ useradd -m -b /home -k /root/demo/useradd/train-skel -s /bin/bash u4estudent1
$ useradd -m -b /home -k /root/demo/useradd/train-skel -s /bin/bash u4estudent2
$ ls -A /home/u4estudent1 /home/u4estudent2
/home/u4estudent1:
WELCOME.txt
/home/u4estudent2:
WELCOME.txt
$ cat /home/u4estudent2/WELCOME.txt
training env, do not store prod data
要点:-k 替换了默认骨架,两个学员家目录里只有统一预置的 WELCOME.txt;按 3.8 的提示,把 /etc/skel 的文件复制进模板目录即可兼顾默认环境与定制内容。
4.5 场景五:实测账号的清理(配套 userdel)
场景一~四的演示账号按剧情清理,删除后逐项复核(userdel 详解见《userdel 命令详解与参数实测(Ubuntu 22.04)》篇):
userdel -r u4ezw
userdel u4eapp
userdel -r u4etemp
userdel -r u4estudent1
userdel -r u4estudent2
groupdel u4edevs
rm -rf /opt/u4eapp
grep -E 'u4ezw|u4eapp|u4etemp|u4estudent' /etc/passwd
实测回显(节选,含最终复核):
$ userdel -r u4ezw; userdel u4eapp; userdel -r u4etemp; userdel -r u4estudent1; userdel -r u4estudent2
userdel: u4ezw mail spool (/var/mail/u4ezw) not found
userdel: u4etemp mail spool (/var/mail/u4etemp) not found
userdel: u4estudent1 mail spool (/var/mail/u4estudent1) not found
userdel: u4estudent2 mail spool (/var/mail/u4estudent2) not found
$ grep -E 'u4ezw|u4eapp|u4etemp|u4estudent' /etc/passwd; echo grep退出=$?
grep退出=1
要点:grep 退出码 1 表示账号库中已无残留;mail spool not found 是因为默认 CREATE_MAIL_SPOOL=no 没建过邮箱文件,属预期提示而非错误(详见 userdel 篇)。
五、注意事项与常见问题
Q1:useradd 建完用户为什么登不上?
三层原因,逐项排查:① shadow 里密码位是 !(锁定态,见 3.1 实测),必须 passwd 用户名 或 chpasswd 设密;② 默认 shell 是 /bin/sh,体验差但能登录,需要 -s /bin/bash;③ 没加 -m 时登录后无家目录,shell 会警告并落到 / 目录,脚本化环境还会连带 .bashrc 不生效。
Q2:useradd --version 报 unrecognized option 是坏了吗?
不是。shadow 4.8.1 的 useradd 本就没有 --version 选项(见 2.3 实测),查版本用 dpkg -l passwd,输出 1:4.8.1-2ubuntu2.2 即 shadow 4.8.1。
Q3:为什么本机新用户 UID 从 1004/1005 开始,而教程都说从 1000 开始?
自动分配规则是「取当前普通 UID 区间(UID_MINUID_MAX,实测 100060000)内最大值 +1」。本机 1000~1004 已被既有用户占用,所以新号顺延;全新系统上就是 1000 起。
Q4:useradd -D -s /bin/bash 改完默认值,为什么后来不带 -g 建的用户主组变成了 users?
-D 改写 /etc/default/useradd 时会把注释掉的默认行以激活状态重写进文件(见 3.21 的踩坑实录),GROUP=100(users 组)由此生效。处理:手工删掉或注释该行,再用 useradd -D 与 cat /etc/default/useradd 双重复核。
Q5:-p 传了密码但用户还是登不上?
-p 要求的是 crypt 加密串,不是明文。明文被原样写入 shadow 后永远无法匹配。正确做法:useradd -p "$(openssl passwd -6 '密码')" 用户名,或更简单的 echo '用户名:密码' | chpasswd(见 3.16 实测)。
Q6:加了 -d 却没有目录?
-d 只写记录不建目录,必须 -d 路径 -m 联用(见 3.6);同时确认父目录存在且路径拼写正确。
Q7:can not lock /etc/passwd 之类的锁报错怎么处理?
shadow 工具串行改库,出现该报错通常是另一个账号管理进程(useradd/usermod/passwd)正在执行或异常退出残留锁文件 /etc/passwd.lock、/etc/shadow.lock、/etc/group.lock。先确认无并发进程,再删除残留锁文件重试。
Q8:家目录权限为什么是 750 而不是 755?
Ubuntu 22.04 的 /etc/login.defs 新增 HOME_MODE 0750,建家目录时优先用它(实测 3.2 的 drwxr-x---);该键不存在时才退回按 UMASK 计算。需要放宽时改 HOME_MODE 或事后 chmod。
Q9:生产上还有哪些固化建议?
① 真人账号统一 useradd -m -s /bin/bash -c '姓名,部门';② 服务账号统一 useradd -r -s /usr/sbin/nologin;③ 临时账号必带 -e;④ 脚本判断成败用退出码(9 用户名冲突/4 UID 冲突/3 名字非法);⑤ 建号后用 id、passwd -S、chage -l、ls -A /home/用户名 四件套复核。
六、参考链接
- useradd(8) 手册(Ubuntu 22.04 jammy):https://manpages.ubuntu.com/manpages/jammy/en/man8/useradd.8.html
- login.defs(5) 手册(默认值与 UID_MIN/HOME_MODE 等键):https://manpages.ubuntu.com/manpages/jammy/en/man5/login.defs.5.html
- adduser(8) 手册(Ubuntu 22.04 jammy):https://manpages.ubuntu.com/manpages/jammy/en/man8/adduser.8.html
- chage(1) 手册(账号过期策略查看/修改):https://manpages.ubuntu.com/manpages/jammy/en/man1/chage.1.html
- shadow 工具集源码仓库:https://github.com/shadow-maint/shadow
- usermod(8) 手册(账号属性后续修改):https://manpages.ubuntu.com/manpages/jammy/en/man8/usermod.8.html