【问题标题】:bash: find pattern in one file and apply some code for each pattern foundbash:在一个文件中查找模式并为找到的每个模式应用一些代码
【发布时间】:2015-02-16 19:30:07
【问题描述】:

我创建了一个脚本,它会自动登录到路由器并检查当前的 CPU 负载,如果负载超过某个阈值,我需要它将当前 CPU 值打印到标准输出。

我想在脚本 o/p 中搜索某个模式(在这种情况下为值 80,这是高 CPU 负载的阈值),然后对于该模式的每个实例,它将检查当前值是否大于80 与否,如果为 true,则在模式前打印 5 行,然后是模式的当前行。

问题1:如何遍历模式的每个实例并分别在每个实例上应用一些代码?

问题2:如何在模式前打印n行,在模式后打印x行?

例如。我使用 awk 搜索模式“健康”并在其后打印 6 行,如下所示:

 awk '/health/{x=NR+6}(NR<=x){print}' ./logs/CpuCheck.log

我想对模式“80”做同样的事情,这次在它之前打印 5 行,在它之后打印 1 行....仅当 $3(表示当前 CPU 负载)超过值 80 时

下面是自动登录脚本的输出(文件名:CpuCheck.log)

ABCD-> show health xxxxxxxxxx
* - current value exceeds threshold

                                1 Min  1 Hr  1 Hr
Cpu                Limit   Curr   Avg    Avg   Max
-----------------+-------+------+------+-----+----
01                   80     39     36    36    47

WXYZ-> show health xxxxxxxxxx
* - current value exceeds threshold

                                1 Min  1 Hr  1 Hr
Cpu                Limit   Curr   Avg    Avg   Max
-----------------+-------+------+------+-----+----
01                   80     29     31    31    43

提前感谢您的帮助

【问题讨论】:

  • awk 当然可以。另一种方法是使用grep-A 标志控制将显示匹配之后的行数和-B之前的行数。
  • 在 awk 中最简单的方法是读取文件两次。在第一个循环中设置标记,然后在第二个循环中的标记之间打印

标签: bash shell unix awk


【解决方案1】:

您可以使用 -B-A 并切换到 grep,而不是使用 awk,它会在匹配模式之前和之后打印多行:

grep -E -B 5 -A 1 '^[0-9]+[[:space:]]+80[[:space:]]+(100|9[0-9]|8[1-9])' CpuCheck.log

该模式匹配以一些数字开头的行,然后是空格,然后是 80,然后是介于 81 和 100 之间的更大的数字。-E 开关启用扩展正则表达式 (ERE),如果您需要,则需要使用+ 字符表示“一个或多个”。如果您的 grep 版本不支持 ERE,您可以改用稍微冗长的 \{1,\} 语法:

grep -B 5 -A 1 '^[0-9]\{1,\}[[:space:]]\{1,\}80[[:space:]]\{1,\}\(100\|9[0-9]\|8[1-9]\)' CpuCheck.log

如果 grep 不是一个选项,另一种选择是使用 awk。最简单的方法是将所有行存储在缓冲区中:

awk 'f-->0;{a[NR]=$0}/^[0-9]+[[:space:]]+80[[:space:]]+(100|9[0-9]|8[1-9])/{for(i=NR-5;i<=NR;++i)print i, a[i];f=1}'

这会将每一行存储在数组a 中。当第三列大于 80 时,它会打印数组中的前 5 行。它还将标志f 设置为1,以便f--&gt;0 对下一行为真,从而使其被打印。

最初我选择比较 $3&gt;80 而不是正则表达式,但由于行的格式不同,这不是一个好主意。

如果日志文件真的很大,这意味着将整个内容读入内存是不可行的,您可以实现一个循环缓冲区,以便只存储前 5 行,或者读取文件两次。

【讨论】:

  • 很遗憾我的 grep 版本不支持 -B 或 -A 选项。
  • @tom 你建议如何打印以下行呢?
  • @TomFenech 如果您也使用 f ,那么它将多次打印重叠行
  • @JID 你是对的,尽管根据示例输入看起来这不是问题。
  • @A.Midany 如果您的问题解决了,别忘了接受我的回答。谢谢
【解决方案2】:

不幸的是,awk 是面向流的,没有简单的方法来获取当前行之前的行。但这并不意味着不可能:

awk '
    BEGIN {
        bufferSize = 6;
    }
    {
        buffer[NR % bufferSize] = $0;
    }
    $2 == 80 && $3 > 80 {
        # print the five lines before the match and the line with the match
        for (i = 1; i <= bufferSize; i++) {
            print buffer[(NR + i) % bufferSize];
        }
    }
' ./logs/CpuCheck.log

【讨论】:

  • 它对我来说非常有效......我修改了阈值以检查是否有不止一行与条件匹配并且我有所需的输出
【解决方案3】:

我认为使用 awk 最简单的方法是读取文件。 这应该基本上使用 0 内存,除了用于存储行号的内存。

如果只有一次出现

awk 'NR==FNR&&$2=="80"{to=NR+1;from=NR-5}NR!=FNR&&FNR<=to&&FNR>=from' file{,}

如果出现不止一次

awk 'NR==FNR&&$2=="80"{to[++x]=NR+1;from[x]=NR-5}
     NR!=FNR{for(i in to)if(FNR<=to[i]&&FNR>=from[i]){print;next}}' file{,}

输入/输出

Input

1
2
3
4
5
6
7
8
9
10
11
12
01                   80     39     36    36    47
13
14
15
16
17
01                   80     39     36    36    47
18
19
20

Output

8
9
10
11
12
01                   80     39     36    36    47
13
14
15
16
17
01                   80     39     36    36    47
18

工作原理

NR==FNR&&$2=="80"{to[++x]=NR+5;from[x]=NR-5}

如果第二个字段为 80,则在第一个文件中设置为记录号 + 或 - 任何你想要的。 增加出现变量x。

NR!=FNR

在第二个文件中

for(i in to)

每次出现

if(FNR<=to[i]&&FNR>=from[i]){print;next}

如果当前记录编号(在此文件中)介于此之间,则打印该行。如果该模式的出现非常接近,Next 将防止该行被多次打印。

file{,}

将文件作为两个参数使用两次。 {,} 扩展为 file file

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-17
    • 2013-10-23
    • 2013-08-24
    • 1970-01-01
    • 2018-10-26
    • 2015-03-26
    • 1970-01-01
    相关资源
    最近更新 更多