在 grep 和 sed 中正确使用正则表达式开关

Posted

技术标签:

【中文标题】在 grep 和 sed 中正确使用正则表达式开关【英文标题】:Proper usage of regex switches in grep and sed 【发布时间】:2021-05-11 08:30:06 【问题描述】: 我已经学习 SED 和 GREP 几个星期了。通常我使用 ATOM 编辑器来构建正则表达式,它对我有很大帮助。现在我只需要几分钟就可以买一个。 但是当我尝试使用 ubuntu 终端对数据文件使用相同的正则表达式时,事情开始变得丑陋。 有人能否提供带有 grep 和 sed 的精确开关,但也有限制(例如 - GNU SED 不能将 \d 用于数字,而是使用 [0-9])。让我们看看下面的文本和要求例如:
192.168.10.10,fe80:0:0:0:bcf6:c04e:cb99:6909,10.0.170.11
172.16.32.44
fe80:0:0:0:84a5:1d2e:55d1:ecf,192.168.4.50
fe80:0:0:0:84a5:1d2e:55d1:ec1
10.10.101.22

经过几个小时的努力,我可以找出grep -P '(\d1,3\.)3\d1,3' 来打印唯一的 IPV4 地址。但这是 PERL 正则表达式开关。所以现在我对使用什么和不使用什么感到非常困惑。请帮助我为以下要求构建完整的 SED 和 GREP 命令(假设输入是一个文件):

1- Print only IPV4 addresses using GREP.
2- Print everything except IPV4 addresses using GREP.
3- Print only IPV4 addresses using SED.
4- Print everything except IPV4 addresses using SED.
5- Replace IPV4 addresses with --- using SED.
6- Replace everything except IPV4 addresses using SED.

【问题讨论】:

sed 不是此任务的最佳选择。您可以为 ip4 地址执行类似 sed -nE 's/^/\t/; s/[^.0-9]/\t/g; s/$/\t/; s/\t[^.]*\t/\t/g; s/\t([0-9.]*)\t/\1\n/g;p' ur_file | sed -n '/[0-9]/p' 的操作 sedgrep 做同样的任务有什么意义?没有“魔术开关”可以用 grep 替换 sed 或用 sed 替换 grep。 Replace everything except IPV4 addresses using SED 替换成什么? 我记得 sed 用于打印,但不确定如何在其中添加正则表达式-cat somefile | sed -rn '/(expression-here)/p' 【参考方案1】:

来自question Validating IPv4 addresses with regexp:

ipv4='((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.)3(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'

正则表达式看起来可以用作扩展正则表达式。

1- 使用 GREP 仅打印 IPV4 地址。

grep -Eo "$ipv4"

2- 使用 GREP 打印除 IPV4 地址之外的所有内容。

我认为打印 grep 行的“除”部分之外的所有内容是不可能的。

3- 使用 SED 仅打印 IPV4 地址。

使用适当的正则表达式编写 sed 脚本,在每个 ipv4 地址后添加换行符。然后将每个非换行字符串替换为 ipv4 后跟换行符,仅将 ipv4 替换为换行符。从模式空间中删除换行符并打印它。顺带一提:

sed -E "s/($ipv4)/&\n/g; s/$/\n/; s/([^\n]*)($ipv4)\n/---\2\n/g; s/\n\n/\n/; s/\n//g"

4- 使用 SED 打印除 IPV4 地址之外的所有内容。

sed -E "s/$ipv4//g"

5- 使用 SED 将 IPV4 地址替换为 ---。

sed -E "s/$ipv4/---/g"

6- 使用 SED 替换除 IPV4 地址之外的所有内容。

作为第 3 点,但不是用 ipv4 和换行符替换非换行符字符串,然后用 ipv4 和换行符替换换行符,删除 ipv4 并保留非换行符部分。顺带一提:

sed -E "s/($ipv4)/&\n/g; s/$/\n/; s/([^\n]*)($ipv4)\n/\1---\n/g; s/\n\n/\n/; s/\n//g"

-E(或-r 选项)` 在技术上是对 POSIX sed 的扩展。我怀疑你会找到没有它的实现 - 如果你这样做了,请将正则表达式转换为基本的正则表达式,它应该可以正常工作。

【讨论】:

非常感谢您抽出时间和如此详尽的回答。只是一个幼稚的问题。对于您的第三个答案,我们可以使用 .*(regex).* 匹配整行,然后选择带有反向引用的 IPV4 吗?例如-'s/.*(regex).*/\1/g。我无法让它工作。如果您能在这方面多花点时间,将不胜感激。 可以,但是如果每行有两个 ipv4 地址,第一个将被删除。 .* 是贪婪的 - 它匹配所有内容,包括内部的 ipv4 地址(如果有)。我想你可以用 perl 负前向环视来做到这一点(我认为这就是它的名字),但是普通的正则表达式没有环视。这就是为什么首先切断线路,使其成为每行一个 ipv4,然后移除部分,然后将其折回。

以上是关于在 grep 和 sed 中正确使用正则表达式开关的主要内容,如果未能解决你的问题,请参考以下文章

Shell编程之正则表达式(sed)

正则表达式学习之grep,sed和awk

使用awk / grep / sed / bash / vim进行正则表达式匹配和打印

正则表达式 - grep、sed、awk - 处理大型文本文件

Shell脚本 正则表达式 grep sed awk 工具

正则表达式之扩展正则