我如何使用super-sed的Perl正则表达式点匹配?

时间:2017-01-14 07:51:51

标签: regex sed

我试图使用super-sed的Perl正则表达式/ S,但却无法使用它。此标志使点匹配换行符。这将是一个非常方便的工具,只要我能理解它是如何使用的!例如,我希望以下命令匹配并替换跨越换行的模式以替换为Xs:

echo "(123) 456-7890\n(212) 567-9050" | ssed -R -e "s/78.*?5/x/S"

所以,我期待这个输出:

(123) 456-XXXX
XXXXXXX67-9050

相反,我得到(不匹配):

(123) 456-7890
(212) 567-9050

2 个答案:

答案 0 :(得分:2)

与sed一样,它以基于行的方式工作。如果您想同时处理多行,则必须先获取它们。在sed(和ssed)中这样做的一种方法是

:a $! { N; ba; }

:a是跳转标签,N抓取下一行,ba跳回:a$!检查确认只要有更多行要读,就会发生。

一旦我们有了这个,另一个难点是将X的正确数量放到正确的位置。与sed一样,这样做并不会使这非常方便,并且需要使用保持缓冲区进行一些改组以使替换部分隔离并准备好进行处理。我想出了以下内容:

$ ssed -R ':a $! { N; ba; }; h; s/(.*?78)(.*?5)(.*)/\2/S; s/./X/g; s/^/@/; x; G; s/(.*?78)(.*?5)(.*)\n@(.*)/\1\4\3/S' << EOF
> (123) 456-7890
> (212) 567-9050
> EOF
(123) 456-78XX
XXXXXXX67-9050

其工作原理如下:

:a $! { N; ba; }                     # read full input into pattern space
h                                    # save a copy of it in the hold buffer
s/(.*?78)(.*?5)(.*)/\2/S             # isolate the part to substitute
s/./X/g                              # replace non-newlines with X
s/^/@/                               # Put an @ as marker before the X's.
x                                    # Swap hold buffer and pattern space
G                                    # append hold buffer (now the X's) to
                                     # the pattern space. The PS now contains
                                     # the input followed by an @ followed by
                                     # the X's.
s/(.*?78)(.*?5)(.*)\n@(.*)/\1\4\3/S  # Use the @ marker (that we know to be
                                     # the last @ in the PS) to isolate the
                                     # X's and the original regex to isolate
                                     # the part we want to replace, then
                                     # reassemble.

正如您所看到的,这与sed中的情况一样糟糕,所以我仍然建议使用Perl可能更合理:

$ perl -0777 -pe 's/(?<=78)(.*?5)/$1=~s{[^\n]}{X}gr/se' << EOF
> (123) 456-7890
> (212) 567-9050
> EOF
(123) 456-78XX
XXXXXXX67-9050

这里,-0777选项将perl置于slurp模式,这使得它一次性读取整个输入而不是行,并且代码是一个简单的替换,其中

  • (?<=78)是一个lookbehind表达式,如果前面有78
  • ,则匹配空字符串
  • /e使我们能够在s///的替换子句中使用perl表达式,并且
  • $1=~s{[^\n]}{X}gr进行第一次捕获并用X替换其中的所有非换行符号,从而产生替换结果。然后将其替换为(.*?5)匹配的字符串。

答案 1 :(得分:1)

NOOOO !!!!人们使用sed进行各种古怪的阴谋诡计已经够糟糕了,但是现在有更多疯狂的符文组合超级sed ???

你没有告诉我们sseds /S命令是做什么的所以我猜它是为了在多行块中进行替换,但sed是针对各行的简单替换,就是这样,你应该忘记你听说过super-sed。对于任何与操作文本相关的有趣内容,您应该使用awk,例如使用GNU awk进行多字符RS:

$ printf "(123) 456-7890\n(212) 567-9050\n" |
awk -v RS='78[^5]*5' -v ORS= '{print $0 gensub(/[^\n]/,"X","g",RT)}'
(123) 456-XXXX
XXXXXXX67-9050

或者如果您不想更换78

$ printf "(123) 456-7890\n(212) 567-9050\n" |
awk -v RS='78[^5]*5' -v ORS= '{print $0 substr(RT,1,2) gensub(/[^\n]/,"X","g",substr(RT,3))}'
(123) 456-78XX
XXXXXXX67-9050

或:

$ printf "(123) 456-7890\n(212) 567-9050\n" |
awk -v RS='^$' -v ORS= 'match($0,/(.*78)([^5]*5)(.*)/,a){print a[1] gensub(/[^\n]/,"X","g",a[2]) a[3]}'
(123) 456-78XX
XXXXXXX67-9050

如果由于某些原因你不喜欢它,那么只需使用perl,它就像ssed一样随时可用,可能更多!