sed搜索多个字符串并将每个字符串及其后面的字符串输出到单独的行

时间:2013-09-05 22:10:28

标签: string perl sed

例如; 我有一个长文件,其中包含:

Somestring anotherstring -xone xcont othertring -yone ycont againother \
-detail "detail Contents within quote" stuff morestuff .. 

Somestring anotherstring -xone xcont othertring -yone ycont againother \
morestrings -detail detailCont morestrings etc.. .. 

理想的出局:

-xone xcont
-ycont ycont
-detail "detail Contents withing quote" 

使用csv文件是理想的:

xone yone detail
xcont ycont "detail Contents within quote"

获得所需输出的最佳方法是什么?我一直在尝试使用非常有限的sed命令。我是perl的新手,所以也没有走到那里..请解释建议的解决方案。 在此先感谢!

2 个答案:

答案 0 :(得分:1)

此问题由两部分组成:

  1. 如何匹配代码
  2. 如何以有序的方式输出它们。
  3. 匹配部分非常简单,使用正则表达式。每个标记都是连字符 - 后跟一些单词字符。作为正则表达式模式:-\w+

    值似乎是一个单词(我们可以匹配\w+)或带引号的字符串。假设此字符串不能包含其分隔符,我们可以使用"[^"]+",其中[^"]是一个否定的字符类,它匹配任何双引号字符。

    我们如何结合这个?通过交替和命名捕获:

    # I'll answer with Perl
    my $regex = qr/-(?<key>\w+) \s+ (?: (?<val>\w+) | "(?<val>[^"]+)" )/x;
    

    之后,$+{key}包含密钥,$+{val}包含该标记的值。我们现在可以提取一行中的所有标签。鉴于输入

    Somestring anotherstring -xone xcont othertring -yone ycont againother \-detail "detail Contents within quote" stuff morestuff .. 
    Somestring anotherstring -xone xcont othertring -yone ycont againother \morestrings -detail detailCont morestrings etc.. .. 
    

    代码

    use strict; use warnings; use feature 'say';
    my $regex = ...;
    while (<>) {
      while (/$regex/g) {
        say qq($+{key}: "$+{val}");
      }
    }
    

    我们得到输出

    xone: "xcont"
    yone: "ycont"
    detail: "detail Contents within quote"
    xone: "xcont"
    yone: "ycont"
    detail: "detailCont"
    

    要以表格格式打印出来,我们必须以特定结构收集数据。我假设每个标签可以为每一行发生一次。然后我们可以使用哈希来定义从标签到其值的映射。我们在一个数组中收集这些哈希值,每行一个。我们还必须收集所有标题的名称,以防一行不包含所有标题。现在我们的代码更改为:

    use strict; use warnings; use feature 'say';
    my $regex = ...;
    my %headers;
    my @rows;
    while (<>) {
      my %tags;
      while (/$regex/g) {
        $tags{$+{key}} = $+{val};
      }
      push @rows, \%tags;
      @headers{keys %tags} = ();  # define the headers
    }
    

    现在我们如何打印数据?我们可以将它们转储为制表符分隔值:

    my @headers = keys %headers;
    say join "\t", map qq("$_"), @headers;
    say join "\t", map qq("$_"), @$_{@headers} for @rows;
    

    输出:

    "yone"  "detail"        "xone"
    "ycont" "detail Contents within quote"  "xcont"
    "ycont" "detailCont"    "xcont"
    

    哦,列的顺序是随机的。如果我们使用Text::CSV模块,我们可以做得更好。然后:

    use Text::CSV;
    
    my @headers = keys %headers;
    my $csv = Text::CSV->new({ eol => "\n" });
    $csv->print(\*STDOUT, \@headers);
    $csv->print(\*STDOUT, [@$_{@headers}]) for @rows;
    

    我们得到了输出:

    yone,xone,detail
    ycont,xcont,"detail Contents within quote"
    ycont,xcont,detailCont
    

    列的顺序仍然是随机的,但这可以通过排序来修复。

    您可以通读Text::CSV documentation来发现许多可以调整输出的选项。

答案 1 :(得分:0)

这可能适合你(GNU sed):

sed -r '/-(xone|yone|detail)/!d;s//\n\1/;s/[^\n]*\n//;s/\S+\s+("[^"]*"|\S+)/&\n/;P;D' file

这会查找包含字符串-xone-yone-detail的行,并仅打印它们以及"或其他单词所包含的以下字词。