sed 相当于“grep -o”的是什么?

sed 相当于“grep -o”的是什么?

我想要 sed 相当于: 。假设我可能想对输出进行进一步的处理。此步骤可能只是通过添加更长的 sed 表达式而变得更加复杂的部分的第一部分。grep -Eo 'regex'; s…

更清楚地说,我希望能够隔离每个细绳 匹配输入流中给定的正则表达式。出于概念验证的目的,每个此类字符串应作为没有上下文的单独行输出(即,输入中没有周围的文本)。因此,具有多个(不重叠)匹配的输入行应该会产生多个输出行;没有匹配项的输入行不会产生任何输出。

例子:

正则表达式: [a-zA-Z]{3}[0-9]{4}       (即三个字母后跟四个数字)

输入:

FGH1234 and CAS4057
MAX2345

输出:

FGH1234
CAS4057
MAX2345

答案1

更新以修复零长度正则表达式匹配的行为:

sed 't match;s/REGEX/\n&\n/g;D;:match;/^\n/!P;s/\n//;D' file

全局替换匹配<newline><matched part><newline>.然后通过创建循环来打印它们,P;s/\n//;D依此类推t match,直到打印完所有匹配的部分。/^\n/!P使用而不是 justP以便只打印非空匹配(就像 GNUgrep -o那样)。

使用的类似方法awk可以是:

regex='REGEX' awk 'BEGIN {FS="\n"}
  gsub(ENVIRON["regex"], FS "&" FS) {for (i=2;i<NF;i+=2) if ($i!="") print $i}
' file

原始尝试:请注意,当给定与空字符串(例如.*)匹配的正则表达式时,这些命令的行为会很糟糕 - 空行将在无限循环中打印。

通过一次调用sed

sed '
t match
s/[[:alpha:]]\{3\}[[:digit:]]\{4\}/\
&\
/;D;:match
P;D' file

使用POSIXsed语法:regex 是基本正则表达式,\在 的替换字符串中使用转义换行符s///,并且使用换行符而不是;在分支标签之后。某些版本sed(例如 GNU sed)可以在一行上接受所有脚本:

sed 't match;s/[[:alpha:]]\{3\}[[:digit:]]\{4\}/\n&\n/;D;:match;P;D' file

替换通过在匹配部分之前和之后添加换行符来隔离第一个匹配项。t match仅在成功替换后才会遵循脚本开头的条件分支。:match是打印匹配部分的地方。D使用以便从模式空间中删除包含匹配的行,并将剩余部分用作下一个循环的输入,从而允许找到更多匹配。

答案2

这非常类似于划艇的答案(但独立开发),并且可能有稍微更详细的解释。

使用 GNU sed:

sed -En 't dummy; : dummy; s/[a-zA-Z]{3}[0-9]{4}/&\n/; T; s/.*([a-zA-Z]{3}[0-9]{4}\n)/\1/; P; D'

解释:

  • -E   使用扩展正则表达式 (ERE)。如果没有这个,我们就不得不说\{3\}和 \{4\}

  • n   不要自动打印任何内容;只打印我们所说的打印内容。这是可取的,因为,像 一样grep,我们不想为不包含与正则表达式匹配的字符串的输入行打印任何内容。

  • t dummy; : dummy  跳转到紧随其后的 (“ dummy”) 标签。这是一个条件跳转,因此它可能会也可能不会发生。但这并不重要,因为跳转命令和标签之间没有任何东西。

    这看起来像一个无操作,而且确实是这样,只不过它清除了 sed 的内存,以判断是否有成功的操作。s替代命令。

  • s/[a-zA-Z]{3}[0-9]{4}/&\n/  寻找OP的正则表达式。如果找到,则将其自身加上换行符替换(即添加换行符)。

  • T   如果上述替代命令失败(未找到模式),则跳转到脚本末尾并读取下一行输入。  文档因为T说,

    T label

      如果自上次读取输入行以来以及自最后一个or命令以来没有s///成功替换,则分支到tTlabel;如果 label被省略,分支到脚本末尾。这是一个 GNU 扩展。

    这就是我们用这个dummy标签来做这件事的原因——这样 T命令只会查看前一个s命令。

  • s/.*([a-zA-Z]{3}[0-9]{4}\n)/\1/  查找 OP 的正则表达式,前面有任意数量的任意字符 (.*)并后跟一个换行符,并将它们替换为正则表达式匹配和换行符(即删除正则表达式匹配之前的任何文本)。乍一看,这似乎可以找到 最后的匹配就行,因为.*是贪婪的。但它找到了第一个,因为只有第一个匹配后面跟着一个换行符(因为第一个s不是G局部)。

  • P(首都  通过第一个换行符打印模式缓冲区。这只是与正则表达式匹配的字符串(就像grep -o输出一样)。

  • D   通过第一个换行符删除模式缓冲区并跳转到脚本的开头。

答案3

使用 GNU sed 在管道中调用两次可以获得与 grep 相同的输出:

sed -E 's/[a-zA-Z]{3}[0-9]{4}/\n&\n/g' input \ 
 | sed -E '/^[a-zA-Z]{3}[0-9]{4}$/!d'

在概念上:

sed    -E 's/REGEX/\n&\n/g' input \ 
 | sed -E '/^REGEX$/!d'

第一个调用将匹配的正则表达式与周围的换行符隔离。

第二个调用deletes与正则表达式不匹配的所有行。

实际上,它只打印与正则表达式完全匹配的行grep -o

尝试使用一些扩展的正则表达式来匹配,并删除前导或尾随不需要的部分意味着失败。正则表达式引擎将匹配太多(因为任何*)不受限制并且会匹配as much as possible。构建具有环视匹配的 PCRE 可能会解决此问题,但 sed(任何当今的 sed)无法使用 PCRE。

这个解决方案很简单,没有已知的问题(除了如果正则表达式可以匹配“无”它会打印许多空行)。

尝试将这种使用减少到单行 sed 变得(令人惊讶地)相当复杂。其他答案试图通过几个极端情况和复杂的 sed 语法来完成此任务。

我们将继续努力寻找通用解决方案。

答案4

使用GNUsed

$ sed -Ez ':a;s/([a-zA-Z]{3}[0-9]{4})[a-z ]+/\1\n/;ta' input_file
FGH1234
CAS4057
MAX2345

使用sed

$ sed -E 's/([a-zA-Z]{3}[0-9]{4}) [^A-Z]*/\1\
/' input_file
FGH1234
CAS4057
MAX2345

相关内容