Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 两次grep还是一次使用正则表达式更有效?_Bash_Unix_Grep - Fatal编程技术网

Bash 两次grep还是一次使用正则表达式更有效?

Bash 两次grep还是一次使用正则表达式更有效?,bash,unix,grep,Bash,Unix,Grep,我正试图解析几个2gb+文件,并希望在几个级别上进行grep 假设我要获取包含“foo”的行和包含“bar”的行 我可以做grep foo file.log | grep bar,但我担心运行两次会很昂贵 使用像grep-E'(foo.*bar.*foo)这样的东西会有好处吗?grep-E'(foo.*bar)会找到包含“foo”或“bar”的行 您需要同时包含“foo”和“bar”的行。这两个命令中的任何一个都可以执行以下操作: sed '/foo/!d;/bar/!d' file.log

我正试图解析几个2gb+文件,并希望在几个级别上进行grep

假设我要获取包含“foo”的行和包含“bar”的行

我可以做
grep foo file.log | grep bar
,但我担心运行两次会很昂贵

使用像
grep-E'(foo.*bar.*foo)
这样的东西会有好处吗?

grep-E'(foo.*bar)
会找到包含“foo”或“bar”的行

您需要同时包含“foo”和“bar”的行。这两个命令中的任何一个都可以执行以下操作:

sed '/foo/!d;/bar/!d' file.log

awk '/foo/ && /bar/' file.log
理论上,这两个命令应该比您的
cat | grep | grep
构造更有效,因为:


  • sed
    awk
    都执行自己的文件读取;不需要架空管道
  • 上面我给
    sed
    awk
    的“程序”使用布尔短路来快速跳过不包含“foo”的行,因此只测试包含“foo”的行到/bar/regex

但是,我还没有测试它们。YMMV:)

这两种操作根本不同。这个:

cat file.log | grep foo | grep bar
在file.log中查找foo,然后在最后的grep输出中查找bar。而
cat file.log | grep-E'(foo | bar)
在file.log中查找foo或bar。输出应该非常不同。使用你需要的任何行为


至于效率,它们并不具有可比性,因为它们做的事情不同。但两者都应该足够快。

理论上,最快的方法应该是:

grep -E '(foo.*bar|bar.*foo)' file.log
原因有几个:首先,grep直接从文件中读取,而不是添加让cat读取并将其塞入管道中供grep读取的步骤。其次,它只使用一个grep实例,因此文件的每一行只需处理一次。第三,
grep-E
在大文件上通常比普通grep快(但在小文件上慢),尽管这取决于grep的实现。最后,grep(在其所有变体中)针对字符串搜索进行了优化,而sed和awk是碰巧能够搜索的通用工具(但没有针对它进行优化)。

如果您这样做:

cat file.log | grep foo | grep bar
您只能以任意顺序打印同时包含
foo
bar
的行。如果这是您的意图:

grep -e "foo.*bar" -e "bar.*foo" file.log
将更有效,因为我只需要解析一次输出

注意,我不需要
cat
,它本身效率更高。除非是压缩文件(这是命令的目的),否则很少需要
cat
。99%的情况下,您可以将文件名添加到管道中第一个命令的末尾,或者如果您有一个类似
tr
的命令不允许您使用文件,则始终可以像这样重定向输入:

tr `a-z` `A-Z` < $fileName
grep
不同,
fgrep
不解析正则表达式,这意味着它可以更快地解析行。试试这个:

time fgrep "foo" file.log | fgrep "bar"


看看哪个更快。

我可以也会尝试,但我很好奇别人是否有想法或见解。另外,我想问我的第一个问题,哈哈。这不是一个编程问题,是吗?grep-E’(foo | bar)并不等同于你的管道grep。你真的想要foo和bar吗?
grep-E'(foo | bar)
不匹配既有
foo
又有
bar
的行吗?啊,对。我知道regex错误。老实说,我没怎么想,只是想抓住重点@Gabe,我确实认为这是一个编程问题,尽管我能理解为什么你不相信。有趣的想法。最后,两个greps以26秒到31秒的成绩获胜。我知道这些不是很多,但我很好奇下一次我碰巧有更大的日志文件。因为教我另外两种方法而被接受。@dtbarne
cat | grep | grep
获胜?meh,我关于“sed/awk应该更快”的理论已经过时了:-/我不知道awk,但我的经验是sed比它需要的慢得多,而grep是相当好的优化。sed和
awk
都是编程语言,都不是非常优化的。您还必须记住,第一个
grep
可能需要解析数百行,而第二个
grep
可能只需要解析十几行,因此双grep并不像您想象的那样效率低下。通过使用
fgrep
摆脱正则表达式解析器可能更有效。虽然使用单个grep的优点是只解析输入一次,但正则表达式并不简单。另一方面,两个管道grep使用更简单的表达式,匹配速度更快。正如David W.指出的,第二次调用需要处理更少的输入。不同的输入(例如,不同比例的匹配行)可能会产生相反的结果。与任何与性能相关的问题一样,在测量准确的用例之前,您永远不会真正知道什么是快速的——直觉可能会误导您。
time fgrep "foo" file.log | fgrep "bar"
time grep -e "foo.*bar" -e "bar.*foo" file.log