regex 在shell脚本中使用正则表达式处理多行模式

tpgth1q7  于 2023-11-20  发布在  Shell
关注(0)|答案(4)|浏览(146)

我看过下面的stackoverflow How to use regex for multiple line pattern in shell script,但它并不完全是我想要的。我正在寻找一种基于终端的方式来做一个就地sed(或perl)正则表达式,它将自动为我改变一些文件。(我可能可以用xml库/等来做,但我更喜欢使用终端)。
我手上的文件

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作为就地命令,但我想在实际运行它之前对其进行测试,原因很明显)

**Edit:**关于我想做的事情的想法的一个小补充。虽然上面是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。只是一些在文本上有模式的东西,唯一的条件是random stuffs不会有文本END PATTERN,所以这就是我想要的基础。random stuffs将100%永远不会有END PATTERN文本。没有嵌套,也没有任何边缘情况。它总是与上面显示的模式相同。唯一的“边缘”情况是,第一行PATTERN "info ...可能有一些额外的文本,直到我不关心的换行符。这可以 * 总是 * 被删除。我只关心单词info(又名直到第一个空格字符或第一个"字符)。

jtjikinw

jtjikinw1#

对于初学者来说,这里有一个简单的take,它在我对特定发布文本的测试中起作用

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'


-0777开关使其将整个文件读入$_变量,这是Perl中许多事情的默认值--在本例中是regex的s{}{}运算符。
在一个更大、更结构化的程序中,你可能会在变量中有开始和结束模式,

s{$pbeg (.*?) $pend}{...}sxg


在这种情况下,

my $pbeg = qr{<div\s+ class="(\S+)};
my $pend = qr{</div>}


然而,如果这些模式变得复杂,

jvlzgdj9

jvlzgdj92#

这可能对你有用(GNU sed):

sed -E '/^<div class="([^ "]*).*/{
          s//<!-- \1 start -->/;h;:a;n;/^<\/div>$/!ba;g;s/\bstart/end/}' file

字符串
匹配起始div。
将该行处理为所需的格式并制作副本。
打印/获取下一行,直到div结束。
将该行替换为副本,并将start替换为end,然后打印结果。
重复.

zpgglvta

zpgglvta3#

在第一个例子中使用GNU awk的第三个arg to match()和强类型regexp常量:

$ 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个defs*.awk文件的BEGIN部分中定义了所需的输入regexp和输出格式,我们没有更改common.awk中的其余代码。所有这一切都取决于您可以在regexp中定义第一个捕获组,该捕获组与您的开始行匹配,以包含您希望在开始行和结束行中保留/打印的关键信息。
对于endReg匹配,您并不严格需要match(),但我使用它是为了防止您将来需要调整它以用于其他结尾格式。
只需将awk更改为awk -i inplace,即可执行与所有其他工具相同的伪原地编辑。

xmd2e60i

xmd2e60i4#

下面是一个简单的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)。

相关问题