作者 | 小林coding

责编 | 伍杏玲

出品 | CSDN博客

小林在五一节前,想到有 5 天假期,心里开始飘了。

写个简单的 Bash 脚本都不上心了,写完连检查都不检查,直接拖到实体服务器跑。

结果一跑起来,发生不对劲,怎么一个简单脚本跑了 10 秒还没结束,于是立马直接 ctrl + c 一顿操作停掉了运行中脚本。

接着,习惯性的输入了 ls,结果 what?找不到 ls 命令?

瞬间背后一凉,慌慌张张打开了脚本。

发现问题了,小林我写了个巨蠢的 Bug,间接执行了 rm -fr /* ,这不意味着我删库了?

这台是公司的授权服务器呀,被小林这么一整,公司历史的授权记录和其他重要信息不就丢了?

心里很慌的小林,跟朋友们说了这件事,朋友建议我先第一时间上报给 leader,不要把删库的事情瞒着。

于是,小林就向 leader 说了我删库事情,本以为会被痛批一顿。

结果 leader 笑着说:“没事,你先看看重要的文件还在不在。不过你这么一整,我突然想起编译服务器半年没备份,我先备份一下我的编译服务器,防止哪天也被你们删库了。

我:“????”

吃瓜的小伙伴,是不是觉得小林要删库跑路了?哈哈哈,小林没跑路,反而是恢复了回来,所以接下来说说小林是如何「从删库到恢复」的。

初探案发现场

来看看小林写的垃圾代码,是如何引发这次的删库。

既然发生了 rm -fr /* 的现象,那必然 new_lic_dir 这个变量是空的。

所以导致执行 rm -fr $new_lic_dir/* 这条语句的时候,变成了 rm -fr /* 删库语句。很好,凶器找到了。

那为什么 new_lic_dir 会是空的呢?

细心的小伙伴肯定察觉出来了,是因为给 new_lic_dir 变量赋值的时使用了反引号。

没错,就是反引号的原因。

反引号在 Linux Shell 命令行中有特殊的含义:反引号间的内容,会被 Shell 先执行。其输出被放入主命令后,主命令再被执行。

也就是说, new_lic_dir 的值是 ${lic_path}/new_license 这条命令执行的结果,问题这哪是命令啊,所以肯定返回空值给 new_lic_dir 变量。

小林写的那么温柔的代码,竟然变成了穷凶极恶的删库代码。

这下原因是找到了,反引号应该改成双引号才对。

小林你真菜呀,那么简单的赋值命令都写错。

哈哈哈,确实菜,都说了嘛,因为要五一了,小林是飘着写这份代码的。

所以习惯性开启程序员内容的第一大武功:Crtl+C 和 Crtl+V。

把第一条赋值 lic_path=`pwd` 语句,复制粘贴了,然后只改了变量名,没注意反引号要修改成双引号,所以造成了删库的悲剧。

保留案发现场

既然发生了删库的事情,千万不要重启服务器,也不要关闭 ssh 连接的会话,而是要保留案发现场,接着查查还剩什么。

还好这次是比较幸运,因为在执行脚本的时候,第一时间发现不对劲,立马掐断了还在运行的脚本,所以并非 Linux 所有文件都被删除了。

只要我掐的快,rm -fr /* 就干不死我。

虽然 ls 被删了,但所幸发现 cd 命令还能用。

只要 cd 用的好,它也能用出的 ls 效果。很简单,只需 cd + Tab 键就会自动出现指定目录下的所有文件。

有了 cd + Tab 键,我们就可以查看每个目录下的文件,于是就可以一步一步来确认哪些系统文件被删了。

通过一番的确认和对比后,发现主要被删除的有四个目录分别是:

  • /bin 、/boot 、/dev 这三个目录整个都被删除了

  • /lib 目录里的动态库部分被删除

来复习下上面这四个目录主要是存放了什么:

  • /bin 存放常用系统命令,ls、cp、rm、chmod 等常用命令都在此目录;

  • /boot 系统启动目录,保存与系统启动相关的文件,如内核文件和启动引导程序;

  • /dev 设备文件保存位置;

  • /lib 存放程序所需的动态库和静态库文件;

/boot 都被删除了,还好小林没有重启服务器,要是重启了服务器,就完犊子了,系统肯定起不来了。

cd 命令是在 /sin 目录下,/sin 还健全,所以 cd 是可以正常使用。

所幸重要的数据库信息和文件都还没删除,所以小林首要的目标是要恢复 /bin、/boot、/dev、/lib 这四个目录。

还原文件

由于 /bin 目录 和 /lib 部分动态文件被删除,常用的传递文件的方式是无法使用的,如 ftp、scp、mount 等。

小林摸索了很久,竟然发现 wget 可以使用,wget 命令是在 /usr/bin 目录,所幸 /usr/bin 还健全。

于是,用了取巧的方法,先另一台正常的服务器,把 /bin 目录放到了 Web 服务器的 Web 目录,接着通过 wget 进行下载。

有戏,看到了成功的曙光。

但是新的问题就来了,我下载过来的命令文件,是没有执行权限的。

而 chmod 命令是在 /bin 目录的,它同样也被删除了,无法使用它来给予文件权限。

还在,在网上搜到了一个伟大命令 perl,可以通过它来给予文件权限:

perl -e "chmod 777, 'ls'"

真是个神奇的命令。

好了,这下赋值权限问题也解决了,成功在望了。

wget 是无法直接把 /bin 目录下载下来的,只能下载一个文件。

但是小林我不可能一个一个去下载来进行恢复,这得要何年何月才能完成。

小林就想到了一个方法:

  • 先通过 wget 的方式下载 tar 命令,并通过 perl 给予 tar 命令权限

  • 接着把另一台服务器把 /bin 目录打包成压缩文件,然后通过 wget 下载 bin 目录的压缩包文件

  • 最后通过 tar 命令把 bin 压缩包解压出来

/bin 就这样恢复回来啦,剩余的其他目录 也是通过同样的操作恢复了回来。

小林的笑容渐渐恢复了回来,哈哈哈哈哈哈哈哈哈哈哈哈

遇到 rm -fr /* 删库事件发生,一定要沉住气,稳住心态。

本次删库事件,之所以小林能幸运的恢复回来,有非常关键两点:

  • 小林发现脚本执行不正常,果断立马的掐断它,没有造成重要的数据库信息被删除,如果掐断的时候再晚一点,可能就真没了。

  • 小林发现常用命令无法使用的时候,没有重启服务器,不然服务器就起不来了,也没有关闭 ssh 会话,不然无法在重新连接 ssh 会话了,也就无法进行操作了。

如果以上两点都没做好,服务器恢复的难度就加大了很多,更严重的是五一节就没的过了。

预防误执行 rm -fr /*

既然 rm -fr /* 是残忍的凶器,那么预防它是很有必要的,接下来跟大家讨论讨论预防它的几种方案。

方案一:rm -rf 删除目录时要判断目录

#!/bin/bash
work_path=`pwd`
#如果目录不为空,才执行删除操作
if [ ${work_path} != "" ];thenrm -fr ${work_path}/*
fi

在执行删除目录操作前,先判断要删除的目录是否为空,不为空才执行删除操作。

方案二:Shell 脚本指定 set -u

执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它。

#!/bin/bash
echo $a
echo hello

上面代码中,$a 是一个不存在的变量,执行结果如下。

$ bash test.sh
hello

可以发现,echo $a 输出了一个空行,Bash 忽略了不存在的 $a,然后继续执行 echo hello。

最好是遇到变量不存在,脚本应该报错,而不是一声不响地往下执行。

set -u 就用来改变这种行为,在脚本加上它,遇到不存在的变量就会报错,并停止执行。

#!/bin/bash
set -u
rm -fr $a/*
echo hello

运行结果如下:

$ bash test.sh
test.sh: line 4: a: unbound variable

可以看到,因为 a 是未定义变量,脚本报错了,并且不再执行后面的语句。

方案三:safe-rm 替换 rm

safe-rm 是一个开源软件工具,这名字听起来就很安全嘛,所以它是用来替代不太安全的 rm。

它可以在 /etc/safe-rm.conf 中配置路径黑名单,定义哪些不能被 safe-rm 删除。

我们可以将 safe-rm 更名为 rm,假设定义了 /etc/ 无能被删除,那么删除 /etc 时就会报错:

$ rm -rf /etc/
safe-rm: skipping /etc/

方案四:建立回收站机制

Windows 是有回收站的,即使误删了,也可以在回收站恢复。

所以,我们也可以在 Linux 实现回收站的机制。

实现思路:

删除文件时,它并不真正执行删除操作,而是将文件移动到一个特定目录,可以设置定时清楚回收站,或者在回收站里面的文件大小达到一定容量时(或者用时间做判断)执行删除操作以腾出空间。

可以写个 Shell 脚本替换 rm 命令,或者在需要删除文件的时候使用 mv 命令将文件移动到回收站。

① 创建回收站目录

mkdir /home/.trash

② 编写 remove.sh 脚本,内容如下

③ 修改 ~/.bashrc, 用我们自建的 remove.sh 替代 rm 命令

alias rm="sh /home/remove.sh"

④ 设置 crontab,定期清空垃圾箱,如每天 0 点清空垃圾箱:

0 0 * * * rm -rf /home/.trash/*

⑤ 最后,执行以下命令,使之生效

source ~/.bashrc

方案五:根文件挂载成只读

在 /etc/fstab 文件,把 / 文件系统挂载成只读的方式。

其中 remount,ro,就表示只读的方式挂载。

只读的方式挂载后,进行删除操作是无法成功的:

事后反思

涉及到 rm -fr 命令的代码,要留个心眼,要反复检查,要做好预防误执行 rm -fr /*,并在测试机验证完后,再拖到实体机上跑,千万不可大意。

就算的发生了 rm -fr /*,要第一时间停掉它,并且要做到三不要:

  • 不要慌,不要心跳爆炸(稳住稳住)

  • 不要隐瞒删库事件(不丢人)

  • 不要重启服务器或断开 ssh 会话(保留现场)

只要立马掐断 rm -fr /* ,它是干不死我们的。

利用当下环境剩有的命令,冷静分析,是有机会恢复的。

小林现在是一个删过库没跑路的男人了,Goodbye, 我们下次见。

版权声明:本文为CSDN博主「小林coding」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/qq_34827674/article/details/105865283

更多精彩推荐
☞比 TensorFlow Lite 快 15.6 倍!业界首个移动 GPU BNN 加速引擎 PhoneBit 开源
☞华为副总裁回应应用删除用户图片;美国拟允许华为参与 5G 标准建设;Firefox 76.0 发布 | 极客头条
☞艰难的这年,程序员的未来在哪里?
☞蚂蚁金服高要求的领域建模能力,对研发来说到底指什么?
☞一文看懂主流区块链攻击底层逻辑 | 博文精选
☞饿了么交易系统5年演化史
你点的每个“在看”,我都认真当成了喜欢

写了Bug,误执行 rm -fr /*,我删删删删库了,要跑路吗?| 原力计划相关推荐

  1. php 定时脚本执行wget无效_写了个Bug,误执行rm fr /*,瞬间背后一凉!

    作者 | 小林 来源 | 小林coding 前言 前段时间,临近五一节,想到有 5 天假期,我就开始飘了. 写个简单的 Bash 脚本都不上心了,写完连检查都不检查,直接拖到到实体服务器跑. 结果一跑 ...

  2. 写了Bug,误执行 rm -fr /*,我删删删删库了,要跑路吗?

    每日英语,每天进步一点点(偷笑): 前言 临近五一节,想到有 5 天假期,小林开始飘了. 写个简单的 Bash 脚本都不上心了,写完连检查都不检查,直接拖到到实体服务器跑. 结果一跑起来,发生不对劲, ...

  3. 没有执行此操作所需的足够可用空间。_一文详解 MySQL 高可用之 DRBD | 原力计划...

    作者 | wzy0623责编 | 屠敏出品 | CSDN 博客大多数MySQL高可用解决方案都是基于MySQL自带的各种复制技术.本质上是将一个实例上的数据更新或事务,在其它实例上进行重放,从而完成数 ...

  4. 找不到libmmd.dll无法继续执行代码_300 行代码带你秒懂 Java 多线程!| 原力计划...

    作者 | 永远在路上[] 责编 | 胡巍巍 出品 | CSDN博客 线程线程的概念,百度是这样解释的:线程(英语:Thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际 ...

  5. 优秀的程序员真的不写注释吗? | 原力计划

    作者 | 沉默王二 责编 | 王晓曼 出品 | CSDN博客 前言 我在很多地方看到这样一个观点,"请停止写注释,因为只有烂的代码才需要注释."这个观点非常巧妙,它让我想起了孟子的 ...

  6. So easy!10 行代码写个“让你惊叹”的文章生成器 | 原力计划

    作者 | liu志军 责编 | 屠敏 出品 | CSDN 博客 前几天,GitHub 有个开源项目特别火,只要输入标题就可以生成一篇长长的文章. 背后实现代码一定很复杂吧,里面一定有很多高深莫测的机器 ...

  7. python 消息框但不影响程序执行_还在用print()查找错误?日志消息不香嘛?| 原力计划...

    作者 | 灰小猿 责编 | 王晓曼 出品 | CSDN博客 最近在做项目开发的时候,跟小伙伴聊到修 Bug 这件事. 嗯,对于一只没技术的程序猿来说,修 Bug 的确是一件让人头疼的事情,尤其是对于比 ...

  8. 女朋友学高数,我花了 15 分钟用栈给她写了一个计算器 | 原力计划

    作者 | 天才程序YUAN 责编 | 夕颜 头图 | CSDN 下载自东方 IC 出品 | CSDN(ID:CSDNnews) 前记 女朋友说:"高数好难,看我最近挺辛苦的,你送我一个礼物给 ...

  9. 经典实战案例:用机器学习 KNN 算法实现手写数字识别 | 原力计划

    作者 | 奶糖猫 来源 | CSDN 博客,责编 | 夕颜 头图 | CSDN 下载自视觉中国 出品 | CSDN(ID:CSDNnews) 算法简介 手写数字识别是KNN算法一个特别经典的实例,其数 ...

最新文章

  1. 复数的物理意义是什么
  2. 存货编码数字_用友T3软件存货编码与存货代码有什么不同?
  3. linux匿名页 文件页,文件页和匿名页
  4. 前端学习(3310):redux的state hook对象
  5. 【OS学习笔记】二十一 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之内核代码
  6. 修改自己的centos输入法
  7. 《Storm企业级应用:实战、运维和调优》——1.6 本章小结
  8. DNS resource record的写法
  9. 实例解析:OperaMasks2.0中的DataGrid之一:定义DataGrid
  10. 浅谈DOMContentLoaded事件及其封装方法
  11. 非确定性算法_《长安十二时辰》背后的文娱大脑:如何提升爆款的确定性?
  12. 天线年会 闭幕式 ppt_OracleVS谷歌–闭幕式,陪审团审议版权阶段
  13. 身份证城市代号python_身份证号码的地区代码分别是什么?
  14. 解决 EndNote X9 安装报错 lnstallation ended prematurely because of an error.
  15. java后台实现支付宝支付接口、支付宝订单查询接口 前端为APP
  16. 凸凹函数定义以及判断
  17. Yolov8如何在训练意外中断后接续训练
  18. php开发证书转换工具,各类证件生成器
  19. binlog_do_db 与 binlog_ignore_db
  20. 基于C++的坦克动荡游戏

热门文章

  1. Codeforces205E Little Elephant and Furik and RubikLittle Elephant and Furik and Rubik
  2. 浅谈localStorage、sessionStorage 与cookie
  3. C程序范例(3)——结构体
  4. 第一次使用并配置Hibernate
  5. full stack on the road
  6. js获取网页高度和宽度(备份)
  7. hadoop2.2.0 centos6.4 编译安装详解
  8. [位运算|状态压缩] leetcode 78 子集
  9. linux java解压文件怎么打开,linux下面的解压缩文件的命令
  10. hack wifi android,WiFi Hack AIO 2010 - WiFi v1.2