grep 中这个棘手的括号表达式如何工作?

grep 中这个棘手的括号表达式如何工作?

我最近看到了这样一句话:

$ 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 元字符”,例如,,,,,,,,等等*——尤其是正则表达式![({=,;

相关内容