最近在网上看到一张图(原图出处不详,题图据原图重制,有修改。)
唔,感觉很有道理啊,你看,rm
是删除,这个单词简单好记;连 rf
都给出了解释,“垃圾文件”;/*
代表目录下的所有文件,没毛病;sudo
也有了,确保权限没问题。
哈哈,你一定会说,又编段子搞笑,没人会信的。
会有人执行这样的命令来清理所谓的垃圾文件么?你别说,这还真不好说。
我们经常会发一些关于关于 Bash 别名的文章,总有一些同学皮一下“贡献”一些别致的别名,比如 alias ls=rm -rf
,alias cd=rm -rf
等等。虽然我认为几乎没有人会被这些命令恶搞,但是大家也屡屡乐此不疲。
我将这张图发到朋友圈里,好友“龙十三”表示,这不仅仅清除垃圾文件,而且清除垃圾系统管理员。
是啊,为什么会有这样的段子一再出现呢?就是因为我们有些不求甚解的人,遇到问题了,习惯于从网上随便找个命令示例瞎试,不去探究其原理,也不去核实可靠性。所以,这样的段子真是用来清除“系统管理员里的垃圾”的。
那么让我们来探究一下上面这条命令,看看这里有多少知识点。
rm 的那些事
首先,这条命令是用来清除 Linux 系统的根目录(/
)下所有文件的。它使用了两个命令选项:
-r
: 递归 ( recursive ) ,对目录及其下的内容进行递归操作-f
: 强制 ( force ) ,无需确认操作
这两个选项可以单独写作 -r
和 -f
,也可以按 POSIX 惯例,将两个选项合并成 -rf
。这里的 -r
和 -f
选项,绝非上图中恶搞的 “Rubbish” 和 “Files”。
其后的参数 /*
指的是根目录 /
下的所有文件。
-r 选项
-r
选项代表 递归 ( recurive ) ,其意思是指递归地对参数中的目录及其下的文件或子目录进行删除操作。
这个选项除了短选项风格,还有 GNU 风格的长选项 --recursive
;也出于兼容性的原因,支持同义的大写 -R
参数。
如果没有该选项,则不会删除目录及其下的内容。
-f 选项
在介绍 -f
选项之前,让我们先看一下这里没有出现的 -i
选项。
原生的 rm
命令在删除文件或目录时,遵循 UNIX 惯例,在执行删除操作前和操作成功后,是静默的,毫无提示的。除非遇到错误(如要删除的文件不存在)时,否则绝不抱怨。
后来,可能是鉴于很多人经常会错误删除文件,在绝大多数的 Linux 发行版上的 rm
命令是一个添加了 -i
选项的别名:
alias rm=rm -i
这里的 -i
选项用于在每一个删除动作前做个提示,需要用户明确给予确认才会删除。
但是,有时候,这种提示实在是太烦了,所以,很多人在操作时,特意使用 -f
选项覆盖了 -i
选项的行为,使得这个别名的定义毫无意义。
因此,还有一个 -I
选项,这个选项在要删除三个及更多的文件或递归删除时,会做一次确认提示。这样,既没有 -i
选项那么烦人,又能防止大部分错误操作。所以,可以将上述别名采用 -I
选项,并避免使用 -f
选项。
* 通配了什么
我们看到命令中以*
来指代目录下的所有文件。但是严格来说,*
这个通配符代表不以点 “.
” 开头的所有文件。以 “.
” 开头的文件默认属于 Linux 下的隐藏文件。
因此,这个命令不会删除 /
目录下以 .
开头的隐藏文件,以及 .
和 ..
两个目录。但是在递归操作时,会递归地删除子目录下除了 .
和 ..
目录之外的所以文件和子目录——无论是否以 .
开头——因为递归操作不是由 Bash 等 shell 进行通配展开的。
至于为什么不在删除目录下的内容时也将 .
和 ..
一视同仁?因为自从 1979 年 rm
命令开始有删除目录的能力时,就专门避开了这两个特殊目录。
根目录保护
有一定经验的系统管理员可能这个时候会想起来,rm
命令有一对专门针对根目录的选项 --preserve-root
和 --no-preserve-root
。这对选项的意思是:
--preserve-root
:保护根目录,这是默认行为。--no-preserve-root
:不保护根目录。
这对选项是后来添加到 rm
命令的。可能几乎每个系统管理员都犯过操作错误,而这其中删除过根目录的比比皆是(我就是一个)。出现这种情况的原因有几种:
- 输入手误:比如本来想输入
rm /tmp/test.txt
,结果不小心键盘打的飞起,多输入了一个空格变成:rm / tmp/test.txt
。看到根目录(/
)后面的空格了么?!——这就是我当前自己亲手犯过的错误,而且是在生产服务器上。 - 未正确初始化或命名错误的 shell 脚本变量:比如在脚本中,
rm -rf /${tmp_dir}
,如果无论是tmp_dir
变量没有正确赋值还是输入错误(原本或许是tmpdir
?),那会导致什么?当然是删除根目录咯~
鉴于这种情况层出不穷,在 Linux 圈子几乎和“初学者如何退出 vi” 一样成为经典笑话了。所以,在 POSIX 第七版规范中,rm
命令添加了 --preserve-root
选项,并将其作为默认行为,以降低出现这种错误的可能。
但是,这个选项不能防范本文中所述的清除根目录下所有文件(/*
)的操作。
有的同学可能要问,那为什么还会专门出现 --no-preserve-root
选项呢?这可能主要是出于 UNIX 哲学的考虑,给予你想要的一切权力,犯傻是你的事情,而不是操作系统的事情。万一,你真的想删除根目录下的所有文件呢?
你还别说,真有这种需求:比如你要清除一个 chroot 环境下的所有文件。 chroot 我们这里不多讲,它就是以一个目录作为“监狱”,该目录在逻辑上形成了新的“根目录”,在该监狱内的文件操作不能跨出该目录范畴。近些年流行的 Docker、LXC/LXD 之类的容器技术,都是一种 chroot 技术。
UEFI 系统
好吧,你可能更特立独行一些,就是要清除物理环境中的根目录下所有文件!但是在你按下回车键之前,请再考虑一下,你是否在一个 UEFI 系统上?
因为 UEFI 系统会将其固件、变量和设置映射到根目录下的 /sys
分区里面,所以,如果在 UEFI 环境中清除根目录下的所有内容,也会同样清除 /sys
,这将可能会导致你丢失 UEFI 的固件设置,从而使设备变砖。
sudo 提权
为了可以删除属于 root 等系统和其它用户的文件,这个命令还需要在前面加上 sudo
来提权。
输入该命令后,会要求输入密码。谁的密码?不是 root 密码,而是输入该命令的当前用户的密码。
而对于谁能执行 sudo
命令,以及他可以通过 sudo
命令执行什么命令等知识点,这里就不再赘述,请参阅我们的其它文章。顺便说一句,要记得区分 sudo
和 su
命令的联系与区别。
垃圾文件
研究到这里,我们不能忘记这条命令原本的意图,删除“垃圾文件”。
Linux 下有垃圾文件么?有。这些垃圾文件一般来源于:
- 没有被包管理器管理的孤儿文件,在软件包被删除后,遗留在系统中
- 无用的依赖包,在需要这些依赖包的软件被删除后,没有相应删除
- 没有清理的临时文件
- 遗留的诊断文件
那么这些垃圾文件需要清除么?一般而言,Linux 系统上的这些文件大多不会对系统的健康运行造成任何影响,除非太多了,占据了很多存储空间和 inode。
所以,如果你感觉你的 Linux 系统慢了,那几乎可以肯定不是由于垃圾文件导致的,至少在这一点上,来自 Windows 系统的经验并不值得复制。
好了,关于这个简单的命令,我们已经挖掘了这么多知识点,你都知道了吗?
事实上,关于这些知识,还有更多的内涵、外延和历史信息,作为一个真正的系统管理员,而不是一个脚本小子,需要认真地研究每个命令和细节。