程序猿防 rm 删库的自我修养

“他走了进来,俯下身子直到吻住她/从她那里,他得到力量。/(女人望着他,全无回应。)/一面氲湿的镜子/模糊地仿效人生。/他收紧领带,/收紧心脏,/喝了一小口散淡浑浊的咖啡,/开始讲解/今天的计划,/昨日的梦想和无法实现的/渴望。/(她安静地注视着他。)/他又开口。回忆起多少日子的/争斗和过去的/爱情。人生真是想不到,/他说。(比词语脆弱得多。)/终于他跟随她的沉默闭了嘴,/贴近她的唇/就这样哭起来,那双唇/已永远不会有回应。” —— 何塞·安赫尔·巴伦特 / 汪天艾

最近看到一则新闻,说的是负责郑大医院 HIS 系统运维的工程师因为违规操作致使甲方业务系统瘫痪进,合计损失 800万,而被判刑 5年半。加之之前腾讯云微盟“删库跑路”事故,以及更早之前的一些新闻,不禁觉得这也是一种高危工作啊😱。回过头来看郑大医院事件,我觉得运维工程师有一定的背锅成分,刨去整个甲方测算的是估算值不说,而承包方中的项目主管等难道没有什么责任吗?

回到今天要说的问题,删库从来就是一个危险的举动,轻则业务停摆耗时费力,重则锒铛入狱商业信誉受损。恰好前一段时间也是有童鞋误操作执行命令,导致服务器异常。所以有必要针对 rm -rf 命令进行一些防误操作机制。当然也是着重强调的是,删了重要的文件后,不要重启系统!
真·删库

脚本开启验证

1
2
3
set -e #若指令传回值不等于0,则立即退出shell。这时候可以使用 failOp||nextOp 操作执行下一步命令。
set -u #当执行时使用到未定义过的变量,则显示错误信息。
set -x #执行指令后,会先显示该指令及所下的参数。

这样做的目的是能快速发现异常,保证当一些变量比如路径为空时,停止继续执行诸如删除之类的操作。

轮子

safe-rm

替换系统自带的 rm,目前版本停留在 0.12:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
wget -c https://launchpad.net/safe-rm/trunk/0.12/+download/safe-rm-0.12.tar.gz
tar -xzvf safe-rm-0.12.tar.gz

# 替换系统 rm
cp safe-rm-0.12/safe-rm /usr/local/bin/rm
chown root:root /usr/local/bin/rm

#添加配置
vim /etc/profile
#把safe-rm的路径配置在其他的前面
PATH=/usr/local/bin:$PATH

#配置路径黑名单,如添加 /root/test 目录
vim /etc/safe-rm.conf
/root/test

mkdir /root/test
#删除不成功
rm -rf /root/test

附常见的需要保护的文件/目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/
/bin
/boot
/dev
/etc
/home
/initrd
/lib
/proc
/root
/sbin
/sys
/usr
/usr/bin
/usr/include
/usr/lib
/usr/local
/usr/local/bin
/usr/local/include
/usr/local/sbin
/usr/local/share
/usr/sbin
/usr/share
/usr/src
/var
/etc/safe-rm.conf

trash-cli

一个基于命令行的回收站工具,具体使用可以参考 GitHub

1
2
3
4
5
6
7
8
9
10
11
12
# 安装,使用 easy_install 或者 pip
easy_install trash-cli

# 用法
trash-put foo.txt foo
trash-list
trash-restore

trash-empty
trash-empty <days>

trash-rm \*.o

其他

alias rm

有不少人刚从 Windows 转向 Linux 时,特别是在命令行界面,都会感叹有一个回收站多好,防止手哆嗦有奇效。这其中桌面版的 Linux 中已经内置回收站功能,一般位于 $HOME/.local/share/Trash。所以基于此,我们延伸出一种思路,创建回收站目录,然后利用脚本代替 rm 将要删除的文件(夹)移动到目录中即可,当然也就可以在脚本中利用定时清空文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 创建回收站
sudo mkdir -p /.Trash
sudo chmod a+rw /.Trash
sudo chmod +t /.Trash

# 编辑用户变量,添加 alias
vim ~/.bashrc

alias rm=moveToRecycle
alias rcll='ls -l /.Trash'
alias unrm=restoreFile
alias rcempty=emptyRecycle

# 撤销删除文件
restoreFile()
{
if [ $# -eq 0 ];
then
echo "input at least one path"
exit
fi

mv -i /.Trash/$@ ./
}

# 移动文件到回收站
moveToRecycle()
{
if [ $# -eq 0 ];
then
echo "input at least one path"
exit
fi

mv $@ /.Trash/
}

# 清空回收站
emptyRecycle()
{
read -p "Confirm to empty recycle?[n]" confirm
[ $confirm == 'y' ] || [ $confirm == 'Y' ] && /usr/bin/rm -rf /.Trash/*
}

source ~/.bashrc

OK,经过以上操作,我们可以愉快的使用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#删除一个文件夹,cfz下面的文件均被移到回收站中
rm cfz

#删除一个文件
rm cfz.txt

#恢复 abc.txt
unrm cfz.txt

#恢复 cfz 件夹
unrm cfz

#列出回收站
rcll

#清空回收站
rcempty

bonus ——定时删除功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
crontab -e

# 这里注意调用的时系统自带的 rm
# 每日零点自动清理超过 30 天删除的文件
0 0 * * * /bin/find ~/.Trash -ctime 30 -name "*" -exec /bin/rm -rf {} \;

# 也可以写一个脚本放入 crontab
# 自动清理30天删除的文件

#!/bin/bash
find ~/.Trash -ctime 30 -type f -name "*" -exec /bin/rm {} \;
# 自动清理30天删除的目录
find ~/.Trash -ctime 30 -type d -name "*" -exec /bin/rm {} \;

只读、粘滞位、chattr

根文件挂载成只读

1
2
3
#remount,ro,就表示只读的方式挂载
cat /etc/fstab
/dev/root / ext4 remount,ro,noatime 0 1
粘滞位(sticky bit)、chattr

可以理解为防删除位,主要是为公共目录(例如权限为 777 的)设置,权限字符为 t,粘滞位的权限数字为 1,作用是多用户具有读写权限,但是不能删除该目录中其他用户的文件,只能删自己创建的文件

1
2
3
4
5
6
7
8
9
10
# 粘滞位针对目录
chmod o+t /tmp

#禁止文件修改
chattr +i /etc/nginx.conf

# 解除:chattr -i xxx

#日志只能追加
chattr +a /var/log/nginx.log
-------------本文结束感谢您的阅读-------------