grep 替代 cat

我遇到了一篇很有趣的英文文章 Unix Recovery Legend,讲的是一群人拯救一个被 rm -rf / 毁坏的系统的故事。几十年前,在电脑还没发展到人手一台,一台电脑要给好几个人同时使用,每个人自己只有一个终端的年代的某一天,与该文章作者同处一间办公室的同事发现邮件发不出去了,作者就前去帮忙检查,发现 ls 命令和 /etc 目录被删了,于是就去找系统管理员,发现管理员的屏幕上赫然显示着这么两行文字:

# cd
# rm -rf *

由于是以超级用户身份执行的,第一行命令,cd,把工作目录切到了根目录 /(在现在的 GNU/Linux 环境中这么做则会切到 /root 目录下),然后第二行命令就删除了 / 下的所有目录和文件,效果等同于 rm -rf /。好在这条指令在把硬盘清光之前被强行停止了,虽然 /bin/dev/etc/lib 被删,但是 /tmp/usr/users(相当于现在的 /home)逃过一劫。/bin 下都是命令,所以 lsps 等程序没法使用;/dev 下是设备对应的文件,由于被删了,磁带机这样的存储设备无法使用;/etc 下则是有各种配置文件,包括网络配置,所以也无法通过网络连接机器。这几重因素造成了即使这些人有磁带备份也难以恢复系统的窘境。

好在,有几个关键的实用程序还是可以用的。首先是文本编辑器,由于作者自己的终端上还开着 GNU Emacs,编辑器的代码已经被读入了内存,所以即使硬盘上的编辑器程序已经被删了,程序仍然可以正常运行。然后,因为 /usr 被救了下来,/usr/bin 中的程序也可以使用。/bin/usr/bin 中都有安装命令,所以即使 /bin 没了,仍然有部分命令是可以用的。最后,这帮人用汇编语言写了一个创建 /etc 目录的程序(因为 mkdir 命令也没了),在别的机器上编译之后转成十六进制,用 Emacs 输进要恢复的机器里再转回二进制。之后他们就可以继续用文本编辑器重写 /etc 下的配置文件,然后就成功恢复了备份。

文章的最后,作者提出了一个技巧:如果 /bin/cat 被删,但 /usr/bin/grep 还在,可以用 grep 替代 cat。看到这,我陷入了沉思:这是怎么做到的呢?

我们首先来看看 grepcat 的作用都是什么。grep 可以在给定的输入文本中搜索正则表达式的匹配,然后将有匹配的行全部输出,没有匹配的行会被过滤掉;而 cat 则是将输入的文本全部原封不动地输出。对于同样的输入内容,grep 的输出是 cat 输出的子集。如果我们能想办法不让 grep 过滤任何输入的内容,让它输出全部文本,那它和 cat 的效果就一样了。

最直接的让 grep 不过滤掉任何文本的方法就是给它一个空的正则表达式,也就是空字符串。在用作正则表达式时,空字符串将匹配所有字符串。既然每一个字符串都被匹配了,那么所有的文本就都可以被输出了。

所以,grep "" 可以用来取代 cat 的最基础用法。

使用 grep 创建和写入文件

cat 命令最常见的用法就是直接从终端查看一个文件的内容,但是它同样可以被用来直接从命令行界面写文件,不需要任何文本编辑器。网上有许多教程都会在需要建立新文件的时候用 cat 命令写入文件的内容。

如果您直接运行 cat 命令,不指定任何文件的话,那么 cat 会读取标准输入(也就是键盘),然后将您输入的任何内容直接输出。cat 的本质是复读机)

这么个复读机程序有什么卵用吗?然而,利用 Unix 的输出重定向cat 的复读机行为就可以发挥真正的作用。默认情况下,cat 的标准输出是终端;如果把输出重定向到一个文件,那么 cat 就可以把您输入的内容写到文件里,不再愚蠢地复读了。

提示:当您完成输入后,可以按 Ctrl-D,发送“文件结尾”(EOF)字符。绝大多数的 Unix 程序通过检测 EOF 字符来查看用户是否已经结束了输入;收到 EOF 后,程序就可以开始处理用户输入的内容了。cat 在遇到 EOF 时,会将其读取到的内容写入到输出中,然后退出。

cat 可以用来写入文件,那 grep "" 行吗?答案是肯定的,因为 grep 在用户没有指定文件名时也会从标准输入来读取文本,和 cat 一样(尽管有些选项,比如 -r,可以改变此行为)。

使用 grep 查找空行

既然空的正则表达式匹配任何字符串,那么如果真的想要搜索空行的话,应该用什么正则表达式呢?

正则表达式的语法中有两个特殊字符 ^$,分别对应一个字符串的开始和结尾。比如说,正则表达式 ^grep$ 只会匹配 grep,不会匹配 egrepgrep -E 或者 fgreping。所以,正则表达式 ^$ 可以用来查找空字符串,故如果想用 grep 搜索空行,只需使用 grep "^$" 即可。

grep 还提供一个 -x 选项,其效果等同于在正则表达式前后加上 ^$。因此,grep -x ""grep "^$" 的作用是一样的,都可以用来寻找空行。