bash:在一个文件中查找模式,并为找到的每个模式应用一些代码

时间:2015-02-16 10:05:07

标签: bash shell unix awk

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

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

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

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

离。我使用awk搜索模式“health”并在其后面打印6行:

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

我想对模式“80”做同样的事情,这次在它之前打印5行,在此之后打印一行......只有当$ 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

提前感谢您的帮助

3 个答案:

答案 0 :(得分:2)

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

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-->0对于下一行为真,从而导致它被打印。

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

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

答案 1 :(得分:1)

不幸的是,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

答案 2 :(得分:1)

我认为使用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}

如果当前记录编号(在此文件中)介于此次出现之间,则打印该行。如果模式的出现位置靠近,则会阻止多次打印该行。

file{,}

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