簡體   English   中英

awk模式可以匹配多行嗎?

[英]Can awk patterns match multiple lines?

我有一些復雜的日志文件,需要編寫一些工具來處理它們。 我一直在玩awk,但不確定awk是否適合此工具。

我的日志文件是OSPF協議解碼的打印輸出,其中包含各種協議pkts及其內容的文本日志,以及用值標識的各種協議字段。 我想處理這些文件並僅打印出與特定pkts有關的日志的某些行。 每個pkt日志可以包含該pkt條目的不同行數。

awk似乎能夠處理與模式匹配的一行。 我可以找到所需的pkt,但是接下來我需要在后面的行中匹配模式,以確定它是否是我要打印的pkt。

另一種看待這種情況的方式是,我想隔離日志文件中的幾行,並根據幾行上的模式匹配來打印出這些行,這些行是特定pkt的詳細信息。

由於awk似乎是基於行的,因此我不確定這是否是最好的工具。

如果awk可以做到這一點,怎么做? 如果沒有,關於使用哪種工具的任何建議?

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的值 在更規范的狀態機中,您將保留一個顯式狀態變量,並根據現有狀態和當前輸入從一個狀態轉換到另一個狀態。 但是您可能不需要那么多的控制機制。

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/'

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

我經常用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匹配的所有行。 代碼的結構當然是日志文件結構的產物,因此您需要針對要分析和提取的數據自定義解決方案。

`pcregrep -M` works pretty well for this.

從pcregrep(1):

-M,-多行

允許模式匹配多行。 給出此選項后,模式可能會有用地包含文字換行符以及內部出現的^和$字符。 成功匹配的輸出可能包含多行,最后一行是匹配結束的那一行。 如果匹配的字符串以換行符結尾,則輸出在該行的結尾處結束。

設置此選項后,將以“多行”模式調用PCRE庫。 pcregrep在掃描輸入文件時對其進行緩沖的方式對可匹配的行數進行了限制。 但是,pcregrep確保至少有8K個字符或文檔的其余部分(以較短者為准)可用於正向匹配,並且類似地,保證前8K個字符(或所有先前的字符,如果少於8K)也可用。對於后置斷言。 當逐行讀取輸入時,此選項不起作用(請參閱--line-buffered。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM