简体   繁体   English

如何在Shell脚本中匹配然后替换多行

[英]How to match & then replace multiple lines in shell script

Hi I have this text file. 嗨,我有这个文本文件。

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....

Now If Physical link is Up & value of MTU is 9000 then only I need to replace both the corresponding lines as. 现在,如果“物理链接”已打开且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,

In every other situation it will be <Fail> in place of <Pass> . 在其他所有情况下,它将用<Fail>代替<Pass> these values lie in different line that why I am not getting any idea of using sed or anything else..please help... here is the expected output.. 这些值位于不同的行中,这就是为什么我不了解使用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....

With sed: 与sed:

sed '/Physical link is/ { :a /MTU:/! { N; ba; }; /Physical link is Up.*MTU: 9000/ { s/\(.*\n\)\s*/<Pass>\1<Pass>/; b; }; s/\(.*\n\)\s*/<Fail>\1<Fail>/; }' filename

That is: 那是:

/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>\1<Pass>/ # insert Pass markers
    b
                                           # we're done.
  }
  s/\(.*\n\)[[:space:]]*/<Fail>\1<Fail>/   # otherwise insert Fail markers
}

Note that with BSD sed, this cannot be used as a one-liner because of the b instructions. 请注意,对于BSD sed,由于使用b指令,因此不能用作单线。 In that case, put the expanded (without comments, for BSD sed is easily confused) code in a file, say foo.sed , and use sed -f foo.sed filename . 在这种情况下,请将扩展的代码(不带注释,因为BSD sed很容易混淆)放在文件中,例如foo.sed ,然后使用sed -f foo.sed filename I've already replaced the other GNU-ism ( \\s ) with its POSIX equivalent ( [[:space:]] ) there. 我已经用它的POSIX等效项( [[:space:]] )替换了其他GNU-ism( \\s )。

To keep the whitespaces at the beginning of the MTU line, remove the \\s or [[:space:]] . 要将空格保留在MTU行的开头,请删除\\s[[:space:]] To place the whitespaces before the result marker, put the \\s or [[:space:]] inside the capturing group (ie, \\(.*\\n\\s*\\) ). 要将空格放在结果标记之前,请将\\s[[:space:]]放在捕获组内(即\\(.*\\n\\s*\\) )。

Also note: This assumes that every interface description has an MTU field. 另请注意:这假设每个接口描述都有一个MTU字段。

Alternatively, you might try this awk script: 或者,您可以尝试以下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 $0 }' filename

This splits the file into records at Physical interface: and the records into fields at newlines. 这会将文件拆分为“ Physical interface:的记录Physical interface:并将记录拆分为换行符的字段。 Then: 然后:

{ 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 $0                   # once done, print the whole shebang.
                                       # We have to reinsert the record
                                       # separator because it was removed
                                       # by the splitting.
}

Note that a multi-character RS is not strictly POSIX-conforming. 注意,多字符RS并非严格符合POSIX。 The most common awks (gawk and mawk) support it, though. 不过,最常见的awks(gawk和mawk)都支持它。 Notably, BSD awk does not. 值得注意的是,BSD awk没有。

Try the following awk command, which should be POSIX-compliant and preserves leading whitespace: 尝试使用以下awk命令,该命令应符合POSIX并保留前导空白:

awk '
 / Physical link is / { ++count }
 /, MTU: / {
    tag = (blockLines[1] ~ /Up$/ && $0 ~ /, 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++] = $0; next }
 { print }
' file 

The basic idea is: 基本思想是:

  • Collect a block of lines - all lines between and including the two lines that must be tagged - in an array without printing them yet. 在数组中收集一行代码-包括必须标记的两行之间的所有行,并且不打印它们。
  • On reaching the end of the block, determine what tag (fail or pass) must be used 到达块的末尾时,确定必须使用哪个标签(失败或通过)
  • Print all lines in the block, with the first and last one tagged accordingly. 打印块中的所有行,并相应标记第一行和最后一行。

Annotated version of the Awk script only: 仅带注释的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$/ && $0 ~ /, 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++] = $0; next }
 # Print last line in block and lines outside of blocks.
 { print }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM