1220 lines
33 KiB
Bash
Executable File
1220 lines
33 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================================
|
||
# Linux 用户和组管理脚本 (增强版)
|
||
# 适用于: Debian/Ubuntu (amd64/arm64)
|
||
# 用法: curl -fsSL "https://pan.shumengya.top/d/scripts/user_manager.sh" | bash
|
||
# 或者: sudo bash user_manager.sh
|
||
# ============================================================================
|
||
|
||
set -euo pipefail
|
||
|
||
# ============================================================================
|
||
# 颜色定义
|
||
# ============================================================================
|
||
readonly RED='\033[0;31m'
|
||
readonly GREEN='\033[0;32m'
|
||
readonly YELLOW='\033[0;33m'
|
||
readonly BLUE='\033[0;34m'
|
||
readonly CYAN='\033[0;36m'
|
||
readonly MAGENTA='\033[0;35m'
|
||
readonly BOLD='\033[1m'
|
||
readonly NC='\033[0m' # 无颜色
|
||
|
||
# ============================================================================
|
||
# 日志函数
|
||
# ============================================================================
|
||
log_info() {
|
||
echo -e "${GREEN}[信息]${NC} $*"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[警告]${NC} $*"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[错误]${NC} $*" >&2
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[成功]${NC} $*"
|
||
}
|
||
|
||
log_title() {
|
||
echo -e "\n${CYAN}${BOLD}=== $* ===${NC}\n"
|
||
}
|
||
|
||
# ============================================================================
|
||
# 系统检测和初始化
|
||
# ============================================================================
|
||
init_system() {
|
||
# 检查root权限
|
||
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||
log_error "需要root权限运行此脚本"
|
||
log_info "请使用: sudo bash $0"
|
||
exit 1
|
||
fi
|
||
|
||
# 检测操作系统
|
||
if [ ! -f /etc/os-release ]; then
|
||
log_error "无法检测操作系统版本"
|
||
exit 1
|
||
fi
|
||
|
||
. /etc/os-release
|
||
OS_NAME="$NAME"
|
||
OS_VERSION="${VERSION_ID:-未知}"
|
||
|
||
# 检测架构
|
||
ARCH=$(uname -m)
|
||
case "$ARCH" in
|
||
x86_64|amd64)
|
||
ARCH_NAME="amd64"
|
||
;;
|
||
aarch64|arm64)
|
||
ARCH_NAME="arm64"
|
||
;;
|
||
*)
|
||
log_warn "未知架构: $ARCH"
|
||
ARCH_NAME="$ARCH"
|
||
;;
|
||
esac
|
||
|
||
# 检查是否为Debian/Ubuntu系统
|
||
if [[ ! "$OS_NAME" =~ (Debian|Ubuntu) ]]; then
|
||
log_warn "此脚本主要为Debian/Ubuntu设计,当前系统: $OS_NAME"
|
||
read -p "是否继续? (y/n): " -n 1 -r < /dev/tty
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
exit 0
|
||
fi
|
||
fi
|
||
|
||
log_info "系统: $OS_NAME $OS_VERSION"
|
||
log_info "架构: $ARCH_NAME ($ARCH)"
|
||
|
||
# 设置sudo组名
|
||
SUDO_GROUP="sudo"
|
||
}
|
||
|
||
# ============================================================================
|
||
# 工具函数
|
||
# ============================================================================
|
||
|
||
# 检查用户是否存在
|
||
user_exists() {
|
||
id "$1" &>/dev/null
|
||
}
|
||
|
||
# 检查组是否存在
|
||
group_exists() {
|
||
getent group "$1" &>/dev/null
|
||
}
|
||
|
||
# 获取用户的UID
|
||
get_user_uid() {
|
||
id -u "$1" 2>/dev/null
|
||
}
|
||
|
||
# 获取用户的主组
|
||
get_user_primary_group() {
|
||
id -gn "$1" 2>/dev/null
|
||
}
|
||
|
||
# 获取用户的所有附加组
|
||
get_user_groups() {
|
||
id -Gn "$1" 2>/dev/null | tr ' ' ','
|
||
}
|
||
|
||
# 检查用户是否在某个组中
|
||
user_in_group() {
|
||
local username="$1"
|
||
local groupname="$2"
|
||
id -nG "$username" 2>/dev/null | grep -qw "$groupname"
|
||
}
|
||
|
||
# 验证用户名格式
|
||
validate_username() {
|
||
local username="$1"
|
||
if [[ ! "$username" =~ ^[a-z_][a-z0-9_-]*[$]?$ ]]; then
|
||
log_error "用户名格式无效: $username"
|
||
log_info "用户名必须以小写字母或下划线开头,只能包含小写字母、数字、下划线和连字符"
|
||
return 1
|
||
fi
|
||
if [ ${#username} -gt 32 ]; then
|
||
log_error "用户名过长 (最大32字符): $username"
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# 验证组名格式
|
||
validate_groupname() {
|
||
local groupname="$1"
|
||
if [[ ! "$groupname" =~ ^[a-z_][a-z0-9_-]*[$]?$ ]]; then
|
||
log_error "组名格式无效: $groupname"
|
||
log_info "组名必须以小写字母或下划线开头,只能包含小写字母、数字、下划线和连字符"
|
||
return 1
|
||
fi
|
||
if [ ${#groupname} -gt 32 ]; then
|
||
log_error "组名过长 (最大32字符): $groupname"
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# 安全读取密码
|
||
read_password() {
|
||
local password
|
||
local password_confirm
|
||
|
||
while true; do
|
||
read -s -p "请输入密码: " password < /dev/tty
|
||
echo >&2
|
||
|
||
if [ ${#password} -lt 6 ]; then
|
||
log_error "密码长度至少6个字符"
|
||
continue
|
||
fi
|
||
|
||
read -s -p "请再次输入密码: " password_confirm < /dev/tty
|
||
echo >&2
|
||
|
||
if [ "$password" != "$password_confirm" ]; then
|
||
log_error "两次输入的密码不一致"
|
||
continue
|
||
fi
|
||
|
||
echo "$password"
|
||
return 0
|
||
done
|
||
}
|
||
|
||
# 确认操作
|
||
confirm_action() {
|
||
local prompt="$1"
|
||
local response
|
||
read -p "${prompt} (y/n): " -n 1 -r response < /dev/tty
|
||
echo
|
||
[[ $response =~ ^[Yy]$ ]]
|
||
}
|
||
|
||
# 短暂延迟以便用户查看结果(用于添加、删除、修改等操作)
|
||
show_result() {
|
||
sleep 1.5
|
||
}
|
||
|
||
# 等待用户按回车继续(用于查看、列出等操作)
|
||
pause_for_view() {
|
||
echo
|
||
read -p "按回车键返回菜单..." -r < /dev/tty
|
||
}
|
||
|
||
# ============================================================================
|
||
# 用户管理功能
|
||
# ============================================================================
|
||
|
||
# 添加用户
|
||
add_user() {
|
||
log_title "添加新用户"
|
||
|
||
# 输入用户名
|
||
local username
|
||
while true; do
|
||
read -p "请输入用户名: " username < /dev/tty
|
||
username=$(echo "$username" | xargs) # 去除首尾空格
|
||
|
||
if [ -z "$username" ]; then
|
||
log_error "用户名不能为空"
|
||
continue
|
||
fi
|
||
|
||
if ! validate_username "$username"; then
|
||
continue
|
||
fi
|
||
|
||
if user_exists "$username"; then
|
||
log_error "用户 '$username' 已存在"
|
||
continue
|
||
fi
|
||
|
||
break
|
||
done
|
||
|
||
# 输入主目录
|
||
local homedir
|
||
read -p "主目录 (默认: /home/$username): " homedir < /dev/tty
|
||
homedir=${homedir:-/home/$username}
|
||
|
||
# 输入Shell
|
||
local usershell
|
||
echo "可用的Shell:"
|
||
cat /etc/shells | grep -v "^#" | nl
|
||
read -p "Shell (默认: /bin/bash): " usershell < /dev/tty
|
||
usershell=${usershell:-/bin/bash}
|
||
|
||
# 验证Shell是否存在
|
||
if [ ! -f "$usershell" ]; then
|
||
log_warn "Shell '$usershell' 不存在,使用默认 /bin/bash"
|
||
usershell="/bin/bash"
|
||
fi
|
||
|
||
# 输入密码
|
||
log_info "设置用户密码 (最少6个字符)"
|
||
local password
|
||
password=$(read_password)
|
||
|
||
# 创建用户
|
||
log_info "正在创建用户 '$username'..."
|
||
if useradd -m -d "$homedir" -s "$usershell" "$username" 2>/dev/null; then
|
||
# 设置密码
|
||
echo "$username:$password" | chpasswd
|
||
|
||
log_success "用户 '$username' 创建成功"
|
||
log_info "UID: $(get_user_uid "$username")"
|
||
log_info "主目录: $homedir"
|
||
log_info "Shell: $usershell"
|
||
|
||
# 询问是否添加sudo权限
|
||
if confirm_action "是否授予sudo权限?"; then
|
||
add_user_to_sudo "$username"
|
||
fi
|
||
|
||
# 询问是否添加到其他组
|
||
if confirm_action "是否添加到其他组?"; then
|
||
add_user_to_groups_interactive "$username"
|
||
fi
|
||
else
|
||
log_error "创建用户失败"
|
||
return 1
|
||
fi
|
||
|
||
show_result
|
||
}
|
||
|
||
# 删除用户
|
||
delete_user() {
|
||
log_title "删除用户"
|
||
|
||
# 列出普通用户
|
||
list_normal_users_simple
|
||
|
||
local username
|
||
read -p "请输入要删除的用户名: " username < /dev/tty
|
||
username=$(echo "$username" | xargs)
|
||
|
||
if [ -z "$username" ]; then
|
||
log_error "用户名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! user_exists "$username"; then
|
||
log_error "用户 '$username' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
# 获取用户信息
|
||
local uid=$(get_user_uid "$username")
|
||
local homedir=$(getent passwd "$username" | cut -d: -f6)
|
||
|
||
log_warn "即将删除用户: $username (UID: $uid)"
|
||
log_warn "主目录: $homedir"
|
||
|
||
if ! confirm_action "确认删除此用户?"; then
|
||
log_info "已取消删除操作"
|
||
return 0
|
||
fi
|
||
|
||
# 询问是否删除主目录
|
||
local delete_home=""
|
||
if confirm_action "是否同时删除主目录和邮件?"; then
|
||
delete_home="-r"
|
||
fi
|
||
|
||
# 执行删除
|
||
if userdel $delete_home "$username" 2>/dev/null; then
|
||
log_success "用户 '$username' 已删除"
|
||
else
|
||
log_error "删除用户失败"
|
||
fi
|
||
|
||
show_result
|
||
}
|
||
|
||
# 修改用户信息
|
||
modify_user() {
|
||
log_title "修改用户信息"
|
||
|
||
# 列出普通用户
|
||
list_normal_users_simple
|
||
|
||
local username
|
||
read -p "请输入要修改的用户名: " username < /dev/tty
|
||
username=$(echo "$username" | xargs)
|
||
|
||
if [ -z "$username" ]; then
|
||
log_error "用户名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! user_exists "$username"; then
|
||
log_error "用户 '$username' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
# 显示修改菜单
|
||
while true; do
|
||
echo
|
||
log_info "当前用户: $username"
|
||
log_info "UID: $(get_user_uid "$username")"
|
||
log_info "主组: $(get_user_primary_group "$username")"
|
||
log_info "所有组: $(get_user_groups "$username")"
|
||
log_info "主目录: $(getent passwd "$username" | cut -d: -f6)"
|
||
log_info "Shell: $(getent passwd "$username" | cut -d: -f7)"
|
||
echo
|
||
echo "请选择要修改的项目:"
|
||
echo " 1) 修改主目录"
|
||
echo " 2) 修改Shell"
|
||
echo " 3) 修改主组"
|
||
echo " 4) 修改用户名"
|
||
echo " 5) 锁定/解锁用户"
|
||
echo " 0) 返回上级菜单"
|
||
echo
|
||
|
||
local choice
|
||
read -p "请选择 [0-5]: " choice < /dev/tty
|
||
|
||
case $choice in
|
||
1)
|
||
modify_user_home "$username"
|
||
;;
|
||
2)
|
||
modify_user_shell "$username"
|
||
;;
|
||
3)
|
||
modify_user_primary_group "$username"
|
||
;;
|
||
4)
|
||
modify_username "$username"
|
||
return 0
|
||
;;
|
||
5)
|
||
toggle_user_lock "$username"
|
||
;;
|
||
0)
|
||
return 0
|
||
;;
|
||
*)
|
||
log_error "无效的选择"
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# 修改用户主目录
|
||
modify_user_home() {
|
||
local username="$1"
|
||
local current_home=$(getent passwd "$username" | cut -d: -f6)
|
||
|
||
log_info "当前主目录: $current_home"
|
||
local new_home
|
||
read -p "新主目录: " new_home < /dev/tty
|
||
|
||
if [ -z "$new_home" ]; then
|
||
log_error "主目录不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if usermod -d "$new_home" "$username" 2>/dev/null; then
|
||
log_success "主目录已修改为: $new_home"
|
||
|
||
if confirm_action "是否移动现有文件到新目录?"; then
|
||
if [ -d "$current_home" ]; then
|
||
usermod -m -d "$new_home" "$username" 2>/dev/null
|
||
log_success "文件已移动"
|
||
fi
|
||
fi
|
||
else
|
||
log_error "修改主目录失败"
|
||
fi
|
||
}
|
||
|
||
# 修改用户Shell
|
||
modify_user_shell() {
|
||
local username="$1"
|
||
local current_shell=$(getent passwd "$username" | cut -d: -f7)
|
||
|
||
log_info "当前Shell: $current_shell"
|
||
echo "可用的Shell:"
|
||
cat /etc/shells | grep -v "^#" | nl
|
||
|
||
local new_shell
|
||
read -p "新Shell: " new_shell < /dev/tty
|
||
|
||
if [ -z "$new_shell" ]; then
|
||
log_error "Shell不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if [ ! -f "$new_shell" ]; then
|
||
log_error "Shell '$new_shell' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
if usermod -s "$new_shell" "$username" 2>/dev/null; then
|
||
log_success "Shell已修改为: $new_shell"
|
||
else
|
||
log_error "修改Shell失败"
|
||
fi
|
||
}
|
||
|
||
# 修改用户主组
|
||
modify_user_primary_group() {
|
||
local username="$1"
|
||
local current_group=$(get_user_primary_group "$username")
|
||
|
||
log_info "当前主组: $current_group"
|
||
|
||
# 列出所有组
|
||
echo "系统中的组:"
|
||
getent group | cut -d: -f1 | column
|
||
|
||
local new_group
|
||
read -p "新主组: " new_group < /dev/tty
|
||
|
||
if [ -z "$new_group" ]; then
|
||
log_error "组名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! group_exists "$new_group"; then
|
||
log_error "组 '$new_group' 不存在"
|
||
if confirm_action "是否创建此组?"; then
|
||
if groupadd "$new_group" 2>/dev/null; then
|
||
log_success "组 '$new_group' 已创建"
|
||
else
|
||
log_error "创建组失败"
|
||
return 1
|
||
fi
|
||
else
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
if usermod -g "$new_group" "$username" 2>/dev/null; then
|
||
log_success "主组已修改为: $new_group"
|
||
else
|
||
log_error "修改主组失败"
|
||
fi
|
||
}
|
||
|
||
# 修改用户名
|
||
modify_username() {
|
||
local old_username="$1"
|
||
|
||
log_warn "修改用户名是一个敏感操作"
|
||
|
||
local new_username
|
||
read -p "新用户名: " new_username < /dev/tty
|
||
new_username=$(echo "$new_username" | xargs)
|
||
|
||
if [ -z "$new_username" ]; then
|
||
log_error "用户名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! validate_username "$new_username"; then
|
||
return 1
|
||
fi
|
||
|
||
if user_exists "$new_username"; then
|
||
log_error "用户 '$new_username' 已存在"
|
||
return 1
|
||
fi
|
||
|
||
if ! confirm_action "确认将 '$old_username' 改名为 '$new_username'?"; then
|
||
return 0
|
||
fi
|
||
|
||
if usermod -l "$new_username" "$old_username" 2>/dev/null; then
|
||
log_success "用户名已修改为: $new_username"
|
||
|
||
# 询问是否同时修改主目录名
|
||
if confirm_action "是否同时修改主目录名?"; then
|
||
local old_home=$(getent passwd "$new_username" | cut -d: -f6)
|
||
local new_home=$(dirname "$old_home")/"$new_username"
|
||
|
||
if usermod -d "$new_home" -m "$new_username" 2>/dev/null; then
|
||
log_success "主目录已修改为: $new_home"
|
||
fi
|
||
fi
|
||
else
|
||
log_error "修改用户名失败"
|
||
fi
|
||
}
|
||
|
||
# 锁定/解锁用户
|
||
toggle_user_lock() {
|
||
local username="$1"
|
||
|
||
# 检查用户是否被锁定
|
||
if passwd -S "$username" 2>/dev/null | grep -q " L "; then
|
||
log_info "用户 '$username' 当前状态: 已锁定"
|
||
if confirm_action "是否解锁此用户?"; then
|
||
if passwd -u "$username" 2>/dev/null; then
|
||
log_success "用户已解锁"
|
||
else
|
||
log_error "解锁失败"
|
||
fi
|
||
fi
|
||
else
|
||
log_info "用户 '$username' 当前状态: 未锁定"
|
||
if confirm_action "是否锁定此用户?"; then
|
||
if passwd -l "$username" 2>/dev/null; then
|
||
log_success "用户已锁定"
|
||
else
|
||
log_error "锁定失败"
|
||
fi
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 修改用户密码
|
||
change_user_password() {
|
||
log_title "修改用户密码"
|
||
|
||
list_normal_users_simple
|
||
|
||
local username
|
||
read -p "请输入用户名: " username < /dev/tty
|
||
username=$(echo "$username" | xargs)
|
||
|
||
if [ -z "$username" ]; then
|
||
log_error "用户名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! user_exists "$username"; then
|
||
log_error "用户 '$username' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
log_info "为用户 '$username' 设置新密码"
|
||
local password
|
||
password=$(read_password)
|
||
|
||
if echo "$username:$password" | chpasswd 2>/dev/null; then
|
||
log_success "密码修改成功"
|
||
else
|
||
log_error "密码修改失败"
|
||
fi
|
||
|
||
show_result
|
||
}
|
||
|
||
# 添加用户到sudo组
|
||
add_user_to_sudo() {
|
||
local username="$1"
|
||
|
||
if ! group_exists "$SUDO_GROUP"; then
|
||
log_warn "sudo组 ($SUDO_GROUP) 不存在,正在创建..."
|
||
if groupadd "$SUDO_GROUP" 2>/dev/null; then
|
||
log_success "sudo组已创建"
|
||
else
|
||
log_error "创建sudo组失败"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
if user_in_group "$username" "$SUDO_GROUP"; then
|
||
log_info "用户 '$username' 已经拥有sudo权限"
|
||
return 0
|
||
fi
|
||
|
||
if usermod -aG "$SUDO_GROUP" "$username" 2>/dev/null; then
|
||
log_success "已授予用户 '$username' sudo权限"
|
||
else
|
||
log_error "授予sudo权限失败"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 交互式添加用户到多个组
|
||
add_user_to_groups_interactive() {
|
||
local username="$1"
|
||
|
||
echo "系统中的组:"
|
||
getent group | cut -d: -f1 | column
|
||
|
||
local groups
|
||
read -p "请输入要添加的组 (用逗号或空格分隔): " groups < /dev/tty
|
||
|
||
if [ -z "$groups" ]; then
|
||
return 0
|
||
fi
|
||
|
||
# 将逗号替换为空格
|
||
groups=$(echo "$groups" | tr ',' ' ')
|
||
|
||
for group in $groups; do
|
||
group=$(echo "$group" | xargs)
|
||
if [ -z "$group" ]; then
|
||
continue
|
||
fi
|
||
|
||
if ! group_exists "$group"; then
|
||
log_warn "组 '$group' 不存在"
|
||
if confirm_action "是否创建组 '$group'?"; then
|
||
if groupadd "$group" 2>/dev/null; then
|
||
log_success "组 '$group' 已创建"
|
||
else
|
||
log_error "创建组 '$group' 失败"
|
||
continue
|
||
fi
|
||
else
|
||
continue
|
||
fi
|
||
fi
|
||
|
||
if user_in_group "$username" "$group"; then
|
||
log_info "用户已在组 '$group' 中"
|
||
else
|
||
if usermod -aG "$group" "$username" 2>/dev/null; then
|
||
log_success "已将用户添加到组 '$group'"
|
||
else
|
||
log_error "添加到组 '$group' 失败"
|
||
fi
|
||
fi
|
||
done
|
||
}
|
||
|
||
# ============================================================================
|
||
# 组管理功能
|
||
# ============================================================================
|
||
|
||
# 管理用户组成员
|
||
manage_user_groups() {
|
||
log_title "管理用户组成员"
|
||
|
||
list_normal_users_simple
|
||
|
||
local username
|
||
read -p "请输入用户名: " username < /dev/tty
|
||
username=$(echo "$username" | xargs)
|
||
|
||
if [ -z "$username" ]; then
|
||
log_error "用户名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! user_exists "$username"; then
|
||
log_error "用户 '$username' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
while true; do
|
||
echo
|
||
log_info "用户: $username"
|
||
log_info "主组: $(get_user_primary_group "$username")"
|
||
log_info "所有组: $(get_user_groups "$username")"
|
||
echo
|
||
echo "请选择操作:"
|
||
echo " 1) 添加到组"
|
||
echo " 2) 从组中移除"
|
||
echo " 3) 添加/移除sudo权限"
|
||
echo " 0) 返回上级菜单"
|
||
echo
|
||
|
||
local choice
|
||
read -p "请选择 [0-3]: " choice < /dev/tty
|
||
|
||
case $choice in
|
||
1)
|
||
add_user_to_groups_interactive "$username"
|
||
;;
|
||
2)
|
||
remove_user_from_group "$username"
|
||
;;
|
||
3)
|
||
toggle_sudo_permission "$username"
|
||
;;
|
||
0)
|
||
return 0
|
||
;;
|
||
*)
|
||
log_error "无效的选择"
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# 从组中移除用户
|
||
remove_user_from_group() {
|
||
local username="$1"
|
||
local current_groups=$(id -Gn "$username" | tr ' ' '\n' | grep -v "^$(get_user_primary_group "$username")$")
|
||
|
||
if [ -z "$current_groups" ]; then
|
||
log_info "用户没有附加组"
|
||
return 0
|
||
fi
|
||
|
||
echo "用户 '$username' 的附加组:"
|
||
echo "$current_groups" | nl
|
||
|
||
local groupname
|
||
read -p "请输入要移除的组名: " groupname < /dev/tty
|
||
groupname=$(echo "$groupname" | xargs)
|
||
|
||
if [ -z "$groupname" ]; then
|
||
return 0
|
||
fi
|
||
|
||
if ! user_in_group "$username" "$groupname"; then
|
||
log_error "用户不在组 '$groupname' 中"
|
||
return 1
|
||
fi
|
||
|
||
if [ "$groupname" = "$(get_user_primary_group "$username")" ]; then
|
||
log_error "不能移除主组"
|
||
return 1
|
||
fi
|
||
|
||
if gpasswd -d "$username" "$groupname" 2>/dev/null; then
|
||
log_success "已将用户从组 '$groupname' 中移除"
|
||
else
|
||
log_error "移除失败"
|
||
fi
|
||
}
|
||
|
||
# 切换sudo权限
|
||
toggle_sudo_permission() {
|
||
local username="$1"
|
||
|
||
if user_in_group "$username" "$SUDO_GROUP"; then
|
||
log_info "用户当前拥有sudo权限"
|
||
if confirm_action "是否移除sudo权限?"; then
|
||
if gpasswd -d "$username" "$SUDO_GROUP" 2>/dev/null; then
|
||
log_success "sudo权限已移除"
|
||
else
|
||
log_error "移除sudo权限失败"
|
||
fi
|
||
fi
|
||
else
|
||
log_info "用户当前没有sudo权限"
|
||
if confirm_action "是否授予sudo权限?"; then
|
||
add_user_to_sudo "$username"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 添加组
|
||
add_group() {
|
||
log_title "添加新组"
|
||
|
||
local groupname
|
||
while true; do
|
||
read -p "请输入组名: " groupname < /dev/tty
|
||
groupname=$(echo "$groupname" | xargs)
|
||
|
||
if [ -z "$groupname" ]; then
|
||
log_error "组名不能为空"
|
||
continue
|
||
fi
|
||
|
||
if ! validate_groupname "$groupname"; then
|
||
continue
|
||
fi
|
||
|
||
if group_exists "$groupname"; then
|
||
log_error "组 '$groupname' 已存在"
|
||
continue
|
||
fi
|
||
|
||
break
|
||
done
|
||
|
||
# 询问是否指定GID
|
||
local gid_option=""
|
||
if confirm_action "是否指定GID?"; then
|
||
local gid
|
||
read -p "请输入GID: " gid < /dev/tty
|
||
if [[ "$gid" =~ ^[0-9]+$ ]]; then
|
||
gid_option="-g $gid"
|
||
else
|
||
log_warn "GID无效,将自动分配"
|
||
fi
|
||
fi
|
||
|
||
if groupadd $gid_option "$groupname" 2>/dev/null; then
|
||
local new_gid=$(getent group "$groupname" | cut -d: -f3)
|
||
log_success "组 '$groupname' 创建成功 (GID: $new_gid)"
|
||
else
|
||
log_error "创建组失败"
|
||
fi
|
||
|
||
show_result
|
||
}
|
||
|
||
# 删除组
|
||
delete_group() {
|
||
log_title "删除组"
|
||
|
||
echo "系统中的组:"
|
||
getent group | awk -F: '$3 >= 1000 {print $1 " (GID: " $3 ")"}' | column
|
||
|
||
local groupname
|
||
read -p "请输入要删除的组名: " groupname < /dev/tty
|
||
groupname=$(echo "$groupname" | xargs)
|
||
|
||
if [ -z "$groupname" ]; then
|
||
log_error "组名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! group_exists "$groupname"; then
|
||
log_error "组 '$groupname' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
# 检查是否有用户以此为主组
|
||
local users_in_group=$(getent group "$groupname" | cut -d: -f4)
|
||
local primary_users=$(awk -F: -v gid="$(getent group "$groupname" | cut -d: -f3)" '$4 == gid {print $1}' /etc/passwd)
|
||
|
||
if [ -n "$primary_users" ]; then
|
||
log_warn "以下用户以此组为主组:"
|
||
echo "$primary_users"
|
||
log_error "无法删除,请先修改这些用户的主组"
|
||
return 1
|
||
fi
|
||
|
||
if [ -n "$users_in_group" ]; then
|
||
log_warn "组中包含以下用户: $users_in_group"
|
||
fi
|
||
|
||
if ! confirm_action "确认删除组 '$groupname'?"; then
|
||
log_info "已取消删除操作"
|
||
return 0
|
||
fi
|
||
|
||
if groupdel "$groupname" 2>/dev/null; then
|
||
log_success "组 '$groupname' 已删除"
|
||
else
|
||
log_error "删除组失败"
|
||
fi
|
||
|
||
show_result
|
||
}
|
||
|
||
# ============================================================================
|
||
# 列表显示功能
|
||
# ============================================================================
|
||
|
||
# 简单列出普通用户
|
||
list_normal_users_simple() {
|
||
echo -e "${BLUE}系统普通用户:${NC}"
|
||
getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {printf " %s (UID: %s)\n", $1, $3}'
|
||
echo
|
||
}
|
||
|
||
# 列出所有用户
|
||
list_all_users() {
|
||
log_title "用户列表"
|
||
|
||
echo -e "${CYAN}${BOLD}普通用户 (UID >= 1000):${NC}"
|
||
printf "%-20s %-8s %-15s %-20s %-30s\n" "用户名" "UID" "主组" "Shell" "主目录"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $0}' | while IFS=: read -r username _ uid gid _ homedir shell; do
|
||
local primary_group=$(getent group "$gid" | cut -d: -f1)
|
||
printf "%-20s %-8s %-15s %-20s %-30s\n" "$username" "$uid" "$primary_group" "$shell" "$homedir"
|
||
done
|
||
|
||
echo
|
||
local user_count=$(getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {count++} END {print count}')
|
||
log_info "普通用户总数: $user_count"
|
||
|
||
pause_for_view
|
||
}
|
||
|
||
# 列出所有用户详细信息
|
||
list_all_users_detailed() {
|
||
log_title "所有用户详细信息"
|
||
|
||
getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' | while read -r username; do
|
||
show_user_details "$username"
|
||
echo
|
||
done
|
||
}
|
||
|
||
# 查看用户详细信息
|
||
view_user_info() {
|
||
log_title "查看用户详细信息"
|
||
|
||
local username
|
||
read -p "请输入用户名 (留空查看所有用户): " username < /dev/tty
|
||
username=$(echo "$username" | xargs)
|
||
|
||
if [ -z "$username" ]; then
|
||
list_all_users_detailed
|
||
else
|
||
if ! user_exists "$username"; then
|
||
log_error "用户 '$username' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
show_user_details "$username"
|
||
fi
|
||
|
||
pause_for_view
|
||
}
|
||
|
||
# 显示单个用户的详细信息
|
||
show_user_details() {
|
||
local username="$1"
|
||
|
||
echo
|
||
echo -e "${CYAN}${BOLD}用户详细信息: $username${NC}"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
local uid=$(get_user_uid "$username")
|
||
local gid=$(id -g "$username")
|
||
local primary_group=$(get_user_primary_group "$username")
|
||
local all_groups=$(get_user_groups "$username")
|
||
local homedir=$(getent passwd "$username" | cut -d: -f6)
|
||
local shell=$(getent passwd "$username" | cut -d: -f7)
|
||
local gecos=$(getent passwd "$username" | cut -d: -f5)
|
||
|
||
echo -e "${GREEN}UID:${NC} $uid"
|
||
echo -e "${GREEN}GID:${NC} $gid"
|
||
echo -e "${GREEN}主组:${NC} $primary_group"
|
||
echo -e "${GREEN}所有组:${NC} $all_groups"
|
||
echo -e "${GREEN}主目录:${NC} $homedir"
|
||
echo -e "${GREEN}Shell:${NC} $shell"
|
||
[ -n "$gecos" ] && echo -e "${GREEN}备注:${NC} $gecos"
|
||
|
||
# 检查主目录是否存在
|
||
if [ -d "$homedir" ]; then
|
||
local home_size=$(du -sh "$homedir" 2>/dev/null | cut -f1)
|
||
echo -e "${GREEN}主目录大小:${NC} $home_size"
|
||
else
|
||
echo -e "${YELLOW}主目录不存在${NC}"
|
||
fi
|
||
|
||
# 检查用户状态
|
||
if passwd -S "$username" 2>/dev/null | grep -q " L "; then
|
||
echo -e "${GREEN}账户状态:${NC} ${RED}已锁定${NC}"
|
||
else
|
||
echo -e "${GREEN}账户状态:${NC} ${GREEN}正常${NC}"
|
||
fi
|
||
|
||
# 显示最后登录时间
|
||
local last_login=$(lastlog -u "$username" 2>/dev/null | tail -n 1)
|
||
if [ -n "$last_login" ]; then
|
||
echo -e "${GREEN}最后登录:${NC} $last_login"
|
||
fi
|
||
|
||
# 检查sudo权限
|
||
if user_in_group "$username" "$SUDO_GROUP"; then
|
||
echo -e "${GREEN}Sudo权限:${NC} ${GREEN}是${NC}"
|
||
else
|
||
echo -e "${GREEN}Sudo权限:${NC} 否"
|
||
fi
|
||
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
}
|
||
|
||
# 列出所有组
|
||
list_all_groups() {
|
||
log_title "组列表"
|
||
|
||
echo -e "${CYAN}${BOLD}系统组 (GID >= 1000):${NC}"
|
||
printf "%-25s %-8s %-50s\n" "组名" "GID" "成员"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
getent group | awk -F: '$3 >= 1000 {print $0}' | while IFS=: read -r groupname _ gid members; do
|
||
# 查找以此组为主组的用户
|
||
local primary_users=$(awk -F: -v gid="$gid" '$4 == gid {printf "%s,", $1}' /etc/passwd | sed 's/,$//')
|
||
|
||
# 合并主组用户和附加组成员
|
||
local all_members="$members"
|
||
if [ -n "$primary_users" ]; then
|
||
if [ -n "$all_members" ]; then
|
||
all_members="$primary_users,$all_members"
|
||
else
|
||
all_members="$primary_users"
|
||
fi
|
||
fi
|
||
|
||
printf "%-25s %-8s %-50s\n" "$groupname" "$gid" "$all_members"
|
||
done
|
||
|
||
echo
|
||
local group_count=$(getent group | awk -F: '$3 >= 1000 {count++} END {print count}')
|
||
log_info "组总数: $group_count"
|
||
|
||
pause_for_view
|
||
}
|
||
|
||
# 查看组详细信息
|
||
view_group_info() {
|
||
log_title "查看组详细信息"
|
||
|
||
local groupname
|
||
read -p "请输入组名: " groupname < /dev/tty
|
||
groupname=$(echo "$groupname" | xargs)
|
||
|
||
if [ -z "$groupname" ]; then
|
||
log_error "组名不能为空"
|
||
return 1
|
||
fi
|
||
|
||
if ! group_exists "$groupname"; then
|
||
log_error "组 '$groupname' 不存在"
|
||
return 1
|
||
fi
|
||
|
||
show_group_details "$groupname"
|
||
pause_for_view
|
||
}
|
||
|
||
# 显示单个组的详细信息
|
||
show_group_details() {
|
||
local groupname="$1"
|
||
|
||
echo
|
||
echo -e "${CYAN}${BOLD}组详细信息: $groupname${NC}"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
local gid=$(getent group "$groupname" | cut -d: -f3)
|
||
local members=$(getent group "$groupname" | cut -d: -f4)
|
||
|
||
echo -e "${GREEN}GID:${NC} $gid"
|
||
|
||
# 查找以此组为主组的用户
|
||
local primary_users=$(awk -F: -v gid="$gid" '$4 == gid {print $1}' /etc/passwd | tr '\n' ',' | sed 's/,$//')
|
||
|
||
if [ -n "$primary_users" ]; then
|
||
echo -e "${GREEN}主组用户:${NC} $primary_users"
|
||
else
|
||
echo -e "${GREEN}主组用户:${NC} 无"
|
||
fi
|
||
|
||
if [ -n "$members" ]; then
|
||
echo -e "${GREEN}附加成员:${NC} $members"
|
||
else
|
||
echo -e "${GREEN}附加成员:${NC} 无"
|
||
fi
|
||
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
}
|
||
|
||
# ============================================================================
|
||
# 菜单系统
|
||
# ============================================================================
|
||
|
||
# 显示主菜单
|
||
show_main_menu() {
|
||
clear
|
||
echo -e "${CYAN}${BOLD}"
|
||
echo "╔════════════════════════════════════════════════════════════╗"
|
||
echo "║ Linux 用户和组管理工具 (增强版) ║"
|
||
echo "╚════════════════════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
echo -e "${BLUE}系统信息:${NC} $OS_NAME $OS_VERSION ($ARCH_NAME)"
|
||
echo
|
||
echo -e "${YELLOW}${BOLD}用户管理:${NC}"
|
||
echo " 1) 添加用户"
|
||
echo " 2) 删除用户"
|
||
echo " 3) 修改用户信息"
|
||
echo " 4) 修改用户密码"
|
||
echo " 5) 查看用户详细信息"
|
||
echo " 6) 列出所有用户"
|
||
echo
|
||
echo -e "${YELLOW}${BOLD}组管理:${NC}"
|
||
echo " 7) 添加组"
|
||
echo " 8) 删除组"
|
||
echo " 9) 列出所有组"
|
||
echo " 10) 查看组详细信息"
|
||
echo
|
||
echo -e "${YELLOW}${BOLD}用户组关系:${NC}"
|
||
echo " 11) 管理用户组成员"
|
||
echo
|
||
echo -e "${YELLOW}${BOLD}其他:${NC}"
|
||
echo " 0) 退出"
|
||
echo
|
||
}
|
||
|
||
# 主菜单循环
|
||
main_menu() {
|
||
while true; do
|
||
show_main_menu
|
||
|
||
local choice
|
||
read -p "请选择 [0-11]: " choice < /dev/tty
|
||
|
||
case $choice in
|
||
1)
|
||
add_user
|
||
;;
|
||
2)
|
||
delete_user
|
||
;;
|
||
3)
|
||
modify_user
|
||
;;
|
||
4)
|
||
change_user_password
|
||
;;
|
||
5)
|
||
view_user_info
|
||
;;
|
||
6)
|
||
list_all_users
|
||
;;
|
||
7)
|
||
add_group
|
||
;;
|
||
8)
|
||
delete_group
|
||
;;
|
||
9)
|
||
list_all_groups
|
||
;;
|
||
10)
|
||
view_group_info
|
||
;;
|
||
11)
|
||
manage_user_groups
|
||
;;
|
||
0)
|
||
echo
|
||
log_success "感谢使用,再见!"
|
||
exit 0
|
||
;;
|
||
*)
|
||
log_error "无效的选择,请重新输入"
|
||
sleep 1
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# ============================================================================
|
||
# 主函数
|
||
# ============================================================================
|
||
|
||
main() {
|
||
# 初始化系统
|
||
init_system
|
||
|
||
# 显示欢迎信息
|
||
echo
|
||
log_success "系统初始化完成"
|
||
sleep 1
|
||
|
||
# 进入主菜单
|
||
main_menu
|
||
}
|
||
|
||
# 执行主函数
|
||
main "$@"
|