如何使用grep搜索同一行上存在的两个单词?

167

如何使用grep命令筛选包含两个输入关键字的行?我想要筛选同时包含这两个关键字的行,应该怎么做呢?我尝试了以下管道操作:

grep -c "word1" | grep -r "word2" logs

在第一个管道命令之后它就卡住了。

为什么?


1
可能是重复的问题:如何使用grep匹配string1和string2? - jww
7个回答

221
为什么要传递 -c?这只会显示匹配的数量。同样,没有使用 -r 的理由。建议您阅读 man grep
要在同一行中搜索2个单词,只需执行以下操作:
grep "word1" FILE | grep "word2"

grep "word1" FILE | grep "word2" | wc -l

grep "word1" FILE | grep -c "word2"

此外,回答你的问题为什么会卡住:在 grep -c "word1" 中,你没有指定文件。因此,grep 期望从 stdin 输入,这就是它看起来卡住的原因。你可以按下 Ctrl+D 发送 EOF(文件结束符)以退出。


65
当你感到困惑时,手册页几乎是你不想去寻求澄清的最后一处地方。它们比随意猜测还要更加令人困惑。 - corsiKa
8
@TotalFrickinRockstarFromMars: 我不同意。虽然开头可能会使人感到困惑,但一旦你习惯了使用它们的格式,就变得非常简单明了了。无论如何,我将它们包含在答案中更多是为了“授人以渔”的部分,我预计问问题的人不知道如何使用它们,而手册页可以非常方便。 - houbysoft
10
那我们就不得不同意有所分歧了。我使用Linux和相关软件已有8年之久,但我仍然宁愿用谷歌而不是阅读man手册。 - corsiKa
你能指出在grep手册中你发现“困惑”的具体事项吗?;) 我认为更广泛的问题是,Linux实用程序会向你抛出一堆选项,然后提供单个字母别名,以便它们可以被简洁地加密在互联网上,让每个人通过谷歌来解密这个命令到底是做什么的。 - aaaaaa
6
@ geneorama 当然,但也许最初编写实用程序的人会编写更好的手册,这样就不会成为问题了。 手册是为那些已经知道该工具并只需要一点提示的人编写的,而不是为试图弄清楚自己在做什么的人编写的。 - corsiKa
显示剩余6条评论

111

指令

对于问题中的指令,一个简单的重写方式如下:

grep "word1" logs | grep "word2"

第一个grep从文件 'logs' 中查找带有'word1'的行,然后将这些行作为输入传递给第二个grep,第二个grep查找包含'word2'的行。

不过,并不必像这样使用两个命令。您可以使用扩展版的grepgrep -Eegrep):

grep -E 'word1.*word2|word2.*word1' logs
如果您知道'word1'会在行上出现在'word2'之前,那么甚至不需要使用替代方案,普通的grep就可以满足需求。
grep 'word1.*word2' logs
'一命令'的变体的优点在于只有一个进程在运行,因此包含“word1”的行不需要通过管道传递给第二个进程。这有多重要取决于数据文件有多大以及有多少行与“word1”匹配。如果文件很小,则性能不太可能成为问题,并且运行两个命令是可以接受的。如果文件很大但只有少数行包含“word1”,则在管道上不会传递太多数据,并且使用两个命令也是可以接受的。然而,如果文件非常大并且“word1”经常出现,则您可能会将大量数据传递到管道中,其中单个命令可以避免这种开销。相对应的,正则表达式更加复杂;您可能需要进行基准测试才能找到最佳方案,但前提是性能真正有影响。如果要运行两个命令,则应该尝试在第一个grep中选择出现频率较低的单词,以最小化第二个命令处理的数据量。

诊断

初始脚本如下:

grep -c "word1" | grep -r "word2" logs
这是一个奇怪的命令序列。第一个 grep 将在其标准输入中计算 'word1' 出现的次数,并在其标准输出上打印该数字。在指示 EOF(例如通过键入 Control-D)之前,它将停在那里,等待您输入内容。第二个 grep 在目录 logs 下的文件中(或者如果是文件,则在文件 logs 中)进行递归搜索 'word2'。或者,在我的情况下,它会失败,因为我在运行管道的地方没有名为 logs 的文件夹或文件。请注意,第二个 grep 根本不读取其标准输入,因此管道是多余的。

使用 Bash,父 shell 等待直到管道中的所有进程退出,因此它会一直等待 grep -c 完成,直到你指示 EOF 为止。因此,你的代码似乎会卡住。使用 Heirloom Shell,第二个 grep 完成并退出,shell 提示符再次出现。现在有两个进程正在运行,第一个 grep 和 shell,它们都尝试从键盘读取数据,但无法确定哪个进程获得任何给定的输入行或 EOF 指示。

请注意,即使您在第一个 grep 中键入了数据作为输入,您也只会看到包含 'word2' 的行显示在输出中。


注:

曾经该答案使用:

grep -E 'word1.*word2|word2.*word1' "$@"
grep 'word1.*word2' "$@"

这引发了下面的评论。


1
为了更高效地执行grep grep,可以尝试加1。 - David Fairbanks
这个解决方案的另一个优点是,如果两个单词相同,它也可以检测出单词在一行中是否重复。而被接受的解决方案则无法处理这种情况。+1。 - Diego Pino
@JonathanLeffler 当使用 grep 'word1.*word2' 时,它能否仅在查找到的行上突出显示 word1word2 - alper
@alper — 不行。你可能会通过 grep ‘word1.*word2’ | grep -F -e ‘word1’ -e ‘word2’ 得到所需的结果,但我没有检查过。 - Jonathan Leffler
感谢@JonathanLeffler,我发布了问题并得到了一些解决方案,但似乎最简单的方法是使用您建议的--color=always,然后在管道中grep word2。 - DenisZ
显示剩余6条评论

12

你可以使用awk。像这样...

cat <yourFile> | awk '/word1/ && /word2/'

顺序不重要。所以如果您有一个文件和...

一个名为file1的文件包含:

word1 is in this file as well as word2
word2 is in this file as well as word1
word4 is in this file as well as word1
word5 is in this file as well as word2

那么,

/tmp$ cat file1| awk '/word1/ && /word2/'

会导致

word1 is in this file as well as word2
word2 is in this file as well as word1

是的,awk更慢一些。


3
cat(1) 的无用使用 - Michael Shigorin
2
一个单独的Awk仍然比两个独立的grep进程更快。 (但是,当然,额外的无用的“cat”进程会或多或少地抵消这种差异。) - tripleee

7
主要问题在于您没有向第一个 grep 提供任何输入。您需要重新排列您的命令,例如:
grep "word1" logs | grep "word2"

如果你想统计出现次数,那么在第二个grep命令后面加上“-c”。


5

git grep

以下是使用git grep语法,结合多个模式使用布尔表达式的方法:

git grep -e pattern1 --and -e pattern2 --and -e pattern3

以上命令将一次打印匹配所有模式的行。

如果文件不在版本控制下,请添加--no-index参数。

搜索当前目录中未由Git管理的文件。

查看man git-grep以获取帮助。

另请参阅:


4

您可以尝试以下命令:

cat log|grep -e word1 -e word2

5
这些命令搜索至少一个单词,而不是所有单词。并且 cat | 是不必要的,你可以将文件作为 grep 的最后一个参数。 - Mat M
5
可能是无用的猫的使用? - Ganapathy

-2
使用grep命令:
grep -wE "string1|String2|...." file_name

或者您可以使用:

echo string | grep -wE "string1|String2|...."

1
这些命令是搜索至少一个单词,而不是所有单词。 - Mat M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接