【问题标题】:How to avoid having newlines with grep -o for multiple match at the same line (on a text of several lines)如何避免在同一行使用 grep -o 换行符进行多个匹配(在多行文本上)
【发布时间】:2020-05-14 17:11:19
【问题描述】:

我有以下文字:

aaa rr tt zz pp
aaa pp xx yy uu zz

并且需要提取所有 'aaa'、'zz' 和 'xx' 模式并将它们打印在一行上,如下所示:

aaa zz
aaa xx zz

我找到的最好的是grep -oP 'aaa|xx|zz',但这会返回在新行上找到的每个模式:

aaa
zz
aaa
xx
zz

我尝试添加类似tr -d '\n' 的内容,但在这种情况下,它会在单行上返回整个匹配项,这不是我想要的。

注意:我需要一个支持正则表达式和非贪婪正则表达式的解决方案,因为搜索模式如下所示:^.+?,|,IN:.+?\-|,OUT:.+?-|State.+?[$,]

【问题讨论】:

    标签: regex unix grep


    【解决方案1】:

    你可以使用

     while IFS= read -r line; do
       echo $(grep -oP 'aaa|xx|zz' <<< "$line");
     done < file
    

    也就是说,

    1. 逐行读取输入文件
    2. 每行使用grep 命令获取匹配项
    3. shell 将用空格转换换行符,因为$(...) 没有用双引号括起来。

    如果您想保留匹配项中的特定空格,请考虑使用

    while IFS= read -r line; do 
      echo "$(grep -oP 'aaa|xx|zz' <<< "$line" | awk '{ printf "%s", $0" "}')"; 
    done < file
    

    这样,您将以空格分隔的方式获得每行匹配。您可以在awk 命令中使用任何自定义分隔符(在$0 之后)。

    【讨论】:

    • 如果您使用(然后不是)无用的echo,则不需要Awk,反之亦然。
    • @tripleee 如果您可以提供命令的变体,您的意思是它会有所帮助。我试过没有,但这是唯一有效的。
    • while IFS= read -r line; do echo $(grep -oP 'aaa|xx|zz' &lt;&lt;&lt;"$line"); done &lt;&lt;$'aaa bb cc\nzz xx yy\nboo baa
    • 不完全是; shell 将在您传递未加引号的字符串(例如命令替换的结果)时展平空格。等效地 echo "$(grep -oP 'aaa|xx|zz' | awk '{ printf "%s", $0 }')" 可以工作,并正确引用 shell 的输出。如果grep 的输出可能包含不规则的空格和/或未加引号的通配符,这是更好的方法。另见stackoverflow.com/questions/10067266/…
    • @WiktorStribiżew 很好,你关于我的正则表达式的相关性的论点是不可接受的,但无论如何我已经做了更多的测试,而且在 cygwin 上的性能确实很糟糕(比如处理 3 行/秒)但不是很明显在本机Linux上。所以我会接受那个:)
    【解决方案2】:

    假设您有 grep -P,这里有一个简单的 Awk 后处理器,用于将输出重新排列为所需的格式。

    grep -Pno '^.+?,|,IN:.+?\-|,OUT:.+?-|State.+?[$,]' - /dev/null <file |
    awk 'BEGIN { re="^\\(standard input\\):[1-9][0-9]*:" }
        $0 ~ re { sep="\n"; sub(re, "") }
        { if(NR>1) printf "%s", sep; printf "%s", $0; sep=" " }
        END { if(sep) printf "\n" }'
    

    如果 grep 结果可能会意外地输出一个前缀,看起来像 (standard input):1: 从实际匹配中,这将不起作用。

    这是来自 BSD grep;如果您的本地 grep 为标准输入输出不同格式的文件名前缀(或者如果您需要重构以读取多个命名文件而不是标准输入),则需要相应地调整 Awk 正则表达式。

    【讨论】:

    • 感谢 Tripleee,我尝试了几台机器,但无法正常工作。但是,在 grep 上添加 -n 的想法非常好,所以我制作了自己的版本: awk -F':' '{curr = $1; sub("^[1-9][0-9]*:", ""); if(prev && prev!=curr) {打印 STR; STR=$0;} 否则 {if (!prev) STR=$0;否则 STR=STR " " $0;} prev=curr;} END {打印 STR}'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    相关资源
    最近更新 更多