【问题标题】:printing the same field of next record after a match in awk在 awk 中匹配后打印下一条记录的相同字段
【发布时间】:2016-07-13 21:58:01
【问题描述】:

我试图在匹配后获取下一条记录的确切字段,例如,如果“string1”在 $2 中匹配,那么我需要获取下一条记录的 $2 的值。我有大约 100 个这样的字符串来匹配每个在输入文件中恰好出现一次的字符串。但是相同的搜索字符串可以出现在不同的输入文件中(我有超过 1000 个这样的输入文件)。

示例输入:逗号分隔

10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot02,
RX RF Frequency Slot02,Channel Spacing Slot02,AMR Range (QPSK) Slot02,AMR Range (16QAM) Slot02
37740.500 [MHz],7 [MHz],Enable,Enable
10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot03,
RX RF Frequency (Slot03),Channel Spacing (Slot03),AMR Range (QPSK) {(Slot03)|(SW GRP2)},AMR Range (16QAM) {(Slot03)|(SW GRP2)}
37712.500 [MHz],7 [MHz],Enable,Enable
10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot04,
RX RF Frequency Slot04,Channel Spacing Slot04,AMR Range (QPSK) Slot04,AMR Range (16QAM) Slot04
,,,
10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot05,
RX RF Frequency (Slot05),Channel Spacing (Slot05),AMR Range (QPSK) {(Slot05)|(SW GRP3)},AMR Range (16QAM) {(Slot05)|(SW GRP3)}

string1,string2....是搜索模式包含字母数字与 ({| 之间。 值字段可以为空。 字段数不固定。 string1 的位置不固定。可能出现在不同的位置,但在文件中只会出现一次。

到目前为止我尝试了什么:

BEGIN {
FS=OFS=","
}
{for (i=1;i<=NF;i++){
if ($i == "string1"){
    getline val;
    split(val,a,",");
    am1=a[i]}
if ($i == "string2"){
    getline val;
    split(val,a,",");
    am2=a[i]}
}
}
END {print am1,am2}

我知道这对于给定的输入不起作用,因为 i 会针对连续的 value1 和 value2 进行更改。我应该为每个搜索字符串使用不同的 for 循环吗?或建议任何解决方案。

用于搜索

string1="AMR Range (QPSK) Slot02",string2="AMR Range (QPSK) {(Slot03)|(SW GRP2)}",string3="AMR Range (QPSK) Slot04",string4="AMR Range (16QAM) Slot02"

期望的输出:

10.217.250.162,NTTN_EMS,Enable,Enable,,Enable

【问题讨论】:

  • 你的另一个输入文件在哪里,它是什么样子的,它叫什么?
  • edit 你的问题要包含一个带有简洁、可测试的样本输入的minimal reproducible example(两个输入文件,并将所有blahs、strings 和values 替换为有意义的代表值)和给定输入的预期输出。如果您正在考虑使用getline,请务必先阅读并完全理解awk.freeshell.org/AllAboutGetline的所有含义和注意事项
  • @MarkSetchell 其他输入文件格式相同,只是搜索字符串位置不同。
  • @Ed Morton 我已经更改了输入和输出数据。我首先尝试使用没有可变格式的 getline 并意识到应该避免它的艰难方式。浏览完这个文档后,我发现 getline 变量格式更安全。我已经考虑过您在匹配 '!--c;/pattern/{c=N}' 文件后打印第 n 条记录的解决方案,但无法弄清楚如何获取特定字段。我仍然不了解 getline 及其注意事项,因此任何没有 getline 的解决方案都可以。如果我能得到一个文件的解决方案,那么我将能够将脚本应用于多个文件。

标签: awk gawk


【解决方案1】:

我不明白您的输出格式,但也许这会有所帮助。这将创建搜索关键字到相应下一行中的值的映射

改变了你最后的输入行

$ cat file
blah,blah,blah,string1,string2,string3,blah
blah,blah,blah,value1,value2,value3,blah
string4,blah,string5,string6,blah
value4,x,value5,value6,x

并创建一个单独的查找文件

$ cat lookup
string1
string2
string3
string4
string5
string6

最后是脚本

$ awk -F, 'NR==FNR{m[$0];next} 
            FNR==1{p=$0;next}
                  {n=split(p,a); 
                   for(i=1;i<=n;i++) if(a[i] in m) print a[i],$i; 
                   p=$0}' lookup file

生成输出

string1 value1
string2 value2
string3 value3
string4 value4
string5 value5
string6 value6

您也可以使用多个数据文件运行相同的脚本

$ awk ... lookup file1 file2 file3 ...

并且可能在打印中添加 FILENAME 以识别哪个文件是匹配的来源。

【讨论】:

    【解决方案2】:

    如果您曾经考虑使用 getline,请务必先阅读并完全理解 http://awk.freeshell.org/AllAboutGetline 的所有含义和注意事项

    我不明白您如何从已发布的示例输入中获得已发布的预期输出,但鉴于此输入:

    $ cat strings
    AMR Range (QPSK) Slot02
    AMR Range (QPSK) {(Slot03)|(SW GRP2)}
    AMR Range (QPSK) Slot04
    AMR Range (16QAM) Slot02
    
    $ cat file
    10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot02,
    RX RF Frequency Slot02,Channel Spacing Slot02,AMR Range (QPSK) Slot02,AMR Range (16QAM) Slot02
    37740.500 [MHz],7 [MHz],Enable,Enable
    10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot03,
    RX RF Frequency (Slot03),Channel Spacing (Slot03),AMR Range (QPSK) {(Slot03)|(SW GRP2)},AMR Range (16QAM) {(Slot03)|(SW GRP2)}
    37712.500 [MHz],7 [MHz],Enable,Enable
    10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot04,
    RX RF Frequency Slot04,Channel Spacing Slot04,AMR Range (QPSK) Slot04,AMR Range (16QAM) Slot04
    ,,,
    10.217.250.162,NTTN_EMS,Radio / AMR Configuration Slot05,
    RX RF Frequency (Slot05),Channel Spacing (Slot05),AMR Range (QPSK) {(Slot05)|(SW GRP3)},AMR Range (16QAM) {(Slot05)|(SW GRP3)}
    

    我认为以下内容与您在文本中描述的一样:

    $ cat tst.awk
    BEGIN { FS=OFS="," }
    NR==FNR { strings[$0]; next }
    FNR==1 { out = $1 OFS $2 }
    {
        if (pos) {
            out = out OFS $pos
            pos = 0
        }
        for (i=1; i<=NF; i++) {
            if ($i in strings) {
                pos = i
            }
        }
    }
    ENDFILE {
        if (pos) {
            out = out OFS $pos
            pos = 0
        }
        print out
    }
    
    $ awk -f tst.awk strings file
    10.217.250.162,NTTN_EMS,Enable,Enable,
    

    上面使用 GNU awk 作为 ENDFILE 而不是 END 所以你可以这样做:

    awk -f tst.awk strings file1 file2 ....
    

    或类似的同时处理多个文件。

    如果你的字符串必须在你的 awk 脚本中硬编码,那么它只是一个调整:

    $ cat tst.awk
    BEGIN {
        FS=OFS=","
        split("AMR Range (QPSK) Slot02\n\
    AMR Range (QPSK) {(Slot03)|(SW GRP2)}\n\
    AMR Range (QPSK) Slot04\n\
    AMR Range (16QAM) Slot02", tmp, /\n/)
        for (i in tmp) {
            strings[tmp[i]]
        }
    }
    FNR==1 { out = $1 OFS $2 }
    {
        if (pos) {
            out = out OFS $pos
            pos = 0
        }
        for (i=1; i<=NF; i++) {
            if ($i in strings) {
                pos = i
            }
        }
    }
    ENDFILE {
        if (pos) {
            out = out OFS $pos
            pos = 0
        }
        print out
    }
    
    $ awk -f tst.awk file
    10.217.250.162,NTTN_EMS,Enable,Enable,
    

    【讨论】:

    • 需要解决的两个问题 1. 搜索字符串没有存储在不同的文件中,并且不能作为输入提供。 2. 搜索字符串的位置因文件而异,因此无法事先知道。这意味着必须从数据文件本身知道匹配的位置。
    • 告诉我们搜索字符串没有存储在哪里很有用,但告诉我们它们存储在哪里会更有用。单个shell变量?一个外壳数组?在awk中硬编码?还有什么?此外,脚本对搜索字符串的位置没有任何假设 - 它只是找到它们并记录找到它们的位置以供下一行使用。
    • 我编辑了我的答案,在脚本中包含一个硬编码字符串的版本,以防你正在寻找。
    • 它是从哪里得到的呢?与所有其他情况不同,在包含最终匹配字符串的行之后没有行,并且除了从后续行获取字段外,您没有提供任何要求。要么您有未告诉我们的其他要求,要么您提供的输入不完整,或者我遗漏了一些东西。
    • 对不起,我不知道你在说什么。也许其他人可以弄清楚。祝你好运!
    最近更新 更多