【问题标题】:awk: Use gensub to substitute multiple lines from a paragraph recordawk:使用 gensub 替换段落记录中的多行
【发布时间】:2019-07-19 07:06:48
【问题描述】:

我有一个输入文件,其中包含由至少两个换行符 (\n\n) 分隔的多个段落,并且我想从某些段落中的行中提取字段。我认为如果我能让gensub 像我希望的那样工作,那么处理将是最简单的。考虑以下输入文件:

[Record R1]
    Var1=0
    Var2=20
    Var3=5

[Record R2]
    Var1=10
    Var3=9
    Var4=/var/tmp/
    Var2=12

[Record R3]
    Var1=2
    Var3=5
    Var5=19

我只想从记录R1R3 中打印Var2 的值(其中Var2 实际上并不存在)。我可以通过设置RS="\n\n" 轻松地将所有变量分组到对应的记录中,然后它们都包含在$0 中。但是由于我不知道它会提前出现在列表的哪个位置,所以我想使用 gensub 之类的东西来提取它。这就是我要做的:

awk '
    BEGIN {
        RS="\n\n"
    }
    /Record R1/ || /Record R3/ {
        print gensub(/[\n.]*Var2=(.*)[\n.]*/, "\\1", "g", $0)
    }
' /tmp/input.txt

但不是只打印20(R1 中 Var2 的值),而是打印以下内容:

[Record R1]
    Var1=0
    20
    Var3=5
[Record R3]
    Var1=2
    Var3=5
    Var5=19

目的是 gensub 命令中的正则表达式将捕获 Var2=XX 之前和之后的所有字符(换行符:\n;和非换行符:.)并将所有内容替换为XX。但相反,它只捕获与Var2=XX 在同一行的字符。 awkgensub可以做这种多行替换吗?

我知道另一种方法是循环遍历记录中的所有字段,在= 符号上拆分与Var2= 匹配的字段,但是当我将其扩展到多个变量时感觉效率较低。

【问题讨论】:

    标签: regex awk substitution


    【解决方案1】:

    我不明白你想用 gensub() 做什么,但要在任何 awk 中做你似乎想要做的事情是:

    awk -F'[][[:space:]=]+' '{f[$2]=$3} !NF{if (f["Record"]~/^R[12]$/) print f["Var2"]; delete f}' file
    20
    12
    
    awk -F'[][[:space:]=]+' '{f[$2]=$3} !NF{if (f["Record"]~/^R[13]$/) print f["Var2"]; delete f}' file
    20
    

    gensub() 并不关心它正在操作的字符串是一行还是多行 - \n 只是多了一个字符,与任何其他字符没有什么不同。

    哦,等一下,现在我明白你对 gensub() 的想法了 - 你的问题是:

    1. [\n.]* 表示 zero or more newlines or periods 但你没有 输入中的任何句点,因此它与\n* 相同,但在Var2 之前没有任何换行符
    2. Var2 不存在于您的第二条记录中,因此正则表达式无法匹配它。
    3. (.*) 将匹配到记录末尾的所有内容(最左边最长的匹配项)。
    4. "g" 具有误导性,因为您只期望 1 次匹配。

    所以在多行文本上使用 gensub() 不是问题,你的正则表达式是错误的。

    【讨论】:

    • 谢谢埃德。我喜欢这种建立数组的方法。我认为从长远来看它会比 gensub 便宜。这也让我找到了我在 gensub 中寻找的答案:gensub(/.*Var2=([^\n]*).*/, "\\1", "g", $0)。当Var2 不存在于记录中时,必须对其进行更新,但这是我正在寻找的主要部分。
    • 在这种情况下,性能并不重要,重要的是我的方法最终得到了一个名称与值的关联数组,因此测试/打印您想要的任何值都很简单以他们的名义。 wrt 你的评论使用 gensub() It would have to be updated to account for when Var2 doesn't exist - 不完全是,它需要完全不同的代码来处理这种情况。
    【解决方案2】:

    另一个awk

    $ awk -v RS= '/\[Record R[13]\]/{for(i=2;i<=NF;i++)
                                       {v=sub(/ *Var2=/,"",$i);
                                        if(v) print $i}}' file
    
    20
    

    【讨论】:

      猜你喜欢
      • 2021-07-02
      • 2016-01-28
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-12-06
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多