SED和GREP显示不同的结果

时间:2017-04-27 16:34:09

标签: regex sed grep

我试图从Apache日志中获取特定时间范围内的请求数量。我虽然使用sed很容易做到这一点但是当我尝试对grep做同样的事情时,我意识到grep显示的结果比sed更多。

这是我使用的grep命令:

#grep '26/Apr/2017:08:0[0-2]:[0-2][0-4]' access.log 

10.51.32.104 - - [26/Apr/2017:08:00:21 +0100] "GET / HTTP/1.1" 301 762 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
10.51.32.104 - - [26/Apr/2017:08:00:22 +0100] "GET /index.php?action=Login&module=Users HTTP/1.1" 200 6591 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
172.30.180.113 - - [26/Apr/2017:08:02:04 +0100] "GET / HTTP/1.0" 301 1906 "-" "Mozilla/4.0 (compatible; ipMonitor 10.7)"
172.30.180.113 - - [26/Apr/2017:08:02:04 +0100] "GET /index.php?action=Login&module=Users HTTP/1.0" 200 21951 "-" "Mozilla/4.0 (compatible; ipMonitor 10.7)"

这里是sed命令:

#sed -n '/26\/Apr\/2017:08:00:21/ , /26\/Apr\/2017:08:02:04/p' access.log

10.51.32.104 - - [26/Apr/2017:08:00:21 +0100] "GET / HTTP/1.1" 301 762 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
10.51.32.104 - - [26/Apr/2017:08:00:22 +0100] "GET /index.php?action=Login&module=Users HTTP/1.1" 200 6591 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
172.30.180.113 - - [26/Apr/2017:08:02:04 +0100] "GET / HTTP/1.0" 301 1906 "-" "Mozilla/4.0 (compatible; ipMonitor 10.7)"

所以,正如您所看到的那样,它缺少172.30.180.113中与该模式匹配的一个访问权。

我做错了什么? sed中的任何其他参数是否有帮助,或者有更好的方法吗?

3 个答案:

答案 0 :(得分:3)

您非常接近使用sed解决问题。这是一个良好的开端,我会鼓励你走这条路。

当然你可以使用regex,但它有其局限性。考虑范围08:0009:59,正则表达式很简单0[89]:[0-5][09]。但如果范围为08:4509:30,则regex将不是您的朋友。因此,我鼓励您尝试使用该范围。

您使用sed看到的限制是符合结束范围,sed已停止处理。但我们知道会有更多的线落在最终范围内。

sed -n '/26\/Apr\/2017:08:00:21/,/26\/Apr\/2017:08:02:04/{p;b};/26\/Apr\/2017:08:02:04/p' access.log

分解sed命令:

/26\/Apr\/2017:08:00:21/,/26\/Apr\/2017:08:02:04/{p;b}

如果在范围内,则p会对该行进行处理,然后b ranch到sed命令的末尾。

/26\/Apr\/2017:08:02:04/p

只有在前一个sed命令的范围之外,才会执行此操作。这将处理范围内的额外行,但不在sed的范围内。

awk可以使用相同的技术。

awk '/26\/Apr\/2017:08:00:21/,/26\/Apr\/2017:08:02:04/{a=NR;print};a!=NR && /26\/Apr\/2017:08:02:04/{print}' access.log

第一个命令:

/26\/Apr\/2017:08:00:21/,/26\/Apr\/2017:08:02:04/{a=NR;print}

将打印范围内的行并将变量a设置为NR(当前记录号)的值。

第二个命令:

a!=NR && /26\/Apr\/2017:08:02:04/{print}

将打印范围内的其余行,但awk被视为超出范围。

答案 1 :(得分:1)

正如评论中所提到的,您正在搜索一系列表达式,sed将匹配从开头的第一个匹配到结束的第一个匹配的所有行。作为一种语言,awk提供了比sed更多的灵活性:

start=26/Apr/2017:08:00:21
end=26/Apr/2017:08:02:04
awk -v "s=$start" -v "e=$end" '$0~s{m=1} $0~e{m=0; f=1; print} f&&$0!~e{exit} m' access.log

我们有4个条件块。首先,我们在开始时检查匹配并设置m。然后我们检查最后的匹配并取消设置m,设置f,然后继续打印。下一次检查是f,只要最后没有匹配。这表明我们已经完成了结束字符串的所有匹配并且可以退出。最后一个块检查是否设置了m,如果是,则打印。

同一程序的更详细版本:

awk -v "start_date=$start" -v "end_date=$end" '
{
    if ($0 ~ start_date) {
        matching = 1;
    }
    else if ($0 ~ end_date) {
        matching = 0;
        finishing = 1;
        print $0;
    }
    else if (finishing) {
        exit;
    }
    if (matching) {
        print $0;
    }
}
' access.log

感谢@alvits在评论中击败我,直到我找到了更好的解决方案!

答案 2 :(得分:1)

是的,有更好的方法(我在底部提到)。由于StackOverflow的建议不合适,我只是回答一下你所提供的代码中发生了什么。

您的grep命令会打印与您指定的正则表达式匹配的每一行输入。虽然这有效,但有时很难在正则表达式中指定范围。 (您如何指定1月10日至3月2日的范围?)

sed命令可能有点复杂。请考虑以下事项:

$ sed -n -e '/re/p'

这将打印与正则表达式re匹配的所有行。基本上与grep相同。

$ sed -n -e '/re1/,/re2/p'

这将打印从re1的第一个匹配开始并以re2的第一个匹配结束的所有行。这就是你问题中的sed脚本正在做的事情。请注意,这也有可能打印出与正则表达式之一不匹配的行:

$ printf 'one\ntwo\nthree\nfour\n' | sed -ne '/one/,/three/p'
one
two
three

如果您想使用sed提取日志中的行数,我建议采用其他方法。虽然sed非常适合模式匹配,但它并不提供可以解释日期的工具。 Perl,或gawk,甚至bash将提供更多功能,并且在您需要更改代码的六个月后更容易理解/调试。