如何匹配然后替换 shell 脚本中的多行
How to match & then replace multiple lines in shell script
嗨,我有这个文本文件。
Physical interface: ge-0/0/3, Enabled, Physical link is Up
Interface index: 132, SNMP ifIndex: 504
Description: # SURVEILLANCE CAMERA #
Link-level type: Flexible-Ethernet, Media type: Copper, MTU: 9000,
LAN-PHY mode, Link-mode: Full-duplex, Speed: 1000mbps, BPDU Error: None,
.....few more lines
Physical interface: ge-0/1/0, Enabled, Physical link is Down
Interface index: 133, SNMP ifIndex: 505
Link-level type: Ethernet, Media type: Fiber, MTU: 1514, LAN-PHY mode,
Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
.....few more lines
Physical interface: ge-0/1/3, Enabled, Physical link is Up
Interface index: 136, SNMP ifIndex: 508
Description: # TO CSS_I-TN-CHNN-ENB-I099 #
Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 8000,
LAN-PHY mode, Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
... few more lines
and so on....
现在如果 Physical link 已启动并且 MTU 的值为 9000 那么我只需要将相应的两行都替换为。
<Pass>Physical interface: ge-0/0/3, Enabled, Physical link is Up
&
<Pass>Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 9000,
在所有其他情况下,它将 <Fail>
代替 <Pass>
。这些值位于不同的行中,这就是为什么我不知道使用 sed 或其他任何东西..请帮助...这是预期的输出..
<Pass>Physical interface: ge-0/0/3, Enabled, Physical link is Up
Interface index: 132, SNMP ifIndex: 504
Description: # SURVEILLANCE CAMERA #
<Pass>Link-level type: Flexible-Ethernet, Media type: Copper, MTU: 9000,
LAN-PHY mode, Link-mode: Full-duplex, Speed: 1000mbps, BPDU Error: None,
.....few more lines
<Fail>Physical interface: ge-0/1/0, Enabled, Physical link is Down
Interface index: 133, SNMP ifIndex: 505
<Fail>Link-level type: Ethernet, Media type: Fiber, MTU: 1514, LAN-PHY mode,
Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
.....few more lines
<Fail>Physical interface: ge-0/1/3, Enabled, Physical link is Up
Interface index: 136, SNMP ifIndex: 508
Description: # TO CSS_I-TN-CHNN-ENB-I099 #
<Fail>Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 8000,
LAN-PHY mode, Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
... few more lines
and so on....
使用 sed:
sed '/Physical link is/ { :a /MTU:/! { N; ba; }; /Physical link is Up.*MTU: 9000/ { s/\(.*\n\)\s*/<Pass><Pass>/; b; }; s/\(.*\n\)\s*/<Fail><Fail>/; }' filename
即:
/Physical link is/ { # Block start found
:a
/MTU:/! { # fetch lines until we find the MTU
N
ba
}
/Physical link is Up.*MTU: 9000/ { # If link is up and MTU 9000
s/\(.*\n\)[[:space:]]*/<Pass><Pass>/ # insert Pass markers
b
# we're done.
}
s/\(.*\n\)[[:space:]]*/<Fail><Fail>/ # otherwise insert Fail markers
}
请注意,对于 BSD sed,由于 b
指令,这不能用作单行代码。在那种情况下,将扩展的(没有注释,因为 BSD sed 很容易混淆)代码放在一个文件中,比如 foo.sed
,然后使用 sed -f foo.sed filename
。我已经用 POSIX 等价物 ([[:space:]]
) 替换了其他 GNU 主义 (\s
)。
要保留 MTU 行开头的空格,请删除 \s
或 [[:space:]]
。要在结果标记之前放置空格,请将 \s
或 [[:space:]]
放在捕获组内(即 \(.*\n\s*\)
)。
另请注意:这假设每个接口描述都有一个 MTU 字段。
或者,您可以试试这个 awk 脚本:
awk -v RS='Physical interface:' -F '\n' -v OFS='\n' '{ result = "<Fail>" } /Physical link is Up/ && /MTU: 9000/ { result = "<Pass>" } NR != 1 { for(i = 1; i <= NF; ++i) { if(index($i, "MTU:")) { sub(/^ */, result, $i) } } print result RS [=12=] }' filename
这会将文件拆分为 Physical interface:
处的记录,并将记录拆分为换行符处的字段。那么:
{ result = "<Fail>" } # result is Fail
/Physical link is Up/ && /MTU: 9000/ { # unless link is up and MTU 9000
result = "<Pass>"
}
NR != 1 { # the first record is the empty string
# before the first actual record, so
# we remove it.
for(i = 1; i <= NF; ++i) { # wade through the fields (lines)
if(index($i, "MTU:")) { # find the MTU line
sub(/^ */, result, $i) # put the marker there. To keep the
# whitespace, use $i = result $i
# instead, or sub(/^ */, "&" result, $i)
# to keep the spaces before the marker.
}
}
print result RS [=13=] # once done, print the whole shebang.
# We have to reinsert the record
# separator because it was removed
# by the splitting.
}
请注意,多字符 RS
并不严格符合 POSIX。不过,最常见的 awk(gawk 和 mawk)支持它。值得注意的是,BSD awk 没有。
尝试以下 awk
命令,该命令应 POSIX 兼容并保留前导空格:
awk '
/ Physical link is / { ++count }
/, MTU: / {
tag = (blockLines[1] ~ /Up$/ && [=10=] ~ /, MTU: 9000,/ ? "<Pass>" : "<Fail>")
sub(/^/, "&" tag, blockLines[1])
sub(/^ +/, "&" tag)
for (i = 1; i < count; ++i) print blockLines[i]
count = 0
}
count > 0 { blockLines[count++] = [=10=]; next }
{ print }
' file
基本思路是:
- 收集一个行块 - 必须标记的两行之间的所有行,包括必须标记的行 - 在数组中而不打印它们。
- 到达块末尾时,确定必须使用什么标记(失败或通过)
- 打印块中的所有行,并相应标记第一行和最后一行。
仅 Awk 脚本的注释版本:
/ Physical link is / { ++count } # Start of block
/, MTU: / { # End of block - fail/pass can now be determined
# Determine whether to apply a fail or a pass tag based on the
# first and last line in the block.
tag = (blockLines[1] ~ /Up$/ && [=11=] ~ /, MTU: 9000,/ ? "<Pass>" : "<Fail>")
# Prepend tag to 1st line in block
sub(/^/, "&" tag, blockLines[1])
# Prepend tag to last line in block, preserving leading whitespace.
sub(/^ +/, "&" tag)
# Print all lines in block (except for last one).
for (i = 1; i < count; ++i) print blockLines[i]
# Reset block line counter.
count = 0
}
# Inside block: collect lines, do not print yet.
count > 0 { blockLines[count++] = [=11=]; next }
# Print last line in block and lines outside of blocks.
{ print }
嗨,我有这个文本文件。
Physical interface: ge-0/0/3, Enabled, Physical link is Up
Interface index: 132, SNMP ifIndex: 504
Description: # SURVEILLANCE CAMERA #
Link-level type: Flexible-Ethernet, Media type: Copper, MTU: 9000,
LAN-PHY mode, Link-mode: Full-duplex, Speed: 1000mbps, BPDU Error: None,
.....few more lines
Physical interface: ge-0/1/0, Enabled, Physical link is Down
Interface index: 133, SNMP ifIndex: 505
Link-level type: Ethernet, Media type: Fiber, MTU: 1514, LAN-PHY mode,
Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
.....few more lines
Physical interface: ge-0/1/3, Enabled, Physical link is Up
Interface index: 136, SNMP ifIndex: 508
Description: # TO CSS_I-TN-CHNN-ENB-I099 #
Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 8000,
LAN-PHY mode, Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
... few more lines
and so on....
现在如果 Physical link 已启动并且 MTU 的值为 9000 那么我只需要将相应的两行都替换为。
<Pass>Physical interface: ge-0/0/3, Enabled, Physical link is Up
&
<Pass>Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 9000,
在所有其他情况下,它将 <Fail>
代替 <Pass>
。这些值位于不同的行中,这就是为什么我不知道使用 sed 或其他任何东西..请帮助...这是预期的输出..
<Pass>Physical interface: ge-0/0/3, Enabled, Physical link is Up
Interface index: 132, SNMP ifIndex: 504
Description: # SURVEILLANCE CAMERA #
<Pass>Link-level type: Flexible-Ethernet, Media type: Copper, MTU: 9000,
LAN-PHY mode, Link-mode: Full-duplex, Speed: 1000mbps, BPDU Error: None,
.....few more lines
<Fail>Physical interface: ge-0/1/0, Enabled, Physical link is Down
Interface index: 133, SNMP ifIndex: 505
<Fail>Link-level type: Ethernet, Media type: Fiber, MTU: 1514, LAN-PHY mode,
Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
.....few more lines
<Fail>Physical interface: ge-0/1/3, Enabled, Physical link is Up
Interface index: 136, SNMP ifIndex: 508
Description: # TO CSS_I-TN-CHNN-ENB-I099 #
<Fail>Link-level type: Flexible-Ethernet, Media type: Fiber, MTU: 8000,
LAN-PHY mode, Speed: 1000mbps, BPDU Error: None, MAC-REWRITE Error: None,
... few more lines
and so on....
使用 sed:
sed '/Physical link is/ { :a /MTU:/! { N; ba; }; /Physical link is Up.*MTU: 9000/ { s/\(.*\n\)\s*/<Pass><Pass>/; b; }; s/\(.*\n\)\s*/<Fail><Fail>/; }' filename
即:
/Physical link is/ { # Block start found
:a
/MTU:/! { # fetch lines until we find the MTU
N
ba
}
/Physical link is Up.*MTU: 9000/ { # If link is up and MTU 9000
s/\(.*\n\)[[:space:]]*/<Pass><Pass>/ # insert Pass markers
b
# we're done.
}
s/\(.*\n\)[[:space:]]*/<Fail><Fail>/ # otherwise insert Fail markers
}
请注意,对于 BSD sed,由于 b
指令,这不能用作单行代码。在那种情况下,将扩展的(没有注释,因为 BSD sed 很容易混淆)代码放在一个文件中,比如 foo.sed
,然后使用 sed -f foo.sed filename
。我已经用 POSIX 等价物 ([[:space:]]
) 替换了其他 GNU 主义 (\s
)。
要保留 MTU 行开头的空格,请删除 \s
或 [[:space:]]
。要在结果标记之前放置空格,请将 \s
或 [[:space:]]
放在捕获组内(即 \(.*\n\s*\)
)。
另请注意:这假设每个接口描述都有一个 MTU 字段。
或者,您可以试试这个 awk 脚本:
awk -v RS='Physical interface:' -F '\n' -v OFS='\n' '{ result = "<Fail>" } /Physical link is Up/ && /MTU: 9000/ { result = "<Pass>" } NR != 1 { for(i = 1; i <= NF; ++i) { if(index($i, "MTU:")) { sub(/^ */, result, $i) } } print result RS [=12=] }' filename
这会将文件拆分为 Physical interface:
处的记录,并将记录拆分为换行符处的字段。那么:
{ result = "<Fail>" } # result is Fail
/Physical link is Up/ && /MTU: 9000/ { # unless link is up and MTU 9000
result = "<Pass>"
}
NR != 1 { # the first record is the empty string
# before the first actual record, so
# we remove it.
for(i = 1; i <= NF; ++i) { # wade through the fields (lines)
if(index($i, "MTU:")) { # find the MTU line
sub(/^ */, result, $i) # put the marker there. To keep the
# whitespace, use $i = result $i
# instead, or sub(/^ */, "&" result, $i)
# to keep the spaces before the marker.
}
}
print result RS [=13=] # once done, print the whole shebang.
# We have to reinsert the record
# separator because it was removed
# by the splitting.
}
请注意,多字符 RS
并不严格符合 POSIX。不过,最常见的 awk(gawk 和 mawk)支持它。值得注意的是,BSD awk 没有。
尝试以下 awk
命令,该命令应 POSIX 兼容并保留前导空格:
awk '
/ Physical link is / { ++count }
/, MTU: / {
tag = (blockLines[1] ~ /Up$/ && [=10=] ~ /, MTU: 9000,/ ? "<Pass>" : "<Fail>")
sub(/^/, "&" tag, blockLines[1])
sub(/^ +/, "&" tag)
for (i = 1; i < count; ++i) print blockLines[i]
count = 0
}
count > 0 { blockLines[count++] = [=10=]; next }
{ print }
' file
基本思路是:
- 收集一个行块 - 必须标记的两行之间的所有行,包括必须标记的行 - 在数组中而不打印它们。
- 到达块末尾时,确定必须使用什么标记(失败或通过)
- 打印块中的所有行,并相应标记第一行和最后一行。
仅 Awk 脚本的注释版本:
/ Physical link is / { ++count } # Start of block
/, MTU: / { # End of block - fail/pass can now be determined
# Determine whether to apply a fail or a pass tag based on the
# first and last line in the block.
tag = (blockLines[1] ~ /Up$/ && [=11=] ~ /, MTU: 9000,/ ? "<Pass>" : "<Fail>")
# Prepend tag to 1st line in block
sub(/^/, "&" tag, blockLines[1])
# Prepend tag to last line in block, preserving leading whitespace.
sub(/^ +/, "&" tag)
# Print all lines in block (except for last one).
for (i = 1; i < count; ++i) print blockLines[i]
# Reset block line counter.
count = 0
}
# Inside block: collect lines, do not print yet.
count > 0 { blockLines[count++] = [=11=]; next }
# Print last line in block and lines outside of blocks.
{ print }