在sed命令中无法使提到的正则表达式工作

6

我正试图在bash中使用sed命令来使以下正则表达式起作用。

^[^<]?(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*))[^>]?$

我知道这个正则表达式是正确的,它的工作方式符合我的预期。因此,不需要帮助。我已在在线正则表达式测试器上测试过了,并且按照我的期望工作。

请在这里找到上述正则表达式的演示。

我的要求:我想将每个URL都包含在<>中。如果URL已经被包含在其中,则像上面链接中所示将其附加到结果中。

示例输入:(在名为website.txt的文件中)

// List of all legal urls
https://www.google.com/
https://www.fakesite.co.in
https://www.fakesite.co.uk
<https://www.fakesite.co.uk>
<https://www.google.com/>

预期输出:(在名为output.txt的文件中)

<https://www.google.com/> // Please notice every url is enclosed in the <>.
<https://www.fakesite.co.in>
<https://www.fakesite.co.uk>
<https://www.fakesite.co.uk> // Please notice if the url is already enclosed in <> then it is appended as it is.
<https://www.google.com/>

我在 sed 中尝试的内容:

  1. Since I'm not well-versed in bash commands; so previously I was not able to capture the group properly in sed but after reading this answer; I figured out that we need to escape the parenthesis to be able to capture it.

  2. Somewhere; I read that look-arounds are not supported in sed(GNU based) so I removed lookarounds too; but that also didn't worked. If it doesn't support look-arounds then I used this regex and it served my purpose.

  3. Then; this is my latest try with sed command:

    sed 's@^[^<]?(https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]*))[^>]?$@<\1>@gm;t;d' websites.txt > output.txt
    

我的具体问题:

如何使上面的命令正常工作。如果您运行我在第3点中提供的示例命令,则会发现它未正确替换内容。它只是将websites.txt的内容转储到output.txt中。但是在上面的正则表达式演示中,它可以正常运行,即将所有未封闭的网站包含在<>中。任何建议都将有所帮助。我更喜欢使用sed,但如果可能的话,上述命令是否也可以转换为awk?如果可能的话,请帮助我完成,我将不胜感激。谢谢。


评论不适合进行长时间的讨论;此对话已被移至聊天室 - Samuel Liew
3个回答

4

工作了很久后,我让我的sed命令工作了。下面是成功的命令。

sed -E 's@^[^<]?(https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&=]*))[^>]?$@<\1>@gm;t' websites.txt > output.txt

您可以在这里找到该命令的示例实现。

由于正则表达式已经满足我为谁撰写此要求的个人需求,因此我只需要有关命令语法的帮助(虽然任何改进都受到欢迎),并且希望命令能够使用相同的正则表达式模式。

我之前不知道但现在学到了以下内容:

  1. 我之前不知道-E标志的任何内容。现在我知道了-E使用POSIX“扩展”语法(“ERE”)。感谢@GordonDavisson@Sundeep详细请阅读。

  2. 我不清楚sed不支持环视。但现在我知道sed不支持环视。感谢@ dmitri-chubarov 详细请阅读

  3. 我之前不知道sed也不支持非捕获组。感谢@Sundeep解决了这个问题。 深入阅读

  4. 我不知道GNU sed是一个具体的命令行工具。感谢@oguzismail帮助我理解。详细请阅读。


1
我要向@GordonDavisson、Sundeep、dmitri-chubarov、oguzismail、RavinderSingh13表示衷心的感谢,他们帮助我解决了这个问题。 - user7571182

2

关于你的回答中的命令:

sed -E 's@^[^<]?(https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&=]*))[^>]?$@<\1>@gm;t'

以下是一些注意事项:

您发布的样本输入每行有1个URL,因此我认为您sed命令末尾的gm;t没有任何用处,因此您的输入不足或您的脚本有误。

硬编码范围a-zA-Z0-9在不同的语言环境中包含不同的字符。如果您想包括所有(且仅包括)小写字母、大写字母和数字,则应将a-zA-Z0-9替换为POSIX字符类[:alnum:]。因此,请更改为使用与语言环境无关的字符类,或根据您在正则表达式中需要匹配哪些字符的要求,在命令行上指定所需的语言环境。

像大多数字符一样,在方括号表达式内,字符+是文字意义,因此不应该转义 - 将\+更改为+

方括号表达式[^<]?表示“任意不是<的字符出现1次或0次”,对于[^>]?也是如此,因此如果您的“url”开头/结尾包含随机字符,则会被接受,例如:

echo 'xhttp://foo.bar%' | sed -E 's@^[^<]?(https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&=]*))[^>]?$@<\1>@gm;t'
<http://foo.bar%>

我认为你想使用<?>?而不是[^<]?[^>]?
你的正则表达式将允许一个没有字母的"url"。
echo 'http://=.9' | gsed -E 's@^[^<]?(https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&=]*))[^>]?$@<\1>@gm;t'
<http://=.9>

如果您编辑您的问题,提供更具代表性的样本输入和期望输出(包括您不想匹配的情况),那么我们可以帮助您。但是根据对有效URL的快速谷歌搜索,看起来有几个有效URL会被您的正则表达式禁止,并且有几个无效的URL将被允许,因此您可能需要在标记为url或类似标记的问题中询问此事(使用您当前拥有的标记,我们可以帮助您实现您的正则表达式,但可能有更好的人来帮助定义您的正则表达式)。

那么,要么改用与语言环境无关的字符类,你能详细说明一下吗?你是指像 [abcdefghijklmno...] 这样的吗? - oguz ismail
1
@oguzismail 不,按照POSIX术语(比模糊的前POSIX术语更清晰),那是括号表达式中的字符列表,而不是字符类。一个字符类应该是[:alnum:],如果在括号表达式内使用,则应为[[:alnum:]]。我只是想说,如果您想将“a-z”用于特定含义,请设置LC_ALL或类似环境变量来定义它的含义,否则您的代码将在不同的区域设置下表现不同。 - Ed Morton
我明白了@EdMorton。我发现还有其他情况。我已经向客户提出了这个问题,并建议使用perl而不是sed来创建更加可靠的正则表达式。我正在等待他的批准。我可以自己修复正则表达式。但是,对于更多的边缘情况(你提到的),我可能需要使用环视。我完全理解了你的观点。非常感谢你的回答。我学到了一个新东西;就是使用[[:alnum:]]来表示特定区域设置的值。我一定会记住这个方法以备将来之需。非常感谢。如果他同意使用perl,我一定会与您分享更新后的正则表达式。 - user7571182
1
不用谢。我不知道为什么使用perl会使事情变得更容易,但如果您的客户接受使用perl并创建了一个PCRE,则我将无法提供进一步的帮助,因为我坚持使用标准UNIX工具(例如sed和awk)与BRE和ERE,所以我不知道perl脚本或PCRE意味着什么,无法提供任何建议。当然,其他人可能可以。祝你好运。 - Ed Morton
1
顺便说一句,我刚刚谷歌搜索了“匹配URL的正则表达式”,找到了这个链接,希望对你有所帮助:https://dev59.com/NHVC5IYBdhLWcg3w21Iq - Ed Morton
EdMorton先生,perl支持正则表达式中的顺序环视和非捕获组,所以我认为它是编写强大正则表达式的更好选择。如果我错了,请纠正我。另外,您提供的第二个链接非常棒,我会阅读所有答案并找出最好的一个。EdMorton先生,您的回答非常详细,感谢您的启发。 - user7571182

1
如果输入文件只是一个注释,后面跟着一系列的 URL 列表,请尝试:
sed '1d;s/^[^<]/<&/;s/[^>]$/&>/' websites.txt

输出:

<https://www.google.com/>
<https://www.fakesite.co.in>
<https://www.fakesite.co.uk>
<https://www.fakesite.co.uk>
<https://www.google.com/>

1
@Mandy8055,关于“篡改”:Stack Overflow 是一个通过开源示例学习代码的平台。学习编程可以使学生成为程序员或代码的作者。开源程序员有权利随意更改代码,无论更改后的代码本身是有缺陷还是高明,这正好与篡改相反。 - agc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接