30

我有一些复杂的日志文件,我需要编写一些工具来处理它们。我一直在玩 awk,但我不确定 awk 是否是正确的工具。

我的日志文件是 OSPF 协议解码的打印输出,其中包含各种协议 pkts 及其内容的文本日志,以及用它们的值标识的各种协议字段。我想处理这些文件并仅打印出与特定 pkts 相关的某些日志行。每个 pkt 日志可以包含该 pkt 条目的不同数量的行。

awk 似乎能够处理与模式匹配的单行。我可以找到所需的 pkt,但随后我需要匹配后面几行中的模式,以确定它是否是我要打印的 pkt。

另一种看待这个问题的方法是,我想隔离日志文件中的几行,并根据多行上的模式匹配打印出作为特定 pkt 详细信息的那些行。

由于 awk 似乎是基于行的,我不确定这是否是最好的工具。

如果awk可以做到这一点,它是如何做到的?如果没有,关于使用哪种工具的任何建议?

4

6 回答 6

25

awk 可以轻松检测模式的多行组合,但您需要在代码中创建所谓的状态机来识别序列。

考虑这个输入:

how
second half #1
now
first half
second half #2
brown
second half #3
cow

如您所见,识别单一模式很容易。现在,我们可以编写一个 awk 程序,该程序仅在前半行直接位于前半行之前识别后半部分。(使用更复杂的状态机,您可以检测任意模式序列。)

/second half/ {
  if(lastLine == "first half") {
    print
  }
}

{ lastLine = $0 }

如果你运行它,你会看到:

second half #2

现在,这个例子非常简单,只是一个状态机。有趣的状态仅持续if语句的持续时间,并且前面的状态是隐含的,取决于lastLine 的值。在更规范的状态机中,您将保留显式状态变量并根据现有状态和当前输入从状态到状态转换。但是你可能不需要那么多的控制机制。

于 2013-01-16T03:29:04.570 回答
14

awk 能够处理从开始模式到结束模式

/start-pattern/,/end-pattern/ {
  print
}

我正在寻找如何匹配

 * Implements hook_entity_info_alter().
 */
function file_test_entity_type_alter(&$entity_types) {

如此创造

/\* Implements hook_/,/function / {
  print
}

我需要的内容。一个更复杂的例子是跳过行并擦掉非空格部分。注意 awk 是一个记录(行)和单词(空格分割)的工具。

# start,end pattern match using comma
/ \* Implements hook_(.*?)\./,/function (.\S*?)/ {
  # skip PHP multi line comment end
  $0 ~ / \*\// skip

  # Only print 3rd word
  if ($0 ~ /Implements/) {
    hook=$3
    # scrub of opening parenthesis and following.
    sub(/\(.*$/, "", hook)
    print hook
  }

  # Only print function name without parenthesis
  if ($0 ~ /function/) {
    name=$2

    # scrub of opening parenthesis and following.
    sub(/\(.*$/, "", name)

    print name
    print ""
  }
}

希望这也有帮助。

另见ftp://ftp.gnu.org/old-gnu/Manuals/gawk-3.0.3/html_chapter/gawk_toc.html

于 2017-06-14T14:35:43.540 回答
12

awk 确实是基于记录的。默认情况下,它将一行视为一条记录,但您可以使用 RS(记录分隔符)变量对其进行更改。

解决此问题的一种方法是使用 sed 进行第一次传递(如果您愿意,也可以使用 awk 执行此操作),以使用不同的字符(如换页)分隔记录。然后,您可以编写 awk 脚本,它将这组行视为一条记录。

例如,如果这是您的数据:

animal 0
name: joe
type: dog
animal 1
name: bill
type: cat
animal 2
name: ed
type: cat

要使用换页符分隔记录:

$ cat data | sed $'s|^\(animal.*\)|\f\\1|'

现在我们将接受它并通过 awk 传递它。这是有条件地打印记录的示例:

$ cat data | sed $'s|^\(animal.*\)|\f\\1|' | awk '
      BEGIN { RS="\f" }                                     
      /type: cat/ { print }'

输出:

animal 1
name: bill
type: cat

animal 2
name: ed
type: cat

编辑:作为奖励,这里是使用 awk-ward ruby​​ 的方法(-014 表示使用换页符(八进制代码 014)作为记录分隔符):

$ cat data | sed $'s|^\(animal.*\)|\f\\1|' |
      ruby -014 -ne 'print if /type: cat/'
于 2013-01-16T04:00:10.637 回答
3

我不时对 sendmail 日志做这种事情。

鉴于:

Jan 15 22:34:39 mail sm-mta[36383]: r0B8xkuT048547: to=<www@web3>, delay=4+18:34:53, xdelay=00:00:00, mailer=esmtp, pri=21092363, relay=web3., dsn=4.0.0, stat=Deferred: Operation timed out with web3.
Jan 15 22:34:39 mail sm-mta[36383]: r0B8hpoV047895: to=<www@web3>, delay=4+18:49:22, xdelay=00:00:00, mailer=esmtp, pri=21092556, relay=web3., dsn=4.0.0, stat=Deferred: Operation timed out with web3.
Jan 15 22:34:51 mail sm-mta[36719]: r0G3Youh036719: from=<obfTaIX3@nickhearn.com>, size=0, class=0, nrcpts=0, proto=ESMTP, daemon=IPv4, relay=[50.71.152.178]
Jan 15 22:35:04 mail sm-mta[36722]: r0G3Z2SF036722: lost input channel from [190.107.98.82] to IPv4 after rcpt
Jan 15 22:35:04 mail sm-mta[36722]: r0G3Z2SF036722: from=<amahrroc@europe.com>, size=0, class=0, nrcpts=0, proto=SMTP, daemon=IPv4, relay=[190.107.98.82]
Jan 15 22:35:36 mail sm-mta[36728]: r0G3ZXiX036728: lost input channel from ABTS-TN-dynamic-237.104.174.122.airtelbroadband.in [122.174.104.237] (may be forged) to IPv4 after rcpt
Jan 15 22:35:36 mail sm-mta[36728]: r0G3ZXiX036728: from=<clunch.hilarymas@javagame.ru>, size=0, class=0, nrcpts=0, proto=SMTP, daemon=IPv4, relay=ABTS-TN-dynamic-237.104.174.122.airtelbroadband.in [122.174.104.237] (may be forged)

我使用这样的脚本:

#!/usr/bin/awk -f

BEGIN {
  search=ARGV[1];  # Grab the first command line option
  delete ARGV[1];  # Delete it so it won't be considered a file
}

# First, store every line in an array keyed on the Queue ID.
# Obviously, this only works for smallish log segments, as it uses up memory.
{
  line[$6]=sprintf("%s\n%s", line[$6], $0);
}

# Next, keep a record of Queue IDs with substrings that match our search string.
index($0, search) {
  show[$6];
}

# Finally, once we've processed all input data, walk through our array of "found"
# Queue IDs, and print the corresponding records from the storage array.
END {
  for(qid in show) {
    print line[qid];
  }
}

获得以下输出:

$ mqsearch airtel /var/log/maillog

Jan 15 22:35:36 mail sm-mta[36728]: r0G3ZXiX036728: lost input channel from ABTS-TN-dynamic-237.104.174.122.airtelbroadband.in [122.174.104.237] (may be forged) to IPv4 after rcpt
Jan 15 22:35:36 mail sm-mta[36728]: r0G3ZXiX036728: from=<clunch.hilarymas@javagame.ru>, size=0, class=0, nrcpts=0, proto=SMTP, daemon=IPv4, relay=ABTS-TN-dynamic-237.104.174.122.airtelbroadband.in [122.174.104.237] (may be forged)

这里的想法是我正在打印与我要搜索的字符串的 Sendmail 队列 ID 匹配的所有行。代码的结构当然是日志文件结构的产物,因此您需要针对要分析和提取的数据自定义解决方案。

于 2013-01-16T03:44:07.717 回答
1
awk '/pattern-start/,/pattern-end/'

参考

于 2013-01-16T03:28:37.553 回答
1
`pcregrep -M` works pretty well for this.

来自 pcregrep(1):

-M,--多行

允许模式匹配多于一行。当给出此选项时,模式可能有用地包含文字换行符和内部出现的 ^ 和 $ 字符。成功匹配的输出可能包含多行,最后一行是匹配结束的那一行。如果匹配的字符串以换行序列结束,则输出在该行的末尾结束。

设置此选项时,PCRE 库以“多行”模式调用。可以匹配的行数是有限制的,这是由 pcregrep 在扫描输入文件时缓冲输入文件的方式所施加的。但是,pcgrep 确保至少 8K 字符或文档的其余部分(以较短者为准)可用于前向匹配,并且类似地,前面的 8K 字符(或所有前面的字符,如果少于 8K)保证可用对于后向断言。此选项在逐行读取输入时不起作用(请参阅 --line-buffered。)

于 2013-01-16T03:38:28.773 回答