通常情况下,我们总是会结合使用ps
和grep
的方式来筛选出只包含某个进程名的那些行,比如:
1 2 3 4 5 6 |
|
结果是出来了,但你有没有注意到最后多出了一行?那是什么?那其实就是我们的 grep 命令本身的命令行。 因为它的命令行中也包含了被筛选的内容(nginx),所以自然也会被包含进来。
所以怎么去掉?先说答案,像下面这样即可:
1 2 3 4 5 |
|
解释
grep
(全局正则表达式打印,Global Regular Expression Print),是一种正则表达式模式匹配工具:在输入中搜索/匹配内容,并打印出匹配的内容。
也就是说在搜索匹配时执行的是模式匹配,而不是简单地单个字符的依次比较匹配。
在上面的用例中,[n]
这个模式在正则表达式中的含义是:中括号中的字符n
必须出现一次。那么grep '[n]ginx'
的含义是:在输入中搜索匹配nginx
这个字符串。
因此就含义本身而言, grep '[n]ginx'
整体的含义等价于grep nginx
。也即:如果输入相同,它们匹配到的内容完全相同。
那么,前面的例子怎么解释?很好解释:两个命令的输入并不相同。尽管它们的输入都是来自ps aux
,但是grep
这个命令本身的命令行在被ps
枚举出来时是不一样的。
- 对于
ps aux | grep nginx
,grep 命令的命令行是:grep nginx
; - 对于
ps aux | grep '[n]ginx'
,grep 命令的命令行是:grep [n]ginx
;
由于grep nginx
和grep '[n]ginx'
都是在输入中搜索匹配nginx
,但是因为上述后者的grep
命令行为grep [n]ginx
。因此不被包含在结果中。这,就是原因。
为什么要加单引号?
像上面这样的比较简单的字符串的匹配,不加引号基本上不会出错。
但是,在 bash 这种完全基于字符串的语言中,任何人都能轻易写出漏洞百出的脚本代码。
比如就本文的例子而言:[]
不仅是正则表达式中的特殊字符,它还是bash的路径名展开(pathname expansion)语法的一部分:bash
这个shell在执行我们的命令前会分析我们输入的命令,
并且执行若干个展开步骤中的路径名展开(pathname expansion),最后才是执行命令。
那么何为路径名展开?你可以简单地理解为替换通配符,就像我们可以用*
代替当前目录下的所有文件一样。
展开有成功也有失败:
- 若成功:展开前的特殊字符被路径名替换;
- 若失败:默认保持不变;
举个例子:
- 如果当前目录下没有一个名为
nginx
的文件,grep [n]ginx
(注意没有引号)会展开成grep [n]ginx
,没变。 - 如果当前目录下有一个名为
nginx
的文件,grep [n]ginx
(注意没有引号)会展开成grep nginx
,中括号没了。
所以,后者得不到期望的结果。
为什么加单引号?单引号可以避免 bash 做路径名展开。
单引号会作为参数的一部分传递给grep
吗?不会,bash 还会执行一个叫Quote Removal
的步骤,将(不是用户特意产出的)引号给移除掉。
参考链接: