章节目录:
管理本地用户账户绝不仅仅是添加、修改和删除,还需考虑安全问题、保留工作的需求,以及精确删除账户。
一、基本功能拆解
- 删除本地账户属于更复杂的账户管理任务,至少需要 4 个步骤:
- 获取正确的待删除用户账户名。
- “杀死”系统中正在运行的属于该账户的进程。
- 确认系统中属于该账户的所有文件。
- 删除该用户账户。
1.1 获取正确的账户名
可以使用
read
命令获取账户名称。
- 在
read
命令中加入-t 选项,在超时退出之前给用户 60 秒的时间回答问题:
echo "Please enter the username of the user "
echo -e "account you wish to delete from system: \c"
read -t 60 answer
- 最好再给脚本用户 3 次机会来回答问题:
while [ -z "$answer" ]; do
echo "Please enter the username of the user "
echo -e "account you wish to delete from system: \c"
read -t 60 answer
# 记录当前不作答次数。
ask_count=$((ask_count + 1))
case $ask_count in
2)
echo
echo "Please answer the question."
echo
;;
3)
echo
echo "One last try...please answer the question."
echo
;;
4)
echo
echo "Since you refuse to answer the question..."
echo "exiting program."
echo
#
exit
;;
esac
done
1.2 通过函数获取正确的账户名
我们可以将上面这段代码加入对脚本用户的提问,并放到一个函数以便在脚本中重复使用。
- 创建函数:
#!/bin/bash
get_answer() {
while [ -z "$answer" ]; do
echo "Please enter the username of the user "
echo -e "account you wish to delete from system: \c"
ask_count=$((ask_count + 1))
case $ask_count in
2)
echo
echo "Please answer the question."
echo
;;
3)
echo
echo "One last try...please answer the question."
echo
;;
4)
echo
echo "Since you refuse to answer the question..."
echo "exiting program."
echo
#
exit
;;
esac
echo
# line1 和 line2 用于询问不同的问题。
if [ -n "$line2" ]; then
echo $line1
echo -e $line2" \c"
else
echo -e $line1" \c"
fi
# 超时 60 s。
read -t 60 answer
done
# 清除变量。
unset line1
unset line2
}
- 调用函数:
line1="Please enter the username of the user "
line2="account you wish to delete from system:"
get_answer
user_account=$answer
1.3 核实输入的账户名
- 考虑到可能存在输入错误,应该核实一下输入的用户账户名。这很容易,因为处理提问的代码是现成的:
line1="Is $user_account the user account "
line2="you wish to delete from the system? [y/n]"
get_answer
-
如果用户回答了“yes”,那么我们就得到了确认,脚本也能够继续执行。
-
脚本要在不同的位置多次处理脚本用户的“yes/no”回答,因此有必要创建一个函数来处理这个任务:
process_answer() {
# cut命令截取字符串(只保留第一个字符)。
answer=$(echo $answer | cut -c1)
#
case $answer in
y | Y) ;;
*)
echo
echo $exit_line1
echo $exit_line2
echo
exit
;;
esac
#
unset exit_line1
unset exit_line2
#
}
- 现在调用函数就可以处理脚本用户的回答了:
exit_line1="Because the account, $user_account, is not "
exit_line2="the one you wish to delete, we are leaving the script..."
process_answer
1.4 确认账户是否存在
- 为安全起见,最好将完整的账户记录显示给脚本用户,再次核实这就是要删除的账户:
# grep 的-w 选项会精确匹配特定的用户账户。
user_account_record=$(cat /etc/passwd | grep -w $user_account)
- 如果在 /etc/passwd 中没找到指定账户,则 $? 变量会被设置为 1:
if [ $? -eq 1 ]; then
echo
echo "Account, $user_account, not found. "
echo "Leaving the script..."
echo
exit
fi
- 就算找到了记录,仍然需要向脚本用户核实这是不是正确的账户:
echo "I found this record:"
echo $user_account_record
echo
#
line1="Is this the correct User Account? [y/n]"
get_answer
#
exit_line1="Because the account, $user_account, is not"
exit_line2="the one you wish to delete, we are leaving the script..."
process_answer
1.5 删除账户进程
- 脚本可以使用
ps
命令和 -u 选项来找出属于该账户的所有运行中的进程。记得将命令输出重定向到 /dev/null,这样脚本用户在屏幕上就看不到任何显示信息了:
ps -u $user_account >/dev/null
- 如果
ps
命令的退出状态码返回 1,那么表明系统中没有属于该用户账户的进程在运行。如果退出状态码返回 0,则表明存在相关进程:
case $? in
1) # No processes running for this User Account
#
echo "There are no processes for this account currently running."
echo
;;
0) # Processes running for this User Account.
# Ask Script User if wants us to kill the processes.
#
echo "$user_account has the following process(es) running:"
ps -u $user_account
#
line1="Would you like me to kill the process(es)? [y/n]"
get_answer
#
;;
esac
- 存在相关进程时,脚本需要询问脚本用户是否要“杀死”这些进程:
answer=$(echo $answer | cut -c1)
#
case $answer in
y | Y) # If user answers "yes",
# kill User Account processes.
[...]
;;
*) # If user answers anything but "yes", do not kill.
echo
echo "Will not kill the process(es)"
echo
#
;;
esac
- 需要以下命令完成“杀死”该用户账户的进程操作:
# 1.收集当前处于运行状态且属于该用户账户的进程的 PID。
command_1="ps -u $user_account --no-heading"
# 2.提取 PID。
gawk '{print $1}'
# 3.xargs 可以使用从标准输入 STDIN 获取的命令参数并执行指定的命令。它非常适合放在管道的末尾处。
# 实际上,xarg 命令负责“杀死”PID 所对应的进程。
# xargs(-d)指定换行为分隔符,通过完整路径的命令(sudo)和(kill)“杀死”进程。
command_3="xargs -d \\n /usr/bin/sudo /bin/kill -9"
- 整个命令管道如下所示:
$command_1 | gawk '{print $1}' | $command_3
- 下面是“杀死”用户账户所有运行进程的完整 case 语句:
# 如果回答yes。
case $answer in
y | Y)
echo
echo "Killing off process(es)... "
# 1.收集当前处于运行状态且属于该用户账户的进程的 PID。
command_1="ps -u $user_account --no-heading"
# 2.xargs 命令会将 gawk 命令生成的 PID 列作为输入,创建并执行 kill 命令,“杀死”该用户所有的运行进程。
command_3="xargs -d \\n /usr/bin/sudo /bin/kill -9"
# 3.收集并提取当前账户进程的PID,执行“杀死”进程操作。
$command_1 | gawk '{print $1}' | $command_3
echo
echo "Process(es) killed."
;;
*)
echo
echo "Will not kill process(es)."
;;
esac
1.6 查找账户文件
如果待删除账户的 UID 是 1003,而你没有删除或修改文件的所属关系,那么下一个 UID 为 1003 的账户将拥有这些文件。这种情况必然是一种安全隐患。
find
命令会使用 -user 查找整个文件系统,准确找出属于 user_account 的所有文件:
find / -user $user_account > $report_file
1.7 删除账户
删除系统中的用户账户时一定要小心谨慎。因此,不妨再询问一遍,脚本用户是否真的想删除该账户。
- 再次询问是否删除:
line1="Remove $user_account's account from system? [y/n]"
get_answer
#
exit_line1="Since you do not wish to remove the user account,"
exit_line2="$user_account at this time, exiting the script..."
process_answer
- 最后,就是脚本的主要任务了:从系统中真正地删除指定用户账户。
userdel $user_account
二、完整的脚本内容
Delete_User.sh 与脚本用户之间具有很强的交互性。因此,有一点很重要,即在该脚本的执行过程中,要备注大量的文字信息以让脚本用户了解正在发生的事情。
- 下面是完整的 Delete_User.sh 脚本:
#!/bin/bash
#################### 定义询问用户的函数 ####################
function get_answer {
# 变量初始化。
unset answer
ask_count=0
# 没有答案时继续询问。
while [ -z "$answer" ]; do
# 记录当前不作答次数。
ask_count=$((ask_count + 1))
# 根据次数给出相应提示。
case $ask_count in
2)
echo
echo "Please answer the question."
echo
;;
3)
echo
echo "One last try...please answer the question."
echo
;;
4)
echo
echo "Since you refuse to answer the question..."
echo "exiting program."
echo
# 超过三次则退出。
exit
;;
esac
#
# 打印询问内容。
if [ -n "$line2" ]; then
echo $line1
echo -e $line2" \c"
else
echo -e $line1" \c"
fi
#
# 设置 60 s 超时作答。
read -t 60 answer
done
# 重置变量。
unset line1
unset line2
#
}
#################### 定义用户确认函数 ####################
function process_answer {
# 截取字符串。(只保留第一个字符。)
answer=$(echo $answer | cut -c1)
case $answer in
y | Y)
# 如果回答的是“yes”。则什么也不做。
;;
*)
# 如果回答不是“yes”,则退出脚本。
echo
echo $exit_line1
echo $exit_line2
echo
exit
;;
esac
# 重置变量。
unset exit_line1
unset exit_line2
#
}
#################### 具体的执行逻辑: ####################
#
#################### 【第一步】:获取并确认待删除的用户账户名 ####################
echo "Step #1 - Determine User Account name to Delete "
echo
line1="Please enter the username of the user "
line2="account you wish to delete from system:"
get_answer
user_account=$answer
#
# 询问是否为希望移除的用户账户。
line1="Is $user_account the user account "
line2="you wish to delete from the system? [y/n]"
get_answer
#
# 调用确认函数。(如果用户的回答不是“yes”,则退出脚本。)
exit_line1="Because the account, $user_account, is not "
exit_line1="the one you wish to delete, we are leaving the script..."
process_answer
#
# 检查是否确实是系统上的一个账户。
user_account_record=$(cat /etc/passwd | grep -w $user_account)
# 如果没有找到帐号,退出脚本。
if [ $? -eq 1 ]; then
echo
echo "Account, $user_account, not found. "
echo "Leaving the script..."
echo
exit
fi
#
echo
echo "I found this record:"
echo $user_account_record
echo
#
line1="Is this the correct User Account? [y/n]"
get_answer
#
# 再次确认用户回答,如果不是“yes”,则退出脚本。
exit_line1="Because the account, $user_account, is not "
exit_line2="the one you wish to delete, we are leaving the script..."
process_answer
#
#################### 【第二步】:查找并“杀死”该用户账户的所有进程 ####################
echo
echo "Step #2 - Find process on system belonging to user account"
echo
# 列出正在运行的用户进程。
ps -u $user_account >/dev/null
case $? in
1)
# 退出状态码是1,表示此用户帐户没有正在运行的进程。
echo "There are no processes for this account
currently running."
echo
;;
0)
# 退出状态码是0,表示该用户帐户正在运行的进程。
# 询问脚本用户是否希望“杀死”进程。
echo "$user_account has the following process(es) running:"
ps -u $user_account
#
line1="Would you like me to kill the process(es)? [y/n]"
get_answer
# 截取用户回答。
answer=$(echo $answer | cut -c1)
case $answer in
y | Y)
# 如果回答“yes”,则准备“杀死”进程。
echo
echo "Killing off process(es)..."
# 列出用户正在运行的进程。
command_1="ps -u $user_account --no-heading"
# “杀死”进程的命令。
command_3="xargs -d \\n /usr/bin/sudo /bin/kill -9"
#
# 收集并提取当前账户进程的PID,执行“杀死”进程操作。
$command_1 | gawk '{print $1}' | $command_3
#
echo
echo "Process(es) killed."
;;
*)
# 没有回答“yes”,则不“杀死”进程。
echo
echo "Will not kill process(es)."
;;
esac
;;
esac
#
#################### 【第三步】:创建该用户账户所拥有的全部文件的报告 ####################
echo
echo "Step #3 - Find files on system belonging to user account"
echo
echo "Creating a report of all files owned by $user_account."
echo
echo "It is recommended that you backup/archive these files,"
echo "and then do one of two things:"
echo " 1) Delete the files"
echo " 2) Change the files' ownership to a current user account."
echo
echo "Please wait. This may take a while..."
# 日期及文件信息。
report_date=$(date +%y%m%d)
report_file="$user_account"_Files_"$report_date".rpt
# 查找整个文件系统,准确找出属于该用户的所有文件。
find / -user $user_account >$report_file 2>/dev/null
#
echo
echo "Report is complete."
echo "Name of report: $report_file"
echo -n "Location of report: "
pwd
echo
#
#################### 【第四步】:删除该用户账户 ####################
echo
echo "Step #4 - Remove user account"
echo
#
line1="Do you wish to remove $user_account's account from system? [y/n]"
get_answer
#
exit_line1="Since you do not wish to remove the user account,"
exit_line2="$user_account at this time, exiting the script..."
process_answer
# 删除用户账户。
userdel $user_account
echo
echo "User account, $user_account, has been removed"
echo
# 退出。
exit
#
三、运行脚本
必须以 root 用户身份登录,或者使用
sudo
命令运行这种脚本。
- 运行脚本:
# 1.赋予执行权限。
[root@VM-8-11-centos script]# chmod u+x Delete_User.sh
[root@VM-8-11-centos script]#
[root@VM-8-11-centos script]# ls -og Delete_User.sh
-rwxr--r-- 1 5988 Jul 15 07:24 Delete_User.sh
[root@VM-8-11-centos script]#
# 2.运行脚本。
[root@VM-8-11-centos script]# sudo ./Delete_User.sh
Step #1 - Determine User Account name to Delete
Please enter the username of the user
account you wish to delete from system: jan2
Is jan2 the user account
you wish to delete from the system? [y/n] yes
I found this record:
jan2:x:1009:1009::/home/tmp/:/bin/bash
Is this the correct User Account? [y/n] y
Step #2 - Find process on system belonging to user account
There are no processes for this account
currently running.
Step #3 - Find files on system belonging to user account
Creating a report of all files owned by jan2.
It is recommended that you backup/archive these files,
and then do one of two things:
1) Delete the files
2) Change the files' ownership to a current user account.
Please wait. This may take a while...
Report is complete.
Name of report: jan2_Files_230715.rpt
Location of report: /home/script
Step #4 - Remove user account
Do you wish to remove jan2's account from system? [y/n] yes
User account, jan2, has been removed
[root@VM-8-11-centos script]#
# 3.查看该用户账户所拥有的全部文件的报告。
[root@VM-8-11-centos script]# cat jan2_Files_230715.rpt
/var/spool/mail/jan2
[root@VM-8-11-centos script]#
# 4.查看该账户是否还存在。
[root@VM-8-11-centos script]# grep ^jan2 /etc/passwd
[root@VM-8-11-centos script]#
四、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。