我最近看到了这样一句话:
$ ps -ef | grep [f]irefox
thorsen 16730 1 1 Jun19 ? 00:27:27 /usr/lib/firefox/firefox ...
因此,它似乎返回数据中包含“firefox”的进程列表,但忽略了 grep 进程本身,因此似乎大致相当于:
ps -ef |grep -v grep| grep firefox
但我不明白它是如何工作的。我查看了 grep 的手册页和其他地方,但没有找到解释。
如果我运行,谜团就会变得更加复杂:
$ ps -ef | grep firefox > data
$ grep [f]irefox data
thorsen 15820 28618 0 07:28 pts/1 00:00:00 grep --color=auto firefox
thorsen 16730 1 1 Jun19 ? 00:27:45 /usr/lib/firefox/firefox ....
这个 [t]rick 好像停止工作了!
我确信这里有人会知道发生了什么。
谢谢。
答案1
方括号表达式是bash shell(以及其他 shell) grep 的字符类模式匹配的一部分。
该grep
程序默认理解 POSIX 基本正则表达式。您可以使用它来定义字符类。例如,ps -ef | grep [ab9]irefox
将找到“A火狐浏览器”b火狐浏览器”9irefox”,如果存在的话,但不存在“AB请参阅“火狐浏览器”。
该命令grep [a-zA-Z0-9]irefox
甚至会找到所有以一个字母或数字开头并以“irefox”结尾的进程。
因此ps -ef | grep firefox
搜索包含 的行firefox
。由于 grep 进程本身包含“firefox”,因此 grep 也会找到它。通过添加[]
,我们只搜索字符类“[f]”(它仅由字母“f”组成,因此相当于没有括号的“f”)。括号的优点是字符串“firefox”不再出现在 grep 命令中。因此,grep 本身不会出现在 grep 结果中。
由于很多人不熟悉方括号作为字符类匹配和一般的正则表达式,所以第二个结果可能看起来有点神秘。
如果您想修复第二个结果,您可以按以下方式使用它们:
ps -ef | grep [f]irefox > data
grep firefox data
答案2
原因是字符串
grep firefox
匹配模式firefox
,但字符串
grep [f]irefox
与模式不匹配[f]irefox
(相当于模式firefox
)。
这就是为什么第一个 grep 匹配其自己的进程命令行,而第二个却不匹配的原因。
答案3
丹尼尔的回答是正确的,但是 jokerdino 关于 shell 转义的回答(很大程度上是错误的)让我想到了一个有趣的复杂情况。
首先,请注意,ps
未经过滤的输出将包含一行grep
与您的 shell 启动的进程相对应的内容。如果您grep firefox
在运行 时正在运行ps
,则会在输出中看到它:
$ ps
thorsen 15820 28618 0 07:28 pts/1 00:00:00 grep firefox
thorsen 23983 1 1 Jun19 ? 00:12:34 some other process ....
如果您随后获取ps
的输出并通过该grep
过程对其进行过滤 -ps
在 的输出中查找与正则表达式匹配的字符串firefox
- 那么,该行将匹配!
$ ps | grep firefox
thorsen 15820 28618 0 07:28 pts/1 00:00:00 grep firefox
^^^^^^^ Found it!
但是如果你启动时使用的grep
参数与你正在查找的正则表达式不匹配,那么 的ps
输出将会不是匹配正则表达式。
$ ps | grep 'f[ij]refo*x'
未过滤的输出将包含如下行
thorsen 15820 28618 0 07:28 pts/1 00:00:00 grep f[ij]refo*x
但过滤后的输出不会,因为该行不包含任何与正则表达式匹配的子字符串f[ij]refo*x
。(该行不包含firefx
,或fjrefx
,或firefox
,或fjrefoox
,或...)
但正如 jokerdino 指出的那样,能这里还有别的事情发生!因为括号字符对大多数人来说也是神奇的贝壳. 当你写
ls foo*.[ch]
Bash shell 实际上会查看当前工作目录中有哪些文件可用,然后展开这些文件全局变成,像,
ls foo.c foobar.c foobar.h
如果您不希望发生 shell 通配符,则必须使用反斜杠转义特殊字符*
, [
,]
或将它们括在单引号中:
$ ls foo*.[ch]
foo.c foobar.c foobar.h
$ ls 'foo*.[ch]'
ls: foo*.[ch]: No such file or directory
如果 Bash 在当前目录中找不到任何匹配的文件,则通配符也会变为无操作:
$ rm foo*.[ch]
$ ls foo*.[ch]
ls: foo*.[ch]: No such file or directory
所以,当你写
$ grep [f]irefox
没有任何单引号,它导致grep
寻找与正则表达式匹配的行,[f]irefox
正是因为[f]irefox
在你当前的工作目录中没有与 glob 匹配的文件!这和你的实际观察无关,但值得注意的是,你可以观察到以下行为:
$ cd /usr
$ ps -ef | grep [f]irefox
thorsen 16730 1 1 Jun19 ? 00:27:27 /usr/lib/firefox/firefox ....
$ cd /usr/lib
$ ps -ef | grep [f]irefox
thorsen 15820 28618 0 07:28 pts/1 00:00:00 grep --color=auto firefox
thorsen 16730 1 1 Jun19 ? 00:27:27 /usr/lib/firefox/firefox ....
在第二种情况下,由于当前目录有一个名为 的条目firefox
,未加引号的参数[f]irefox
在 Bashgrep
看到它之前就被展开了,最终你只能 grep 搜索正则表达式firefox
而不是[f]irefox
。解决方案是添加单引号:
$ ps -ef | grep '[f]irefox'
thorsen 16730 1 1 Jun19 ? 00:27:27 /usr/lib/firefox/firefox ....
我建议在每个参数周围添加单引号,包括“shell 元字符”,例如,,,,,,,,等等*
——尤其是正则表达式![
(
{
=
,
;