ps aux | grep with a dot

我正在尝试编写一个脚本,用于查找正在运行的进程。
我想避免实际的grep命令从ps aux输出中返回。
然而,如果我使用点或不使用点,结果会有所不同: ps aux | grep [s]elenium 不返回任何内容。 ps aux | grep [s]elenium.jar 返回grep命令。
beebee   36155  0.0  0.0  15940   956 pts/0    S+   16:20   0:00 grep --color=auto selenium.jar

为什么呢?

你正在匹配以字符 s 开头,后跟 elenium 的确切字符串。实际字符串包含更多内容。由于它是一个 jar 文件,它以 java 命令开头。我的方法就是只匹配 selenium 字符串,并使用 grep -v grep 进行过滤,以去除 grep 命令本身。 - Sergiy Kolodyazhnyy
3个回答

我猜你的当前文件夹中有一个名为 selenium.jar 的文件,但没有名为 selenium 的文件。
如果你运行
 ps aux | grep [s]elenium.jar

shell会尝试从当前文件夹中替换[s]elenium.jar与匹配的文件名。如果存在一个名为selenium.jar的文件,它将被匹配并且[s]elenium.jar将被替换为selenium.jar

然后,shell将使用替换后的值执行命令,即:

ps aux | grep selenium.jar

为了避免这个问题,引用正则表达式以保护它免受Shell的影响。
ps aux | grep '[s]elenium.jar'

我明白了,我刚测试了一下,结果与你描述的完全相同。我不知道shell会干扰管道grep!谢谢。 - Beebee
你有相关的参考资料吗?我不知道当前文件夹中的文件名可以在grep正则表达式中进行替换,而且我在man grep中也找不到相关信息。 - Jos
3@Jos:这不是grep的功能,而是shell的功能:当你在shell中运行一个命令时,shell首先将其分割成“单词”,然后根据一些规则尝试扩展每个“单词”。请参阅bash手册的“EXPANSION”部分。 - Florian Diesch

问题在于,正如@Florian敏锐地发现的那样,shell会在将搜索字符串传递给grep之前,将通配符字符扩展为匹配的文件名。
运行"ps aux | grep foo"是有问题的,因为"grep foo"命令本身会匹配"foo",从而出现在输出中。有两种常见但复杂的解决方法。一种是在管道末尾添加"grep -v grep",另一种是使用"grep [f]oo"。由于grep使用正则表达式,它会将"[f]"视为"括号内字符列表中的任意字符"。由于括号内只有一个字符"f","[f]oo"等同于"foo"。然而,在"ps"结果中显示的grep进程将具有字符串"[f]oo",因此无法被grep找到。
这变得更加复杂,如果像你所说的,在当前目录下有一个名为foo的文件。因为你没有引用给grep的表达式(因为你使用了[s]elenium而不是'[s]elenium'"[s]elenium"),shell会将其视为glob并将其扩展为匹配的文件名。这使得[s]elenium的技巧变得无用,因为实际传递给grep的是selenium而不是[s]elenium,所以grep会匹配自身。
然而,所有这些只是因为你没有使用正确的工具来完成任务。就像经常发生的情况一样,有一个专门做这个的应用程序!不要使用grep+ps。相反,使用pgrep,它专门设计用于完成你想要的功能:
NAME
       pgrep,  pkill  -  look  up  or signal processes based on name and other
       attributes

SYNOPSIS
       pgrep [options] pattern
       pkill [options] pattern

DESCRIPTION
       pgrep looks through the  currently  running  processes  and  lists  the
       process IDs which match the selection criteria to stdout.  All the cri‐
       teria have to match.  For example,

         $ pgrep -u root sshd

所以,在你的情况下,你只需要这样做
pgrep selenium

或者,由于您是通过java运行它,可以使用-f选项来搜索整个命令行。
pgrep -f selenium

我尝试过使用pgrep,但是我是通过java -jar来运行selenium的,所以它找不到。请参考这里:http://askubuntu.com/questions/157075/why-does-ps-aux-grep-x-give-better-results-than-pgrep-x - Beebee
pgrep-f标志,允许您使用完整的命令行进行搜索。尝试使用pgrep -f selenium - Sergiy Kolodyazhnyy
@Beebee,就像Serg所说的那样。你需要使用“-f”来搜索整个命令行,而不仅仅是已启动的可执行文件(java)。 - terdon
由于他的问题是关于命令行提供的文件名的shell扩展,所以他在使用pgrep时也会遇到问题。 - Johnny
@johnny 不,他不会这样做。Shell只是干扰字符串,因为它包含元字符(方括号)。你在使用pgrep时不需要它们。 - Dubu
1@johnny,就像Dubuque所说的那样。Shell扩展搜索字符串的唯一原因是因为它包含通配符,所以Shell将其视为通配符。它之所以包含通配符,是因为这是一种经典的技巧,用于在解析ps时阻止grep出现在输出中。而之所以需要grep,是因为正在解析ps。如果使用专为此功能设计的pgrep,所有这些问题都会消失。 - terdon
@terdon 这个“唯一的原因”链对于你的回答的价值以及理解为什么OP使用括号起初是至关重要的。如果你把这个解释放到你的回答中,可能在开头部分,会帮助更好地阐明和激发你的回答。起初我以为你的回答是一个无关紧要的离题,但是这个解释终于让它变得清晰了。 - Don Hatch
如@terdon所说:“它之所以有通配符,是因为这是一种经典的技巧,可以防止在解析ps时grep出现在输出中。”我正在尝试编写一个脚本,如果selenium还没有启动,就启动独立服务器。我曾经使用一个bash脚本来ps aux | grep selenium并在没有输出时启动它。 - Beebee
@DonHatch 这样好一些吗? - terdon

你可以在初始grep之后排除“grep”。
ps aux | grep '[s]elenium.jar' | grep -v grep

1这是一个巧妙的解决办法,谢谢 :) 但不幸的是,它实际上并没有回答“为什么”的问题 :) - Beebee
1@drew,如果你已经使用了grep '[s]elenium',就不需要再用grep -v了。 - terdon