【问题标题】:remember a match in some previous line and print it just once and only if a consecutive line contains some match with sed记住前一行中的匹配并仅打印一次,并且仅当连续行包含与 sed 的某些匹配时
【发布时间】:2019-05-22 05:30:00
【问题描述】:

需要 sed 魔法:

我想知道如何在不立即打​​印的情况下保存/记住一行中的匹配项(如标题部分)

如果在后面的某处找到其他匹配项,则打印它,

但保存/记住的标题部分应该只打印一次,用于该标题下的任意数量的后续其他匹配

如果在记住的标题行下方的任何后续行中没有其他内容匹配,则根本不应该打印它

例如ifconfig

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    inet 127.0.0.1 netmask 0xff000000
    inet6 ::1 prefixlen 128
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
    inet 127.94.0.2 netmask 0xff000000
    inet 127.94.0.1 netmask 0xff000000
    nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
XHC20: flags=0<> mtu 0
en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
    ether 68:5b:35:c1:b3:91
    inet6 fe80::8ef:5953:53b:7058%en3 prefixlen 64 secured scopeid 0x5
    inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255
    inet6 2a02:810d:9c0:59bb:c0d:c8af:7e27:42f1 prefixlen 64 autoconf secured
    inet6 2a02:810d:9c0:59bb:643f:a2cb:ac5f:7c71 prefixlen 64 autoconf temporary
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (1000baseT <full-duplex,flow-control,energy-efficient-ethernet>)
    status: active
en0: flags=8823<UP,BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
    ether 6c:40:08:9c:45:ce
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (<unknown type>)
    status: inactive

ifconfig | gsed -n -E '/^[a-z0-9]*:/h; /\tinet (addr:)?[0-9.a-fA-F:]*/{x;p;x;p}'

我明白了(这已经很棒了,但不是很好):

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.0.0.1 netmask 0xff000000
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.94.0.2 netmask 0xff000000
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.94.0.1 netmask 0xff000000
en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255

但我只想为它的所有 ips 获取一次标题(并且没有混乱)

lo0:
    inet 127.0.0.1
    inet 127.94.0.2
    inet 127.94.0.1
en3:
    inet 192.168.0.2

【问题讨论】:

  • 不要解析 ifconfig 输出,它已经过时了。移至ip a

标签: shell awk sed


【解决方案1】:

请您尝试关注一次。

Your_command | awk '
/^[a-zA-Z]+/{
  count=""
  val=$1
  next
}
val && match($0,/.*[0-9]+\.[-0-9]+\.[0-9]+\.[0-9]+ +/){
  if(++count==1){
    print val
  }
  value=substr($0,RSTART,RLENGTH)
  sub(/ +$/,"",value)
  print value
  value=""
}
' 

【讨论】:

    【解决方案2】:

    所以我昨晚想通了怎么做:)

    ifconfig | gsed -n -E '
    /^[a-z0-9]*:/ {                     # for lines starting with this
        s/^([a-z0-9]*:).*/\1/;h         # extract the start and put it into hold space
    }
    /\tinet (addr:)?[0-9.a-fA-F:]*/ {   # for lines containing this
        s/^.*(\tinet (addr:)?[0-9.a-fA-F:]*).*/\1/    # extract it
        x                               # swap hold space and pattern space
        G                               # and then append the hold space (former pattern space) to it 
        s/^\n//                         # replace leading \n if former hold space was empty
        p                               # print the concatenated former hold space and modified pattern space
        s/^.*$//                        # empty the pattern space
        x                               # swap the now empty pattern space to hold space
    }
    '
    

    或作为(或多或少)单行:

    ifconfig | gsed -n -E '
    /^[a-z0-9]*:/                   { s/^([a-z0-9]*:).*/\1/;h };
    /\tinet (addr:)?[0-9.a-fA-F:]*/ { s/^.*(\tinet (addr:)?[0-9.a-fA-F:]*).*/\1/;x;G;s/^\n//;p;s/^.*$//;x}'
    

    给我想要的

    lo0:
        inet 127.0.0.1
        inet 127.94.0.2
        inet 127.94.0.1
    en3:
        inet 192.168.0.2
    

    【讨论】:

      【解决方案3】:

      我有两个建议给你:

      1. 清理输入行时,有时删除比选择更容易。
      2. 在这种情况下,将最终输出收集到保持空间中会更容易。

      例如:

      parse.sed

      /^[^ ]+/ {        # For lines starting with non-space
        x               # \
        /\n/p           #  Did we collect any IP addresses?
        x               # /
      
        s/ .*//         # Clean up interface name
        h               # Overwrite hold-space
      }
      
      /inet / {
        s/ netmask.*//
        H               # Collect ip addresses in hold-space
      }
      
      $ {
        x               # Print the last interface if
        /\n/p           # it contained IP addresses
      }
      

      像这样运行它:

      ifconfig | gsed -nEf parse.sed
      

      输出:

      lo0:
          inet 127.0.0.1
          inet 127.94.0.2
          inet 127.94.0.1
      en3:
          inet 192.168.0.2
      

      【讨论】:

        【解决方案4】:

        sed 用于做简单的s/old/new仅此而已,其他任何事情只需使用 awk:

        $ ifconfig | awk '/^[[:alpha:]]/{hdr=$1 ORS} $1=="inet"{print hdr "    " $1, $2; hdr=""}'
        lo0:
            inet 127.0.0.1
            inet 127.94.0.2
            inet 127.94.0.1
        en3:
            inet 192.168.0.2
        

        【讨论】:

          【解决方案5】:

          这可能对你有用(GNU sed):

          sed -En '/^\S/h;s/^(\s+inet\s+\S+).*/\1/;T;x;s/\s.*//p;x;p' file
          

          将标题行存储在保留空间中。

          删除IP地址后面包含inet的行的所有内容。

          如果替换不成功,请退出。否则,返回hold space并尝试删除标题行的末尾并打印它。

          返回模式空间,打印原始ip地址行。

          注意标题只会打印一次,因为一旦该行已被编辑,替换将失败。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-09-05
            • 1970-01-01
            • 2019-04-29
            • 2018-11-24
            • 2014-05-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多