使用正则表达式和 grep 获取日志文件中所有唯一的模式实例
use regex and grep to get all unique instances of pattern in a log file
我需要从服务器的访问日志中获取正在访问服务器的唯一客户端计算机 names/ip 地址的列表。
目标日志行如下所示:
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".
在此示例中,字符串 (QWER-L1212-W6) [11.22.333.44]
将是客户端 computer/ip 地址的唯一实例的示例。
所以结果会是这样的:
(QWER-L1212-W6) [11.22.333.44]
(QWER-L1234-W7) [11.22.333.55]
etc...
我试过了没有成功:
grep --only-matching '\(.+\) \[.+\]' | sort --unique Access.log
匹配失败,返回整行日志。
请注意,您使用的是 POSIX BRE 正则表达式风格,因为您没有通过 -E
/-r
或 -P
选项来更改默认的正则表达式风格. \(...\)
在 POSIX BRE 中定义了一个捕获组。不过这里还有更多问题。
你需要使用
grep -o '([^()]*) \[[^][]*]' Access.log | sort -u
注意 grep
的输入文件参数的位置。
这里的 ([^()]*) \[[^][]*]
是匹配
的 POSIX BRE 模式
(
- 文字 (
字符(\(
是捕获组的开始)
[^()]*
- (
和 )
以外的零个或多个字符
)
- 文字 )
字符(\)
是捕获组的结尾)
- 一个space
\[
- 一个 [
字符
[^][]*
- [
和 ]
以外的零个或多个字符
]
- 一个 ]
字符。
参见 online demo:
#!/bin/bash
s='2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".'
grep -o '([^()]*) \[[^][]*]' <<< "$s" | sort -u
# => (QWER-L1212-W6) [11.22.333.44]
grep --only-matching '\(.+\) \[.+\]' file.log
这是失败的,因为您没有在 grep
中使用 ERE(扩展正则表达式或 -E
)并且 +
没有被转义。因此,对于您的情况,以下可能有效:
grep -E --only-matching '\(.+\) \[.+\]' file.log
然而,这个正则表达式是有问题的,因为 .+
将在匹配结束 )
和结束 ]
之前匹配任何字符的 1+。如果您的日志中有 (...) [...]
个子字符串,如下所示:
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [21.22.333.33]" opening database "databasename" as "username" (QWER-L1234-W7) [11.22.333.55]
那么您将得到不正确的结果。 不正确的结果也会显示为 '([^()]*) \[[^][]*]'
。
由于您使用的是 access.log
,其中字段的格式和位置是固定的,因此使用 awk
进行此提取更加安全和高效:
awk -F '"' '{sub(/^[^ ]* /, "", ); print }' file.log
(QWER-L1212-W6) [11.22.333.44]
(QWER-L1212-W6) [21.22.333.33]
我需要从服务器的访问日志中获取正在访问服务器的唯一客户端计算机 names/ip 地址的列表。
目标日志行如下所示:
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".
在此示例中,字符串 (QWER-L1212-W6) [11.22.333.44]
将是客户端 computer/ip 地址的唯一实例的示例。
所以结果会是这样的:
(QWER-L1212-W6) [11.22.333.44]
(QWER-L1234-W7) [11.22.333.55]
etc...
我试过了没有成功:
grep --only-matching '\(.+\) \[.+\]' | sort --unique Access.log
匹配失败,返回整行日志。
请注意,您使用的是 POSIX BRE 正则表达式风格,因为您没有通过 -E
/-r
或 -P
选项来更改默认的正则表达式风格. \(...\)
在 POSIX BRE 中定义了一个捕获组。不过这里还有更多问题。
你需要使用
grep -o '([^()]*) \[[^][]*]' Access.log | sort -u
注意 grep
的输入文件参数的位置。
这里的 ([^()]*) \[[^][]*]
是匹配
(
- 文字(
字符(\(
是捕获组的开始)[^()]*
-(
和)
以外的零个或多个字符
)
- 文字)
字符(\)
是捕获组的结尾)\[
- 一个[
字符[^][]*
-[
和]
以外的零个或多个字符
]
- 一个]
字符。
参见 online demo:
#!/bin/bash
s='2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".'
grep -o '([^()]*) \[[^][]*]' <<< "$s" | sort -u
# => (QWER-L1212-W6) [11.22.333.44]
grep --only-matching '\(.+\) \[.+\]' file.log
这是失败的,因为您没有在 grep
中使用 ERE(扩展正则表达式或 -E
)并且 +
没有被转义。因此,对于您的情况,以下可能有效:
grep -E --only-matching '\(.+\) \[.+\]' file.log
然而,这个正则表达式是有问题的,因为 .+
将在匹配结束 )
和结束 ]
之前匹配任何字符的 1+。如果您的日志中有 (...) [...]
个子字符串,如下所示:
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [11.22.333.44]" opening database "databasename" as "username".
2020-11-17 15:34:04.208 -0500 Information 94 XYZ-ASDF-FMP123 Client "%USERNAME% (QWER-L1212-W6) [21.22.333.33]" opening database "databasename" as "username" (QWER-L1234-W7) [11.22.333.55]
那么您将得到不正确的结果。 不正确的结果也会显示为 '([^()]*) \[[^][]*]'
。
由于您使用的是 access.log
,其中字段的格式和位置是固定的,因此使用 awk
进行此提取更加安全和高效:
awk -F '"' '{sub(/^[^ ]* /, "", ); print }' file.log
(QWER-L1212-W6) [11.22.333.44]
(QWER-L1212-W6) [21.22.333.33]