Regex 正则表达式在Shell中的多行上查找和替换

Regex 正则表达式在Shell中的多行上查找和替换,regex,shell,sed,Regex,Shell,Sed,我的问题与我的问题相似,只有一个小的例外 在链接的问题中,用户希望执行以下操作: source: [stuff before] <!--WIERD_SPECIAL_COMMENT_BEGIN--> [stuff here, possibly multiple lines. <!--WIERD_SPECIAL_COMMENT_END--> [stuff after] target: [stuff before] [new content] [stuff aft

我的问题与我的问题相似,只有一个小的例外

在链接的问题中,用户希望执行以下操作:

source:
[stuff before]
<!--WIERD_SPECIAL_COMMENT_BEGIN-->
  [stuff here, possibly multiple lines.
<!--WIERD_SPECIAL_COMMENT_END-->
[stuff after]    

target:
[stuff before]
[new content]
[stuff after]
来源:
[之前的内容]
[这里的东西,可能有多行。
[后面的内容]
目标:
[之前的内容]
[新内容]
[后面的内容]
我的问题与此类似,我想这样做:

source:
[stuff before]
<!--WIERD_SPECIAL_COMMENT_BEGIN-->
  [this]
<!--WIERD_SPECIAL_COMMENT_END-->
<!--WIERD_SPECIAL_COMMENT_BEGIN-->
  [not this]
<!--WIERD_SPECIAL_COMMENT_END-->
[stuff after]    

target:
[stuff before]
[new content]
<!--WIERD_SPECIAL_COMMENT_BEGIN-->
  [not this]
<!--WIERD_SPECIAL_COMMENT_END-->
[stuff after]
来源:
[之前的内容]
[本]
[不是这个]
[后面的内容]
目标:
[之前的内容]
[新内容]
[不是这个]
[后面的内容]
在适当的多行正则表达式中,这很容易做到:

/<!--WIERD_SPECIAL_COMMENT_BEGIN-->.*[this].*<!--WIERD_SPECIAL_COMMENT_END-->/m
/.[this].*/m
但是链接问题中建议的答案使用regex作为范围,这不允许检查两个边界之间的行

有没有办法将一个范围内的所有行添加到模式缓冲区中,这样我就可以一次对所有行执行正则表达式?例如:

sed '
    #range between comment beginning and comment end
    /<!--WIERD_SPECIAL_COMMENT_BEGIN-->/,/<!--WIERD_SPECIAL_COMMENT_END-->/
    #Do something to add the lines in this range to pattern buffer
    /.*[this].*/d
    #Delete all the lines if [this] is in the pattern buffer
' <in.txt >out.txt
sed'
#注释开始和注释结束之间的范围
//,//
#执行一些操作,将此范围内的行添加到模式缓冲区
/.[this].*/d
#如果[this]在模式缓冲区中,则删除所有行
'out.txt

您可以使用
sed
这样做:

解析

/BEGIN/{如果我们遇到BEGIN
:a#通读到最后
N#进入模式空间
/完!/
/\[this\]/d#如果该块包含[this],请将其删除
s/^/[new content]\n/#在块之前插入[new content]
}
按如下方式运行:

sed -f parse.sed infile
输出:

[以前的东西]
[新内容]
[不是这个]
[后面的内容]
免责声明:我是初学者。这肯定不是最好的方法


我在三个步骤中完成了类似的操作。假设您在Linux上运行,您可以执行以下操作:

1) 用特殊字符替换文件中所有出现的换行符:

cat originalText.txt | tr '\n' '~' > temp
2) 使用您最喜欢的方法(我使用perl)执行正则表达式,将特殊字符的实例放置在您希望换行的每个位置。确保保持特殊换行字符的完整性

3) 此时,以另一种方式执行第一个命令:

cat temp | tr '~' '\n' > modText.txt
我希望这会有所帮助。

有没有办法将一个范围内的所有行添加到模式缓冲区中,以便我可以一次对所有行执行正则表达式

当然,使用保留空间。例如:

sed -n '/begin/,/end/{ /begin/{h;d};H}; /end/{g;s/\n/<newline>/gp}'
sed-n'/begin/,/end/{/begin/{h;d};h};/end/{g;s/\n//gp}'
将用文本替换与“开始”和“结束”匹配的行之间的换行符

这可能适合您(GNU-sed):

sed':a;$!N;/^/!{P;D};/$/!ba;s/\[this\]/[new content]/;P;D'文件

使用Perl,它相对简单

perl -0777pe 's/<!--BEGIN-->\n(?:(?!<!--END-->\n).)*?\[this\].*?\n<!--END-->\n/[new content]\n/s' in.txt
perl-0777pe的/\n(?:(?!\n)。*?\[this\].\n\n/[new content]\n/s'in.txt
Perl提供的好处是:(a)“slurp模式”(slurp mode)一次就可以拉入整个输入文件,而不是
sed
的逐行处理;(b)允许点匹配换行符的
/s
regex标志;(c)吝啬的重复操作符
*?
和friends,使重复尽可能少地匹配,而不是尽可能多地匹配;最后(d)负前瞻
(?!…)
,允许您在负前瞻表达式匹配的地方禁止匹配。(没有这一点,如果“stuff before”文本中有一个“false”起始分隔符,那么即使是很小的匹配也会在一个结束分隔符之间匹配。)……当然,(e)一种通用编程语言,其中
sed
只适用于相对简单的文本处理任务


(我使用了更简单的开头和结尾分隔符。我希望“wierd”是故意拼错的。)

你的“简单”regex可能没有达到您预期的效果:它可能会找到一个区域,从第一个开始注释开始,到第三个结束注释结束。有什么建设性的话要说吗?也许告诉我为什么这可能不符合我的想法?我确实有一个解决方案,但解决了我自己的问题原因是regex和region都是贪婪的:它们总是尽可能匹配最长的模式,即使这意味着跳过“end”模式到达那里。抱歉,尝试了。只删除上面有[this]的行。也尝试了/*[this].*/d同样的事情。似乎对range@MichaelAllen:我已使用working code samplean选项进行了更新,但很可能失败。正在处理的文件是生成的文件。我选择的字符实例很可能在最后被替换为\n不应该被替换的字符实例…使用外来字符作为替代品怎么样?我的意思是ascii有很多替代字符,这些字符不可能出现在生成的文件中。(当然,除非它有二进制部分)编辑:我尝试使用不可见字符,如“/30”,但没有遇到任何丢失错误。编辑2:Regex支持不可打印字符,特别是使用\cA到\cZ的ascii控制字符。也许可以尝试这些字符。
perl -0777pe 's/<!--BEGIN-->\n(?:(?!<!--END-->\n).)*?\[this\].*?\n<!--END-->\n/[new content]\n/s' in.txt