如何将一系列通过管道连接在一起的 grep 语句组合成一个 grep 语句?

如何将一系列通过管道连接在一起的 grep 语句组合成一个 grep 语句?

我想知道是否有一种方法可以组合一系列 grep 语句,其效果是“与”表达式而不是“或”匹配的表达式。

下面的演示:

./script  
     From one grep statement, I want output like this
a b c

     not like this
a
c
a b
a b c
a b c d

听听这个剧本。

 #!/bin/bash
 string="a
 b
 c
 d
 a b
 a b c
 a b c d"

 echo -e "\t From one grep statement I want output like this"
 echo "$string" |
 grep a |grep c |grep -v d #Correct output but pipes three grep statements

 echo -e "\n\tNot like this"
 echo "$string" |
 grep -e'a' -e'c' -e-v'd' #One grep statement but matching expressions are "or" versus "and"

答案1

不能将过滤器转换grep a | grep c | grep -v d为单一的简单grep方法。只有复杂且无效的方法。结果性能很慢,并且表达式的含义模糊。

三个 greps 的单个命令组合

如果您只想运行单个命令,则可以使用awk它也适用于正则表达式并可以将它们与逻辑运算符组合。以下是您的过滤器的等效项:

awk '/a/ && /c/ && $0 !~ /d/'

我认为在大多数情况下没有理由将管道简化为单个命令,除非组合产生一个相对简单的 grep 表达式并且速度可能更快(见下面的结果)。

类 Unix 系统被设计为使用管道并将各种实用程序连接在一起。虽然管道通信不是最有效的,但在大多数情况下它已经足够了。因为现在大多数新计算机都有多个 CPU 核心,所以您只需使用管道就可以“自然地”利用 CPU 并行化!

您原来的过滤器工作得很好,我认为在很多情况下,awk即使在单核上,解决方案也会稍微慢一些。

性能比较

用一个简单程序我生成了一个包含 200 000 000 行的随机测试文件,每行包含 4 个字符,这些字符是字符 、 和 的随机组合abc文件d有 1 GB。在测试期间,它完全加载到缓存中,因此没有磁盘操作影响性能测量。测试在 Intel 双核上运行。

单个 grep

$ time ( grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$' testfile >/dev/null )
real    3m2.752s
user    3m2.411s
sys 0m0.252s

单 awk

$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real    0m54.088s
user    0m53.755s
sys 0m0.304s

最初的三个 grep 通过管道传输

$ time ( grep a testfile | grep c | grep -v d >/dev/null )
real    0m28.794s
user    0m52.715s
sys 0m1.072s

混合 - 正向 grep 组合,负向管道

$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real    0m15.838s
user    0m24.998s
sys 0m0.676s

在这里你可以看到grep在这里你可以看到,由于表达式复杂,awk非常慢。由于并行性好,三个 grep 的原始管道非常快。如果没有并行化 - 在单核上 - 原始管道的运行速度略快于单个进程,因为单个进程没有并行化。awk 和 grep 可能使用相同的正则表达式代码,并且这两个解决方案的逻辑相似。

明显的赢家是混合型,它结合了两个正则表达式,并将负则表达式留在管道中。看来正则表达式 with|不会对性能造成影响。

答案2

问题-eor,而 不能作为 运行and。你可以在一行中完成,但这非常复杂。 不是 的部分是最复杂的。

为了简化ac部分(假设顺序未知):

grep -E 'a.*c|c.*a'

或者

grep -e 'a.*c' -e 'c.*a'

因此你可以这样做

grep -E 'a.*c|c.*a' | grep -v 'd'

对于单个 grep 语句,您必须确保andd之前、之后或之间没有 s :ac

grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$'

答案3

您可以使用-x开关,根据grep手册页,“仅选择与整行完全匹配的匹配项。”。

在您的示例中,尝试:grep -x "a b c"

相关内容