awk查找记录中的所有匹配项,但仅限于这些行中的某些字段

awk查找记录中的所有匹配项,但仅限于这些行中的某些字段,awk,Awk,我有一张这样的记录: interface Vlan5 description customerA ip address 1.1.1.1 255.255.255.0 ip address 2.2.2.1 255.255.255.0 ip address 3.3.3.1 255.255.255.0 no ip redirects no ip unreachables no ip proxy-arp standby delay reload 90 standby 9 ip 1.1.1

我有一张这样的记录:

interface Vlan5
 description customerA
 ip address 1.1.1.1 255.255.255.0
 ip address 2.2.2.1 255.255.255.0
 ip address 3.3.3.1 255.255.255.0
 no ip redirects
 no ip unreachables
 no ip proxy-arp
 standby delay reload 90
 standby 9 ip 1.1.1.1
 standby 9 ip 2.2.2.1 secondary
 standby 9 ip 3.3.3.1 secondary
 standby 9 timers 2 6
 standby 9 preempt delay minimum 60
我需要找到“ip地址”的所有匹配项,并从这些行中获取第3和第4个字段(ip地址)。我已经找到了它,所以我正在查看正确的记录,但无法找出如何在给定记录中的字段上返回多个匹配项,这些字段的位置会像这样变化。这是一个不起作用的代码片段,它让我返回“ip地址”,但我不确定如何获取后面的两个字段(或者这是否是最好的方法)

我还尝试将RS重置为新的系列,希望它能以嵌套方式工作,但看起来不是这样。下面修改的示例打印上面第一个示例中的字段3和4,而不是1.1.1.1 255.255.255.0

if ( seenvlan == custvlan )
    RS="\n"; print "this is seenvlan " seenvlan
    if ( $1 ~ ip && $2 ~ address )
        print $3 " " $4
对于整个块,您可以使用带有换行符的
split()
,对于每一行
split()
再次提取字段并检查前两个字段,如:

awk -v custvlan=$custvlan 'BEGIN { RS="!"; FS=" " }
{ if ( $1 ~ "interface" && $2 ~ "Vlan" )
    { seenvlan=gensub(/^Vlan/, "", "g", $2)
        if ( seenvlan == custvlan )
            split($0, lines, "\n")
            for (l in lines) {
                line = lines[l]
                split(line, fields, " ")
                if (fields[1] == "ip" && fields[2] == "address") {
                    print fields[3], fields[4]
                }
            }  
    }
}
END {
}' device-config-file
它产生:

1.1.1.1 255.255.255.0
2.2.2.1 255.255.255.0
3.3.3.1 255.255.255.0

无需使用
RS
即可执行此操作。例如,当bash中将
custvlan
设置为
5
时:

awk -v custvlan=$custvlan '/^interface/ { show=$2=="Vlan" custvlan ? 1 : 0 }
    show && /ip address/ { print $3,$4 }' device-config-file
1.1.1.1 255.255.255.0
2.2.2.1 255.255.255.0
3.3.3.1 255.255.255.0

通过在匹配
custvlan
变量时设置
show
变量来创建虚拟“记录”,并且仅在设置
show
时从
ip地址行打印数据。

另一种解决方案,使用
RS=“!”
来识别块(正如上面给出的另一个答案,我假设使用Cisco IOS配置格式)和
FS=“\n”
将每个块拆分为行。测试
接口VlanX
custvlan
应在命令行中),然后依次循环通过块中的每一行,并在空格上拆分以
ip地址
开头的行。字段编号和数组索引针对空字段进行了调整。为了提高效率,它假设只有一个这样的块,因此一旦发现它就会退出

在Mac OS X 10.9上使用
/usr/bin/awk
gawk
mawk
进行测试

    BEGIN {
        RS = "!"
        FS = "\n"
    }

    $2 ~ "^ *interface Vlan" custvlan {
        for (i=2; i<=NF; ++i) {
            if ($i ~ /^ *ip address /) {
                split($i, a, / */)
                print a[4], a[5]
            }
        }
        exit 0
    }
用法:

    script.awk custvlan=5 sample.txt
    => 1.1.1.1 255.255.255.0
    => 2.2.2.1 255.255.255.0
    => 3.3.3.1 255.255.255.0

    script.awk custvlan=6 sample.txt
    => 4.4.4.4 255.255.255.0
    => 5.5.5.1 255.255.255.0
    => 6.6.6.1 255.255.255.0
一个更健壮的解决方案(例如,防止误用的
字符,例如在描述中)如下所示,需要
mawk
gawk
,它们支持
RS
中的正则表达式。用法相同:

    BEGIN {
        RS = "\n( *!\n)+"
        FS = "\n"
    }

    $1 ~ "^ *interface Vlan" custvlan {
        for (i = 2; i <= NF; ++i) {
            if ($i ~ /^ *ip address /) {
                split($i, a, / */)
                print a[4], a[5]
            }
        }
        exit 0
    }
开始{
RS=“\n(*!\n)+”
FS=“\n”
}
$1~“^*接口Vlan”custvlan{

对于(i=2;我实际上从输入数据中取出了前导空格,当我把它放回去时,gawk仍然工作,我对此感到困惑。如果你看到
地址1.1.1…
,那么在print语句中使用
$4,$5
来对齐字段。这取决于前导空格是否被视为分隔符。默认
F在GNU awk中,似乎忽略了它,而设置
FS=“[[:space]]”
强制解析前导空间并要求移位。感谢您的回答。请您在“{show=$2==“Vlan”custvlan?1:0}”部分展开一点好吗?我想理解第一位,如果field2等于Vlan,请将show设置为Vlan“custvlan”。但是“.1:0”在这里做什么?当然。请这样想:
show
是一个“布尔值”,指示何时解析
/ip地址/
行,并设置在记录的开头(
/^interface/
行)变量
show
被设置为1或0,这取决于
2
是否匹配
Vlan
+使用awk的
custvlan
的值的串联字符串组合(在这种情况下使
Vlan5
)。更明显的情况是:
show=($2=“Vlan”custvlan)?1:0
。感谢您的建议,它非常有效。对于初学者来说,它也非常容易阅读和理解。感谢您的回复。这是解决此问题的另一个有用答案。
    script.awk custvlan=5 sample.txt
    => 1.1.1.1 255.255.255.0
    => 2.2.2.1 255.255.255.0
    => 3.3.3.1 255.255.255.0

    script.awk custvlan=6 sample.txt
    => 4.4.4.4 255.255.255.0
    => 5.5.5.1 255.255.255.0
    => 6.6.6.1 255.255.255.0
    BEGIN {
        RS = "\n( *!\n)+"
        FS = "\n"
    }

    $1 ~ "^ *interface Vlan" custvlan {
        for (i = 2; i <= NF; ++i) {
            if ($i ~ /^ *ip address /) {
                split($i, a, / */)
                print a[4], a[5]
            }
        }
        exit 0
    }