【问题标题】:Looping through fields and print previous line in awk [closed]遍历字段并在awk中打​​印上一行[关闭]
【发布时间】:2021-01-01 04:06:10
【问题描述】:

我一直在与 awk 进行斗争以完成这项工作,但我一直无法做到。我有两行如下:

= filename: /path/to/file
years="1990,2001"

我需要根据给定值检查引号之间的每一年,然后打印前一行,如果它匹配并得到文件名作为结果(它只需要匹配找到的第一个)。值和运算符,即 <,>,=,<=,=>,~ 将通过变量传递给 awk,例如:

value="2000"
string"=\$2 < $value"      # just an example

awk ... '"$string"' ...

有条件语句可以根据收到的输入生成此字符串。

我尝试使用空格、引号和逗号作为分隔符来分隔每个字段:

awk -F'[," ]' '{i=(1+(i%N));if(buf[i]&amp;&amp; $2&lt;2000) print buf[i]; buf[i]=$0;}'

这可行,但我需要遍历所有列 += $2。我试图做这样的事情:

awk -F'[," ]' '{for(f=2;f&lt;=NF;f++);i=(1+(i%N));if(buf[i]&amp;&amp; $f&gt;1950) print buf[i]; buf[i]=$0;}'

但这不起作用(我可能只是做错了)。

我还考虑过去掉if(buf[i]&amp;&amp; $2&gt;1950) print buf[i]; buf[i]=$0;,只是加入两行并分隔字段,检查 += $5 上的循环,然后只打印“$3”,因为它始终是文件名,但我不能不知道如何将两条线合并为一条。

示例:

年份

输入文字:

= filename: /mnt/project1/record1.txt
years="2005,2019,2011,2012,2013"

= filename: /mnt/project1/record2.txt
years="1996,2000"

= filename: /mnt/project1/record3.txt
years="2005,2001,2012"

= filename: /mnt/project1/record4.txt
years="2010,2009,1997,2000"

输出(匹配):

/mnt/project1/record2.txt
/mnt/project1/record4.txt

【问题讨论】:

  • 请在您的问题中添加示例输入(无描述、无图像、无链接)以及该示例输入所需的输出(无评论)。
  • 您的示例显示year &lt; 2000;您会一直寻找小于 (&lt;) 所需目标日期(例如,在您的示例中为2000)的日期吗?您是否还需要查看其他比较...&gt;&lt;=&gt;=date1 &lt;= year &lt;= date2 等?
  • 不,它需要是任何运算符或 ~ (如果需要,用于字符串)。 '$2 [operator] [value]' 将作为字符串传递给 AWK:即 "var="\$2
  • @bcHelix 这与您的问题中当前显示的要求非常不同(您可以从到目前为止收到的答案中看到)。请edit您的问题提供更清晰的需求陈述和更真实的示例。
  • 您的示例代码显示了一年的测试&gt;1950,但示例输入/输出基于一年的测试&lt; 2000;可能应该着眼于更新问题,以更简洁/清晰地解释您在做什么;提供几个不同的输入/输出示例(例如,&gt;1950&lt;2000 和任何演示 ~ (for strings if need be))也不会造成什么影响

标签: awk


【解决方案1】:

我仍在学习 AWK,但 IIUC 你想要类似于 关注script.awk

#!/usr/bin/awk -f

BEGIN {
    FS = "= filename:"
}

{
    if ( NF == 2 ) {
        filename = $2
        getline
    }
}

/^years="1990,2001"$/ \
{
    print "filename: " filename
}

使用以下input 运行:

= filename: /path/to/file5
years="1990,2001"

= filename: /path/to/file4
years="1990,2002"

= filename: /path/to/file3
years="1990,2003"

= filename: /path/to/file2
years="1990,2004"

= filename: /path/to/file1
years="1990,2005"

= filename: /path/to/file0
years="1990,2006"

这样:

$ ./script.awk input
filename:  /path/to/file5

【讨论】:

  • @markp-fuso:当然,在我发布答案后,OP 已经编辑了他们的问题...
  • 请参阅awk.freeshell.org/AllAboutGetline 了解何时/如何调用 getline。
  • @EdMorton:我的代码到底出了什么问题?
  • 请阅读那篇文章,它会告诉你。我写了它,这样我就不必反复讨论这些问题了。
【解决方案2】:

除了你所展示的之外,尝试做一些假设......
这是一个如何解析它的示例。

$: cat tst
= filename: /path/to/file/none
years="1890,1001"

= filename: /path/to/file/first
years="1990,0001"

= filename: /path/to/file/none
years="1890,1001"

= filename: /path/to/file/both
years="1990,2001"

= filename: /path/to/file/none
years="1890,1001"

= filename: /path/to/file/second
years="1790,2001"

= filename: /path/to/file/none
years="1890,1001"


$: awk '/filename:/ { fn=$3 } /years=/{ split($0,a,"[^0-9]+");
          for (y in a) { if (a[y]>1950) { printf "%s(%s)\n",fn,a[y]; break; } } }' tst
/path/to/file/first(1990)
/path/to/file/both(1990)
/path/to/file/second(2001)

【讨论】:

  • 这个看起来很有效。这可以与字符串一起使用吗?即a[y] ~ some-string
  • 引用字符串 - a[y] ~ "some-string".
【解决方案3】:

如果你需要多年的变量,你可以做这样的事情

awk -v y0=1990 -v y1=2001 '
$0 ~ "^years=\"" y0 "," y1 "\"$" {
    printf("filename: %s\n", filename);
}

/^= filename/ {
    filename = $3
}
' file.txt

【讨论】:

    【解决方案4】:

    假设:

    • OP 将始终搜索给定目标日期小于 (&lt;) 的日期(例如,在 OP 的示例中为 2000
    • ^= filename: 开头的行后面总是跟着以^years= 开头的行(否则我们需要更多样本输入数据才能添加更多逻辑)

    样本数据:

    $ cat file_year.dat
    = filename: /mnt/project1/record1.txt
    years="2005,2019,2011,2012,2013"             # no match
    
    = filename: /mnt/project1/record2.txt
    years="1996,2000"                            # match on 1996 < 2000
    
    = filename: /mnt/project1/record3.txt
    years="2005,2001,2012"                       # no match
    
    = filename: /mnt/project1/record4.txt
    years="2010,2009,1997,2000"                  # match on 1997 < 2000
    
    = filename: /mnt/project1/record5.txt
    years="2010,2009,2007,1947"                  # match on 1947 < 2000
    

    注意:添加 cmets 以显示应该(不)匹配的内容;实际数据文件中不存在cmets

    一个awk想法:

    awk -v tgt=2000 '                            # pass target date in as awk variable "tgt"
    /^= filename: / { fn=$(NF)                   # save the filename in awk variable "fn"
                      next                       # skip to the next line of input
                    }
    fn != ""        { n=split($0,arr,"[,\"]")    # if "fn" is set then split the current line on double quotes
                                                 # and commas; store results (ie, individual years)
                                                 # in array "arr[]"
                      for (i=2; i<n; i++)        # process arr[2-(n-1)] elements (individual years)
                          if ( arr[i] < tgt )    # if less than "tgt" ...
                             { print fn          # print associated filename ("fn") and ...
                               break             # break out of loop
                             }
                       fn=""                     # clear "fn" varible
                    }
    ' file_year.dat
    

    这会生成:

    /mnt/project1/record2.txt
    /mnt/project1/record4.txt
    /mnt/project1/record5.txt
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-02
      • 2018-10-02
      • 2017-04-24
      • 2018-11-21
      • 2020-10-28
      • 1970-01-01
      • 1970-01-01
      • 2016-04-23
      相关资源
      最近更新 更多