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
}