【问题标题】:Using awk or sed to merge / print lines matching a pattern (oneliner?)使用 awk 或 sed 合并/打印与模式匹配的行(oneliner?)
【发布时间】:2016-08-15 04:36:48
【问题描述】:

我有一个包含以下文本的文件:

subject:asdfghj
subject:qwertym
subject:bigger1
subject:sage911
subject:mothers
object:cfvvmkme
object:rjo4j2f2
object:e4r234dd
object:uft5ed8f
object:rf33dfd1

我希望使用 awk 或 sed 实现以下结果(因为 oneliner 将是一个奖励![Perl oneliner 也可以接受]):

subject:asdfghj,object:cfvvmkme
subject:qwertym,object:rjo4j2f2
subject:bigger1,object:e4r234dd
subject:sage911,object:uft5ed8f
subject:mothers,object:rf33dfd1

我希望将匹配“主题”和“对象”的每一行按照列出的顺序组合在一起,并用逗号分隔。我可以看一个用 awk、sed 或 perl 完成的例子吗? (如果可能的话,最好是单线?)

我已经尝试过使用 awk 来执行此操作,但我仍在学习我应该添加:

awk '{if ($0 ~ /subject/) pat1=$1; if ($0 ~ /object/) pat2=$2} {print $0,pat2}'

但是并没有按照我的想法去做!所以我知道我的语法错误。如果我看到一个对我学习有很大帮助的例子。

【问题讨论】:

  • 在这里发布需求并等待有人为您完成工作是不可接受的。如果您有一些无法使用的代码,请在此处发布,我们将帮助您完成它。如果您无法为自己写任何东西,那么您应该考虑花钱请人为您写,就像您在任何其他学科中所做的那样
  • 我很抱歉,但我一直在尝试使用 awk 和 sed 的变体,但这是不“知道”如何构造命令语法以产生接近结果的东西的问题。例如我做了 awk '{if ($0 ~ /subject/) pat1=$1; if ($0 ~ /object/) pat2=$2} {print $0,pat2}' 但这并没有产生我想要的东西。所以我想我根本不懂语法,所以我的问题。不过还是谢谢你的评论。
  • 当您不“知道如何”修理汽车、建造阁楼扩建或修理漏水的供水系统时,您会怎么做?您是否要求免费为您写诗歌或文学作品?
  • 我只是想看一些例子,本着“授人以渔”的精神。不过还是谢谢你的回复。
  • perl -nE 'chomp; /^s/ ? push @s, $_ : push @o, $_; END {for (0..$#s) { say "$s[$_],$o[$_]"} }' file

标签: regex perl awk sed


【解决方案1】:

由于您特别要求使用“oneliner”,我认为简洁对您来说比清晰更重要,因此:

$ awk -F: -v OFS=, 'NR>1&&$1!=p{f=1}{p=$1}f{print a[++c],$0;next}{a[NR]=$0}' file
subject:asdfghj,object:cfvvmkme
subject:qwertym,object:rjo4j2f2
subject:bigger1,object:e4r234dd
subject:sage911,object:uft5ed8f
subject:mothers,object:rf33dfd1

【讨论】:

    【解决方案2】:

    greppaste 和进程替换

    $ paste -d , <(grep 'subject' infile) <(grep 'object' infile)
    subject:asdfghj,object:cfvvmkme
    subject:qwertym,object:rjo4j2f2
    subject:bigger1,object:e4r234dd
    subject:sage911,object:uft5ed8f
    subject:mothers,object:rf33dfd1
    

    这会将grep 'subject' infilegrep 'object' infile 的输出视为由于进程替换(&lt;( ))而产生的类似文件,然后将结果与paste 一起粘贴,使用逗号作为分隔符(由-d , 表示) .

    sed

    这个想法是读取所有主题行并将其存储在保持空间中,然后为每个对象行获取保持空间,获取正确的主题并将剩余的主题行放回保持空间。

    首先是不可读的单行线:

    $ sed -rn '/^subject/H;/^object/{G;s/\n+/,/;s/^(.*),([^\n]*)(\n|$)/\2,\1\n/;P;s/^[^\n]*\n//;h}' infile
    subject:asdfghj,object:cfvvmkme
    subject:qwertym,object:rjo4j2f2
    subject:bigger1,object:e4r234dd
    subject:sage911,object:uft5ed8f
    subject:mothers,object:rf33dfd1
    

    -r 用于扩展正则表达式(不转义括号,+|),-n 默认不打印。

    扩展,更具可读性和解释性:

    /^subject/H         # Append subject lines to hold space
    /^object/ {         # For each object line
        G               # Append hold space to pattern space
        s/\n+/,/        # Replace first group of newlines with a comma
    
        # Swap object (before comma) and subject (after comma)
        s/^(.*),([^\n]*)(\n|$)/\2,\1\n/
    
        P               # Print up to first newline
        s/^[^\n]*\n//   # Remove first line (can't use D because there is another command)
        h               # Copy pattern space to hold space
    }
    

    备注:

    • 当第一次获取保持空间时,它以换行符开始(H 加一个),所以换行符到逗号替换替换了一个或多个换行符,因此\n+:两个换行符第一次,其他的。
    • 为了在交换中锚定主题部分的结尾,我们使用(\n|$):换行符或模式空间的结尾——这也是为了在最后一行获得交换,我们没有模式空间末尾的换行符。
    • 这适用于 GNU sed。对于 MacOS 中的 BSD sed,需要进行一些更改:
      • -r 选项必须替换为 -E
      • 在右大括号之前必须有一个额外的分号:h;}
      • 要在替换字符串中插入换行符(swap 命令),我们必须将 \n 替换为 '$'\n'''"$(printf '\n')"'

    【讨论】:

      【解决方案3】:

      我会在perl 中这样做:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      my @subjects;
      while ( <DATA> ) { 
          m/^subject:(\w+)/ and push @subjects, $1; 
          m/^object:(\w+)/ and print "subject:",shift @subjects,",object:", $1,"\n";
      }
      
      
      __DATA__
      subject:asdfghj
      subject:qwertym
      subject:bigger1
      subject:sage911
      subject:mothers
      object:cfvvmkme
      object:rjo4j2f2
      object:e4r234dd
      object:uft5ed8f
      object:rf33dfd1
      

      减少到一个班轮,这将是:

      perl -ne '/^(subject:\w+)/ and push @s, $1; /^object/ and print shift @s,$_' file
      

      【讨论】:

        【解决方案4】:

        不是 perl 或 awk,而是更简单。

        $ pr -2ts, file
        subject:asdfghj,object:cfvvmkme
        subject:qwertym,object:rjo4j2f2
        subject:bigger1,object:e4r234dd
        subject:sage911,object:uft5ed8f
        subject:mothers,object:rf33dfd1
        

        说明

        -2 2 列

        t忽略打印标题(文件名、日期、页码等)

        s, 使用逗号作为列分隔符

        【讨论】:

        • -t那些省略的标题是打印页标题,如页码,文件名日期等。-n是列数
        • 唯一需要注意的是,这种方法需要相同数量的“主题”和“对象”条目。
        • 正确,但是如果缺少一个,当您尝试对齐它们时,您将不知道缺少哪个。
        猜你喜欢
        • 2013-07-28
        • 2011-07-20
        • 1970-01-01
        • 2020-10-12
        • 1970-01-01
        • 2020-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多