我想知道是否有一种方法可以组合一系列 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 个字符,这些字符是字符 、 和 的随机组合a
。b
该c
文件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
问题-e
是or
,而 不能作为 运行and
。你可以在一行中完成,但这非常复杂。 不是 的部分是最复杂的。
为了简化a
和c
部分(假设顺序未知):
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 :a
c
grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$'
答案3
您可以使用-x
开关,根据grep
手册页,“仅选择与整行完全匹配的匹配项。”。
在您的示例中,尝试:grep -x "a b c"