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 差异。

Linux命令Linux系统UbuntuUbuntu 22.04命令行用户权限2026/10/15

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交互方式
useraddpasswd(shadow 4.8.1)低层通用工具,全发行版行为一致需显式 -m 才创建/bin/sh(Ubuntu 默认配置)非交互,参数精确可控
adduseradduser(3.118ubuntu5)Debian/Ubuntu 的高层包装脚本默认创建/bin/bash(DSHELL)交互式,提示输入密码、全名等
newuserspasswd(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 UID1004自动分配(当前最大普通 UID +1)
4 GID1006自动创建了同名主组 u4e1
5 GECOS(空)注释未填写
6 家目录/home/u4e1仅是记录值,目录并未创建
7 shell/bin/shUbuntu 默认(来自 /etc/default/useradd 的 SHELL)

💡 要点:

  1. 不加 -m 就没有家目录,/home/u4e1 只存在于 passwd 记录里,登录后会提示无家目录并落到 /。这是 useradd 与 adduser 最大的行为差异(adduser 默认建家目录)。
  2. shadow 记录 u4e1:!:20741:0:99999:7::: 中:! 表示密码已锁定(创建后必须 passwd 设置密码才能登录);20741 是「自 1970-01-01 起的天数」,即最近一次改密日期 2026-10-15;99999 为密码最长有效期(来自 /etc/login.defs 的 PASS_MAX_DAYS);7 为过期前警告天数。
  3. 虽然没指定 -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

💡 要点:

  1. 家目录内容与 /etc/skel 完全一致(.bash_logout 220 字节、.bashrc 3771 字节、.profile 807 字节),属主自动改为新用户,这就是「骨架文件」机制——新用户的 shell 初始化环境来自这里。
  2. 家目录权限是 drwxr-x---(0750),由 /etc/login.defs 的 HOME_MODE 0750 决定(Ubuntu 22.04 起新增该键),而非传统上由 umask 022 推出的 0755。
  3. 对比 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

💡 要点:

  1. id 输出三段:uid、gid(主组)、groups(主组+全部附加组);groups 用户名 是更简短的只读形态。
  2. 附加组的落点是 /etc/group 第 4 字段:u4edev:x:1603:u4e5 表示 u4e5 是 u4edev 的成员;主组 u4egrp 的成员列表为空,因为主组关系记录在 /etc/passwd。
  3. -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

💡 要点:

  1. 系统账号 UID 落在 SYS_UID_MINSYS_UID_MAX(默认 100999)区间,且从大到小分配,与普通账号从小到大分配互不侵占,实测取到 999。
  2. -s /usr/sbin/nologin 与 -r 是服务账号的标准组合:有身份(进程属主)但不能交互登录。
  3. 即使 -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-cryptshadow 工具支持
$6$SHA-512-crypt本文实测所用,兼容性好
$y$yescryptUbuntu 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_DIRdirectory to chroot into挂载了目标系统根的救援环境里,对目标盘建号
-P, --prefix PREFIX_DIRprefix directory where are located the /etc/* files只重定位 /etc 等目录前缀,不进 chroot 的根文件系统构建场景
-Z, --selinux-user SEUSERuse a specific SEUSER for the SELinux user mappingSELinux 系统(Ubuntu 默认 AppArmor,不用此参数)
--btrfs-subvolume-homeuse BTRFS subvolume for home directory根文件系统为 Btrfs 时家目录建为子卷,支持快照回滚
-h, --helpdisplay 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/用户名 四件套复核。

六、参考链接