提问人:Aram Papazian 提问时间:10/24/2023 最后编辑:Aram Papazian 更新时间:11/1/2023 访问量:137
在 shell 脚本中对多行模式使用正则表达式
Using regex for multiple line pattern in shell script
问:
我看到了以下stackoverflow如何在shell脚本中将正则表达式用于多行模式,但它并没有完全按照我的意愿。我正在寻找一种基于终端的方式来执行就地(或)正则表达式,它将为我自动更改一些文件。(我可能可以使用xml库/等来做到这一点,但我更喜欢使用终端)。sed
perl
我拥有的文件
Some text
<div class="firstClass secondClass" something="else">
Some random stuff
</div>
Random Text
<div class="thirdClass fifthClass" something="else">
Some random stuff
< is something
< but not /> This
</div>
<div class="fourthClass">
Some random stuff
</div>
Final Text
我试着做一个足够武断的例子来展示几个不同的用例。 我正在尝试将其转换为如下所示的内容:
Some text
<!-- firstClass start -->
Some random stuff
<!-- firstClass end -->
Random Text
<!-- thirdClass start -->
Some random stuff
< is something
< but not /> This
<!-- thirdClass end -->
<!-- fourthClass start -->
Some random stuff
<!-- fourthClass end -->
Final Text
我正在尝试以下代码:
sed -n '/<div class="\([^ "]*\)[^>]*>/,/<\/div>/{s/<div class="\([^ "]*\)[^>]*>/<!-- \1 start -->/;/<\/div>/d;p}' file
但是由于在之前的 stackoverflow 问题中,该人不想要最后一行,因此答案将其删除,这不是我想要的。可以看出,我希望在内部内容之前和之后重复第一段文字。
上面的正则表达式正确地修复了第一行(将 div 更改为注释),但我似乎无法在文本下方复制它。我试图弄乱正则表达式,但我似乎无法让它工作。它还删掉了第一行和最后一行,尽管我想保留它们。有什么想法可以做这样的事情吗?
(PS,是的,我知道我们需要一个就地命令,但出于显而易见的原因,我想在实际使用它之前对其进行测试)sed -i
编辑:关于我正在尝试做的事情的想法的一点补充。虽然上面是 HTML,但这段代码不一定是专门用于 HTML 的(因此我不需要 HTML/XML 处理)。这个想法是:
Some random text before my pattern
PATTERN "info ...
random stuffs
END PATTERN
Some random stuff after pattern
我希望将其转换为
Some random text before my pattern
NEW PATTERN - info
random stuffs
END NEW PATTERN - info
Some random stuff after pattern
所以不一定有html。只是在一些文本上方采用模式,在下面复制它。唯一的条件是不会有文本,所以这就是我想基于它的基础。 将 100% 永远不会有文本。不涉及嵌套,也没有任何边缘情况。它始终与上面所示的模式相同。唯一的“边缘”情况是第一行可能有一些额外的文本,直到我不关心的换行符。该内容始终可以删除。我只关心这个词(也就是直到第一个空格字符或第一个字符。random stuffs
END PATTERN
random stuffs
END PATTERN
PATTERN "info ...
info
"
答:
这是一个简单的 Awk 脚本,它提取之后的第一个令牌,并在替换文本中使用该令牌。class="
awk '/<div class="/ { sub(/.*<div class="/, ""); sub(/[" ].*/, "");
class=$0; print "<--", class, "start -->"; next }
/<\/div>/ { print "<--", class", "end -->"; class=""; next }
1' file >new
就正则表达式匹配而言,这里没有“多行”,只是一个简单的工具,用于记住行之间的某些状态。Awk 仍然一次检查一行(尽管如果需要,更改它也不难;RS
这可能对你有用 (GNU sed):
sed -E '/^<div class="([^ "]*).*/{
s//<!-- \1 start -->/;h;:a;n;/^<\/div>$/!ba;g;s/\bstart/end/}' file
匹配起始 div。
将该行操作为所需的格式并制作副本。
打印/获取下一行,直到结束 div。
将该行替换为副本,并替换为并打印结果。start
end
重复。
对于初学者来说,这里有一个简单的方法,适用于我对特定发布文本的测试
s{<div\s+ class="(\S+) (.*?) </div>}{<!-- $1 --> $2 <!-- $1 end -->}sxg;
修饰符是:这样也匹配换行符(通常不匹配),这样文字空格就会被忽略,这有助于可读性,并且这样才能继续通过字符串,匹配和替换。s
.
x
g
为此,我建议在文件中使用一个程序,而不是命令行程序(“单行”),但由于这里的问题中特别要求这样做
perl -0777 -wpe'
s{<div\s+ class="(\S+) (.*?) </div>}{<!-- $1 --> $2 <!-- $1 end -->}sxg'
该开关使它把整个文件读入变量中,这是 Perl 中许多东西的默认值——在这种情况下是正则表达式的运算符。请参阅 perlrun 中的开关。-0777
$_
s{}{}
在一个更大、更结构化的程序中,你也许可以在变量中拥有开始和结束模式,因为
s{$pbeg (.*?) $pend}{...}sxg
在这种情况下,它会在哪里
my $pbeg = qr{<div\s+ class="(\S+)};
my $pend = qr{</div>}
但是,如果这些模式变得复杂,这可能会变得笨拙/
在第一个示例中,将 GNU awk 用于第三个参数以匹配 () 和强类型的正则表达式常量:
$ cat defs1.awk
BEGIN {
begReg = @/<div\s+class="([^" ]+)/
endReg = @/<\/div>/
begFmt = "<!-- %s start -->"
endFmt = "<!-- %s end -->"
}
$ cat common.awk
match($0,begReg,a) {
key = a[1]
$0 = sprintf(begFmt,key)
}
match($0,endReg,a) {
$0 = sprintf(endFmt,key)
}
{ print }
$ awk -f defs1.awk -f common.awk file1
Some text
<!-- firstClass start -->
Some random stuff
<!-- firstClass end -->
Random Text
<!-- thirdClass start -->
Some random stuff
< is something
< but not /> This
<!-- thirdClass end -->
<!-- fourthClass start -->
Some random stuff
<!-- fourthClass end -->
Final Text
对于您的第二个示例,我们只需要一个新的定义文件,但可以从上面重用:common.awk
$ cat defs2.awk
BEGIN {
begReg = @/PATTERN "([^" ]+)/
endReg = @/END PATTERN/
begFmt = "NEW PATTERN - %s"
endFmt = "END NEW PATTERN - %s"
}
$ awk -f defs2.awk -f common.awk file2
Some random text before my pattern
NEW PATTERN - info
random stuffs
END NEW PATTERN - info
Some random stuff after pattern
请注意,我们只是在 2 个文件的部分中定义所需的输入正则表达式和输出格式,我们不会更改 .所依赖的只是,您可以在正则表达式中定义与开始分隔符匹配的第一个捕获组,以包含要在开始行和结束行中保留/打印的关键信息。BEGIN
defs*.awk
common.awk
你并不严格需要 endReg 匹配,但我使用它,以防你将来需要为其他结束分隔符格式调整它。match()
只需更改为执行与所有其他工具相同的 pseduo 就地编辑即可。awk
awk -i inplace
评论
div
<div>
##some
<div>
而不是##some
或其他字符串。--哼?但是您想在 和 标签之间捕获?随便怎么称呼它,但那是结构化文本。“##some”怎么样?最后的要素是什么?最好是一些已知的格式,您可以在其中使用库,否则您必须编写一个解析器,恐怕它不会是单行的。(除非你真的有一个微不足道的案例<div>
</div>
start
--text
--stop
)