【问题标题】:How can I print the line number of a record in awk?如何在 awk 中打印记录的行号?
【发布时间】:2019-01-23 23:41:52
【问题描述】:

我正在使用 awk 处理多行记录,其中每条记录的字段数是未知的。这是为了帮助过滤非常大的文件中的记录,因此了解返回记录的行号会很有帮助。我尝试为每条记录指定一个变量,但这似乎很老套,有没有更好的方法来做到这一点?

数据示例(包括行号):

1 | data1 - good
2 |    foo bar
3 |
4 | data2 - bad
5 |    foo bar
6 |    pet cat
7 |    name snuggles
8 |
9 | data3 - good
10|    foo bar
11|    color blue

代码示例:

BEGIN {RS =""; FS="\n"; ORS="\n\n"; OFS=""; x=0}
{
  { x += NF + 1; }
  { if ($1 ~ /bad/) { next; } }
  { print "[", x - NF, "]\n", $0; }
}

我正在寻找的输出是这样的:

[1]
data1 - good
    foo bar

[9]
data3 - good
    foo bar
    color blue

有没有更好的方法来做到这一点,我没有看到?

【问题讨论】:

    标签: awk


    【解决方案1】:

    总的来说,我认为您的方法很好,不会认为它是 hackey。

    您可以考虑进行一些小调整以使其更简单:

    BEGIN {RS =""; FS="\n"; ORS="\n\n"; OFS=""; x=1}
    !($1 ~ /bad/) { print "[", x, "]\n", $0; }
    { x += NF + 1; }
    

    【讨论】:

      【解决方案2】:

      您能否尝试一次,仅使用您的样品进行测试。

      awk '
      /data[0-9]+/{
        flag=$NF=="bad"?"":1
        count=""
      }
      flag && NF>2{
        if(++count==1){
          print "["$1"]"
          sub(/.*\| /,"")
        }
        sub(/.*\|/,"")
        print
      }'   Input_file
      

      【讨论】:

        【解决方案3】:

        您的方法似乎不错,尽管我可能会将其调整为:

        $ cat tst.awk
        BEGIN { RS=""; ORS="\n\n"; FS="\n" }
        {
            nr += prevNf + 1
            if ($1 ~ /good/) {
                print "[" nr "]\n" $0
            }
            prevNf = NF
        }
        
        $ awk -f tst.awk file
        [1]
        data1 - good
           foo bar
        
        [9]
        data3 - good
           foo bar
           color blue
        

        但这里有一个替代方案:

        $ cat tst.awk
        !NF { prt(); next }
        {
            nrs[++numLines] = NR
            rec[numLines]   = $0
        }
        END { prt() }
        
        function prt(   lineNr) {
            if (rec[1] ~ /good/) {
                printf "[%d]\n", nrs[1]
                for (lineNr=1; lineNr<=numLines; lineNr++) {
                    print rec[lineNr]
                }
                print ""
            }
            delete rec
            numLines = 0
        }
        
        $ awk -f tst.awk file
        [1]
        data1 - good
           foo bar
        
        [9]
        data3 - good
           foo bar
           color blue
        

        使用上述方法,您不仅可以仅在一行上测试好坏,还可以根据需要打印每条记录的所有或任何行的输入行号。

        【讨论】:

          【解决方案4】:

          如果 Perl 是一个选项,你可以试试下面

          $ cat caffein.txt
          data1 - good
             foo bar
          
          data2 - bad
             foo bar
             pet cat
             name snuggles
          
          data3 - good
             foo bar
             color blue
          
          $ perl -0777 -ne ' s/^/++$x." "/mge; while(/(^\d+)(\s*data.+?good.+?)(\n\d+\s+\n\d+\s+|\Z)/gms) { $x="[$1] $2\n\n";$x=~s/^\d+/ /mg; print $x } ' caffein.txt
          [1]  data1 - good
               foo bar
          
          [9]  data3 - good
               foo bar
               color blue
          
          
          $
          

          或对不匹配的“坏”进行负前瞻

          $ perl -0777 -ne ' s/^/++$x." "/mge; while(/(^\d+)(\s*data.+?(?!bad).+?)(\n\d+\s+\n\d+\s+|\Z)/gms) { $x="[$1] $2\n\n";$x=~s/^\d+/ /mg; print $x } ' caffein.txt
          

          【讨论】: