【问题标题】:Identify line in hierarchical file bash script识别分层文件bash脚本中的行
【发布时间】:2021-10-31 09:03:13
【问题描述】:

我需要确定文件中配置条目列表的头部。没有可预测的,它可以是任何字符串,但它总是比其他字符串更靠近左侧开始的行(不包括“exit”):

这是一个例子:

    vpls 2662 customer 1 v-vpls vlan 2662 create
        description "RES_2662"
        mac-move
            allow-res-res
            allow-reg-res
        exit
        stp
            shutdown
        exit
        ingress
            qos 2
        exit
        sap lt:1/1/1:2662 create
            description "RES_2662"
            enable-stats
            no shutdown
        exit
        sap lag-1:2662 create
            no shutdown
        exit
        no shutdown
    exit
    vpls 2663 customer 1 v-vpls vlan 2663 create
        description "RES_2663"
        mac-move
            allow-res-res
            allow-reg-res
        exit
        stp
            shutdown
        exit
        ingress
            qos 2
        exit
        sap lt:1/1/1:2663 create
            description "RES_2663"
            enable-stats
            no shutdown
        exit
        sap lag-1:2663 create
            no shutdown

在这种情况下,我需要能够识别开头的两行: vpls 266X customer 1 v-vpls vlan 266X create 脚本应该知道这些是我正在寻找的行。

输出并不总是会在左侧显示空格,如下例所示:

port vlan-port:1/1/1/3/7/4/4:824
  admin-up
  severity no-value
exit
port vlan-port:1/1/1/3/7/4/4:1224
  admin-up
  severity no-value
exit

在这种情况下,所需的行是: port vlan-port:x/x/x/x/x/x/x/x

我不知道是否可以使用 grep/sed/awk 来完成。

感谢您的帮助。

【问题讨论】:

    标签: awk sed grep


    【解决方案1】:

    以下内容将在每个 Unix 机器上的任何 shell 中使用任何 awk 工作,并将保留输入行的顺序以用于输出,以防万一:

    $ cat tst.awk
    $1 != "exit" {
        match($0,/^ */)
        if ( (min == "") || (RLENGTH <= min) ) {
            min = RLENGTH
            lines[min,++cnt[min]] = $0
        }
    }
    END {
        for (i=1; i<=cnt[min]; i++) {
            print lines[min,i]
        }
    }
    

    $ awk -f tst.awk file
        vpls 2662 customer 1 v-vpls vlan 2662 create
        vpls 2663 customer 1 v-vpls vlan 2663 create
    

    【讨论】:

      【解决方案2】:

      我怀疑有更好的方法可以做到这一点,但我的第一个想法是以下。你可以从这样的东西开始并改进它。

      minl=$(awk '{match($0, /^ */);if (NR==1 || RLENGTH<minl) {minl=RLENGTH}} END{print minl}' test.txt)
      sed -n "/^[ ]\{${minl}\}[^ ]/p" test.txt | grep -v "exit"
      

      第一行使用awk 获取文件行首的最小空格数。

      第二行使用sed 匹配从第一行计算的空格数开始的行。我通过grep -v "exit" 将结果通过管道传输以摆脱退出行...您可能需要更严格地检查有效的输出行是否可以包含文本“exit”。

      【讨论】:

        【解决方案3】:

        另一种可能的解决方案

        # gets the number of leading spaces + 1
        n=$(sort -r file.txt | sed -nE '1s/(^ *).*/\1/p' | wc -c | tr -d ' ')
        # filter the file
        egrep -vE "^ {$n,}|^ *exit" file.txt
        

        【讨论】:

        • 它适用于第一个示例,但不适用于第二个......
        • 对我来说它产生了port vlan-port:1/1/1/3/7/4/4:824 port vlan-port:1/1/1/3/7/4/4:1224,这似乎是预期的,你的输出是什么?
        【解决方案4】:

        假设:

        • 前导空白仅由空格组成(即,没有制表符,没有非打印字符)

        一个awk 的想法是我们维护一个具有(当前)最小数量的前导空格的行的数组,每当我们发现一行具有更少(即“新”最小)前导空格时重置数组:

        awk '
        BEGIN   { min = 9999999 }
        
        /^$/    { exit }                  # skip blank lines
        
        /exit/  { if {NF==1) next }       # skip lines with single field "exit"
        
                { n = match($0,/[^ ]/)    # find index of first non-space
        
                  if ( n < min ) {        # if a new minimum is found then ...
                     delete arr           # delete the array and ...
                     i = 1                # reset the array index and ...
                     min = n              # reset the min
                  }
        
                  if ( n == min )         # if current row matches with "min" then ...
                     arr[i++] = $0        # save the row in our array; increment the index
                }
        
        END     { for (j=1;j<i;j++)       # loop through entries in array
                     print arr[j]
                }
        ' file.dat
        

        对于 OP 的第一组数据,这会生成:

            vpls 2662 customer 1 v-vpls vlan 2662 create
            vpls 2663 customer 1 v-vpls vlan 2663 create
        

        对于 OP 的第二组数据,这会生成:

        port vlan-port:1/1/1/3/7/4/4:824
        port vlan-port:1/1/1/3/7/4/4:1224
        

        【讨论】:

          【解决方案5】:

          试试perlawk 的组合:

          $ perl -ne ' /(^.\s*)/ and !/^\s*exit/ and print length($1), $_ ' fernando.txt | sort -n | awk ' { f=$1; p=NR==1?f:p; sub(/^[0-9]+/,"",$0);if(f==p)  print } '
              vpls 2662 customer 1 v-vpls vlan 2662 create
              vpls 2663 customer 1 v-vpls vlan 2663 create
          

          【讨论】:

            猜你喜欢
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            • 2016-12-21
            • 1970-01-01
            • 2022-11-22
            • 2011-04-01
            • 2020-10-18
            • 1970-01-01
            相关资源
            最近更新 更多