3.4《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——grepping(检索目标行命令)
grep
是检查文件内容最强大的工具之一,这也许不能代表什么,但这不是重点。的确,grep常用作动词,比如‘你完全应该检索(grep)那个文件’.
grep
最常用于在文件中搜索子字符串。例如,我们在第三章节中学到的在莎士比亚诗中搜索'rose'字符串。而使用grep
,我们可以直接找到标记,如Listing 16中展示的那样:
Listing 16: 寻找出现在莎士比亚诗中的'rose'
$ grep rose sonnets.txt
The rose looks fair, but fairer we it deem
As the perfumed tincture of the roses.
Die to themselves. Sweet roses do not so;
Roses of shadow, since his rose is true?
Which, like a canker in the fragrant rose,
Nor praise the deep vermilion in the rose;
The roses fearfully on thorns did stand,
Save thou, my rose, in it thou art my all.
I have seen roses damask'd, red and white,
But no such roses see I in her cheeks;
使用Listing 16中的命令,看来我们得用WC
来计数‘rose’个数了(3.3章中介绍过), 正如Listing 17中:
Listing 17:* 对
grep
的输出结果使用wc
*
$ grep rose sonnets.txt | wc
10 82 419
在上面👆Listing 17中我们得知有10行包含'rose'(或'roses',因为'rose'是‘roses’的自字符串)。但是你可能在之前的插图12中看到莎士比亚的第一首诗就包含"Rose",以大写字母'R'开始,再看Listing 16,我们会发现事实上这行被忽略了。这是因为grep
默认要区分大小写字母,'rose'不匹配"Rose".
正如你猜想那样,grep
也有个选项可以改变大小区分匹配方式。要弄清楚是哪个选项有个方法是搜索grep
的man
手册页:
- 输入
man grep
- 在输入
/case
然后回车 - 阅读结果(插图19)
(正如1.3章节中简洁批注样,手册页使用与3.3章遇到的less
命令相同的用户界面,所以我们可以通过/
来搜索)
运用以上学到的知识像Listing 18那样操作。比较Listing 18和Listing 17的结果,我们看到现在有12行匹配而不只是10行了,所以诗中总共有12 -10 = 2行包含'Rose'(但不是'rose')。
Listing 18: 不区分大小写的grep
$ grep -i rose sonnets.txt | wc
12 96 508
grep
工具非常非常强大,特别是与所谓的正则表达式结合在一起时,但是学习grep
和grep -i
几乎是很厉害的了(包括用grep搜索寻找过程中的重要应用(Box 10)).在第四章中我们会发现第三和最后个grep
会变得更加厉害。
Box 10 搜索命令过程
grep的众多用途之一是过滤找到众多运行Unix进程的匹配进程。(在似Unix系统中,如Linux和macOS, 用户和每个系统会各占进程,即定义好的容器中。)这对杀死流氓程序非常有用。(查找这些进程的好办法是运行‘stop’命令,它会显示消耗最多资源的进程。)
例如,在Ruby Rails的教程中里有一点,在进程列表中清除'spring'的程序非常重要。要清除的话,首先要找到这个进程,要查看系统中所有的进程用ps
命令加上aux
选项:
$ ps aux
图16, ps 是'process status'的简写。由于模糊不清,ps
选项没有用短横线(所以它用ps aux 而不是 ps -aux)。(地球人怎么会懂?这正是本篇教程的目的,介绍这些知识。)
通过程序名过滤进程,你可以通过grep
管道输出ps的结果:
$ ps aux | grep spring
ubuntu 12241 0.3 0.5 589960 178416 ? Ssl Sep20 1:46
spring app | sample_app | started 7 hours ago
结果中展示了一些进程的细节,但是最总要的是第一个数字,就是进程的id,或者pid(与kid发音押韵)。要清楚一个不想要的进程,我们使用kill命令终止Unix代码pid(正好是15)的进程:
$ kill -15 12241
这是我建议杀死单个进程的技术,比如流氓网页服务(通过ps aux | grep server查找pid), 但有很容易杀掉与特定进程名匹配的所有进程,比如杀死所有消耗系统资源的'spring'进程。在这种情况下,可以像下面这样使用'pkill'杀死所有带有名'spring'的进程:
$ pkill -15 -f spring
不是所有时候都能如预期表现,或冻结一个进程,有个好办法是,运行top或者ps aux查看什么在运行,通过grep与ps aux选出可疑进程,然后在执行kill -15
练习
- 通过搜索
man grep
手册页中的'line number', 构建命令查找sonnets.txt中'rose'出现的行数。 - 你应该发现了最后出现'rose'(匹配的 'roses')在2203行。弄清楚运行less sonnets.txt时做噩梦直接跳到这行。备注: 再查阅Table 4中,1G可以到文件的顶部,即就是1行。类似地,
17G
就到 17行等。
3.通过piping grep
的输出head
,只打印出'sonnets.txt'中包含'rose'的第一行。备注:使用3.2.2章节中的第二个练习。
4.在Listing 18中,我们看到另外两个不区分大小写匹配'rose'。执行命名确认这两行都包含字符串"Rose"(而不是其他的,像'rOSe'). 备注: 使用匹配大小写的'grep'搜索"Rose".
5.在之前的练习中可能你已经发现了其实有3行匹配"Rose"而不是Listing 18中预期的2行。这是因为有一行即包含了"Rose"又包含了"rose", 因此在运行命令grep rose
和grep -i rose
时都出现了。写一条命令确认匹配“Rose”但不匹配"rose"的命令,预期是2。备注:pipe grep
的结果和grep -v
,然后将结果与wc
管道输出。(-v
是干嘛的? 阅读grep
的手册页(Box 5)).