给服务器加用户这件事,量小的时候怎么都行:useradd 敲一下、passwd 设个密码就完事。但一旦要一次建几十上百个账号——比如给学生开实训环境、给新入职的一批员工开权限、给外包团队开临时账号——手工敲就变成了事故温床:密码记混、uid 撞车、主目录权限漏设、离职的忘记删。
这篇文章讲怎么用 Shell 脚本把批量建用户这件事做得规范、可重复、可追溯。内容包括:用户与组的基础概念、useradd 的常用参数、读取名单批量创建、给 sudo 权限、以及到期自动失效和离职回收的处理。
文中的 uid/gid 区间、密码策略参数都是常见的经验值,可按企业规范调整。涉及权限授予和删除用户的部分会给出风险提示——给错 sudo 权限和误删用户都是很严重的问题,请务必看清再做。
一、先把概念理清楚
Linux 的用户管理围绕三个东西展开:用户(user)、组(group)、以及权限(permission)。理解它们的关系,是用对命令的前提。
每个用户有一个数字 uid,系统靠 uid 而不是用户名来判定身份。每个用户至少属于一个主组(primary group),还可以加入若干附加组(supplementary group)。文件权限则由属主、属组和其他人的读/写/执行位决定,配合 umask 决定新建文件的默认权限。
系统的用户和自建用户通常分开管理,靠 uid 区间区分:
| 用户类型 | 常见 uid 区间 | 说明 | 举例 |
|---|---|---|---|
| 系统用户 | 1-999(RHEL 系常见) | 给服务进程用,不登录 | nginx、mysql、sshd |
| 普通用户 | 1000 起 | 真实登录用户 | 运维、开发账号 |
| root | 0 | 超级用户 | root |
| nobody | 65534 | 匿名/降权用 | nobody |
新增登录用户时,建议从 1000 以上开始分配,避免和系统用户撞号。uid 冲突会导致文件属主显示错乱、权限判断出错,是很隐蔽的故障。
二、useradd 关键参数
批量建用户之前,得先把 useradd 的参数摸熟。它和 adduser 是两个不同的东西:useradd 是底层命令、可精细控制;adduser 是很多发行版提供的友好封装,交互式、自动化时不方便。脚本里统一用 useradd。
| 参数 | 作用 | 示例 |
|---|---|---|
-m |
创建主目录 | useradd -m alice |
-d |
指定主目录路径 | -d /data/home/alice |
-s |
指定登录 shell | -s /bin/bash |
-G |
附加组(可多个,逗号分隔) | -G docker,developers |
-g |
主组 | -g developers |
-u |
指定 uid | -u 1500 |
-c |
备注/注释 | -c "张三 研发部" |
-e |
账号过期日期 | -e 2027-12-31 |
-r |
创建系统用户 | -r -s /sbin/nologin appsvc |
-M |
不创建主目录 | -M guest1 |
有几个默认行为的坑要记住:useradd 默认不创建主目录,必须加 -m(Debian 系里 adduser 会默认创建,但 useradd 不会);默认 shell 可能是 /bin/sh,需要交互环境就得显式 -s /bin/bash;默认不设密码,账号建出来后是锁定状态,必须单独设密码或设密钥。
三、批量创建:从名单文件出发
最常见的形式是给一个用户名单文件,每行一个用户,批量创建并统一设初始密码。
先准备名单文件 users.csv:
# 用户名,主组,附加组,备注
alice,developers,docker,研发部-张三
bob,developers,docker,研发部-李四
carol,ops,sudo,运维部-王五
dave,test,,测试部-赵六
#!/usr/bin/env bash
# 批量创建用户脚本
set -uo pipefail
USERS_FILE="${1:-users.csv}"
DEFAULT_SHELL="/bin/bash"
UID_START=1500
LOG="/var/log/user_batch_add.log"
[ -r "$USERS_FILE" ] || { echo "名单文件不存在: $USERS_FILE"; exit 1; }
# 计算当前最大 uid,确定起始点
CUR_UID=$(getent passwd | awk -F: '$3>=1000 && $3<60000 {print $3}' | sort -n | tail -1)
UID_START=$(( ${CUR_UID:-1000} + 1 ))
echo "[$(date '+%F %T')] 开始批量建用户,起始 uid=$UID_START" | tee -a "$LOG"
while IFS=',' read -r uname pgroup sgroup comment; do
# 跳过空行与注释
[ -z "$uname" ] && continue
case "$uname" in \#*) continue;; esac
# 已存在则跳过
if id "$uname" >/dev/null 2>&1; then
echo "已存在,跳过: $uname" | tee -a "$LOG"
continue
fi
# 主组不存在则先建
getent group "$pgroup" >/dev/null || groupadd "$pgroup"
# 组装附加组参数
G_OPT=""
[ -n "$sgroup" ] && G_OPT="-G $sgroup"
useradd -m -s "$DEFAULT_SHELL" -u "$UID_START" \
-g "$pgroup" $G_OPT -c "$comment" "$uname"
if [ $? -eq 0 ]; then
# 生成随机初始密码并强制首次登录修改
PASS=$(tr -dc 'A-Za-z0-9@#%'
这段脚本里几个细节值得说明。uid 动态分配:先算出当前最大 uid 再往上加,避免固定写死 1500 撞号。主组自动创建:getent group 判断组是否存在,不存在先 groupadd,否则 useradd 会失败。chage -d 0:把密码最后修改日期设为 0,效果是用户下次登录必须改密码,这是发放初始密码的标准做法。幂等:已存在的用户直接跳过,脚本可以重复跑而不出错,这对自动化很重要。
初始密码用 /dev/urandom 随机生成,比手写"123456"安全得多。但要注意脚本日志里打印了明文密码,这个日志文件的权限必须收紧。
# 保护含初始密码的日志
sudo chmod 600 /var/log/user_batch_add.log
sudo chown root:root /var/log/user_batch_add.log
四、批量授予 sudo 权限
建完用户,下一步常是授权。授权有两种粒度:全量 sudo(能执行任何命令)和受限 sudo(只能执行指定命令)。能用受限的就别用全量,这是最小权限原则。
# 方式一:把用户加入 wheel 组(RHEL系)或 sudo 组(Debian/Ubuntu)
# RHEL/CentOS/Rocky 的 sudoers 里 wheel 组默认有全部权限
usermod -aG wheel alice
# Debian/Ubuntu 的 sudo 组默认有全部权限
usermod -aG sudo alice
# 方式二:用 sudoers.d 精细授权(推荐,不动主文件)
# 给 ops 组的人只开放服务管理和日志查看
sudo tee /etc/sudoers.d/ops-limited >/dev/null <<'EOF'
%ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, \
/usr/bin/systemctl status *, /usr/bin/journalctl -u nginx*
EOF
sudo chmod 440 /etc/sudoers.d/ops-limited
改 sudoers 极其危险:一旦语法写错,可能导致无法使用 sudo,严重时连 root 都进不去。务必用 visudo 或 visudo -c 校验,并且永远不要直接编辑 /etc/sudoers 主文件,而是往 /etc/sudoers.d/ 放独立文件。
# 校验语法(必须做)
sudo visudo -c
# 校验特定文件
sudo visudo -cf /etc/sudoers.d/ops-limited
NOPASSWD 会让该命令免密执行,方便自动化但降低安全性。只在确实需要脚本化调用的场景使用,普通人工操作建议保留密码验证。
五、账号有效期与离职回收
账号管理不只"建",还要"收"。给临时账号设到期时间,能避免被人遗忘的"僵尸账号"成为安全隐患。
# 建号时设过期(-e 日期 或 -e 天数)
useradd -m -s /bin/bash -e 2027-12-31 temp01 # 到 2027-12-31 失效
# 给已有账号设置到期
chage -E 2027-12-31 temp01
# 查看账号密码/过期信息
chage -l temp01
# 密码有效期策略(经验值:90 天强制改密)
chage -M 90 -m 1 -W 7 alice
# -M 最长有效期 -m 最短使用期 -W 过期前提醒天数
到期后账号会失效,无法登录,但文件和数据还在,需要时再处理。这比"用密码锁住"更明确。
离职或临时任务结束时回收账号,要先禁用、再观察、最后删除,不要一上来就删:
# 第一步:锁定账号(禁止登录,但保留文件和进程归属)
sudo usermod -L alice # 锁定密码
sudo usermod -s /sbin/nologin alice # 禁止交互登录
sudo pkill -u alice # 踢掉该用户所有进程(谨慎!会中断其运行的服务)
# 第二步:观察一段时间(比如 30 天),确认没有业务依赖
# 第三步:确认无误后再删除
sudo userdel -r alice # -r 连主目录和邮件一并删除
# 批量回收:从名单文件批量禁用
while read -r u; do
id "$u" >/dev/null 2>&1 && { usermod -L "$u"; usermod -s /sbin/nologin "$u"; echo "已禁用 $u"; }
done < retire_list.txt
六、风险提示与回滚
删除用户是不可逆操作。userdel -r 会删掉该用户的主目录,如果目录里有业务数据(比如某个服务以该用户身份运行、数据放在其主目录下),删了就没了。删除前务必:先 ls -la /home/alice 和 find / -user alice 看看有没有重要文件;先做备份或用 usermod -L 禁用观察一段时间。
uid/gid 冲突会让文件属主显示成数字。如果发现某目录的属主变成一串数字(如 1005),说明对应的 uid 已被删除,需要 chown 修正属主。
改 sudoers 导致 sudo 失效时,如果还能用 root 直接登录(或通过单用户模式/恢复控制台),用 visudo 修正即可;如果连 root 都受限,需要进入救援模式挂载根分区修复。这是为什么容器和云主机都建议保留一个能用控制台登录的 root 或运维账号。
回滚方式:批量创建脚本的产物是系统账号,回滚即按上面的流程禁用或删除对应用户;名单文件和审计日志要保留,便于核对。脚本本身放在 /usr/local/bin/ 并留一份 .bak 备份。
常见问题
Q1:useradd 建完用户登录不了,密码也没设,为什么?
useradd 默认不设密码,账号是锁定状态;同时它默认也不创建主目录、shell 可能是 /bin/sh。要能正常登录,通常需要 -m -s /bin/bash 加 chpasswd 设密码。用 passwd -S 用户名 可以查看账号密码状态(L 表示锁定,P 表示已设密码)。
Q2:怎么让新用户第一次登录必须改密码?
用 chage -d 0 用户名,把"密码最后修改日期"设为 0,系统会判定密码已过期,下次登录强制修改。这是发初始密码的标准做法。
Q3:给用户加了组,为什么权限没生效?
用户组变更需要重新登录才生效,因为组信息在登录时写入会话。临时想生效可用 newgrp 组名 切到新组,或让用户重新登录。脚本里对已登录用户的权限变更要注意这一点。
Q4:userdel 和 userdel -r 有什么区别?
userdel 只删用户记录,主目录保留;userdel -r 连主目录和邮件一起删。默认建议先用不带 -r 的,确认主目录里没有要保留的数据后再手动清理,或者直接保留目录只删账号。
Q5:批量脚本如何保证安全、不出错?
三条:一是幂等,已存在的用户跳过,可重复执行;二是先禁用、观察、再删除,给"反悔"留时间;三是所有操作写日志,日志文件权限收紧到 600。校验收尾用 pwck 检查用户数据库一致性。
总结
- 批量建用户的核心是"名单驱动 + 幂等脚本",用 uid 动态分配避免撞号,已存在则跳过。
useradd默认不建主目录、不设密码、shell 可能不是 bash,脚本里要显式指定-m -s /bin/bash。- 初始密码用随机生成并配合
chage -d 0强制首登改密;含密码的日志必须限制权限。 - 授权遵循最小权限原则,优先用
/etc/sudoers.d/受限授权;改 sudoers 必须visudo -c校验,切勿直接编辑主文件。 - 账号要能"建"也要能"收":用
chage -E设有效期,离职先禁用观察再删除,删除前核查数据与文件归属。
企业QQ咨询




