用 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
下都是命令,所以 ls
和 ps
等程序没法使用;/dev
下是设备对应的文件,由于被删了,磁带机这样的存储设备无法使用;/etc
下则是有各种配置文件,包括网络配置,所以也无法通过网络连接机器。这几重因素造成了即使这些人有磁带备份也难以恢复系统的窘境。
好在,有几个关键的实用程序还是可以用的。首先是文本编辑器,由于作者自己的终端上还开着 GNU Emacs,编辑器的代码已经被读入了内存,所以即使硬盘上的编辑器程序已经被删了,程序仍然可以正常运行。然后,因为 /usr
被救了下来,/usr/bin
中的程序也可以使用。/bin
和 /usr/bin
中都有安装命令,所以即使 /bin
没了,仍然有部分命令是可以用的。最后,这帮人用汇编语言写了一个创建 /etc
目录的程序(因为 mkdir
命令也没了),在别的机器上编译之后转成十六进制,用 Emacs 输进要恢复的机器里再转回二进制。之后他们就可以继续用文本编辑器重写 /etc
下的配置文件,然后就成功恢复了备份。
文章的最后,作者提出了一个技巧:如果 /bin/cat
被删,但 /usr/bin/grep
还在,可以用 grep
替代 cat
。看到这,我陷入了沉思:这是怎么做到的呢?
我们首先来看看 grep
和 cat
的作用都是什么。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
,不会匹配 egrep
,grep -E
或者 fgreping
。所以,正则表达式 ^$
可以用来查找空字符串,故如果想用 grep
搜索空行,只需使用 grep "^$"
即可。
grep
还提供一个 -x
选项,其效果等同于在正则表达式前后加上 ^
和 $
。因此,grep -x ""
和 grep "^$"
的作用是一样的,都可以用来寻找空行。