繁体   English   中英

使用 Perl 替换不同行上两个字符串中的所有文本

[英]Replace all text within two strings on different lines using Perl

我是一名网络开发人员。 我工作的机构使用 Dreamweaver 的模板/库项目功能。 库项目对于更新导航栏或每个页面上相同的某些内容确实派上用场。 我们大部分时间使用静态 HTML。 因此,要更改菜单项,我们使用 Dreamweaver 库项,我们更新该项一次,然后按更新,它会在项目中的每个 html 页面上更改它。

但是我想制作一个可以从命令行运行的 perl 脚本,而不是打开 GUI,它只会更快。

例如,假设我有一个这样编码的菜单:

<!--MENUITEMS-->
<li><a href="products.html">Products</a></li>
<li><a href="about_us.html">About Us</a></li>
<li><a href="commercial.html">Commercial</a></li>
<li><a href="contact.html">Contact</a></li>
<!--MENUEND-->

我会将 li 项代码存储在它们自己的文件中:nav.lbi:

<li><a href="products.html">Products</a></li>
<li><a href="about_us.html">About Us</a></li>
<li><a href="commercial.html">Commercial</a></li>
<li><a href="contact.html">Contact</a></li>

perl 脚本需要在它扫描的每个文件中将<!--MENUITEMS--><!--MENUEND-->之间的所有文本替换为 nav.lbi 的内容。

我打算首先在 SED 中尝试这个,但 SED 是为逐行内容量身定制的。 我已经成功地使用 SED 在另一个文件的某处插入了整个文本文件,但这有点不同。 使用 Perl,我知道我应该能够用<!--MENUEND-->的内容替换<!---MENUITEMS--><!--MENUEND-->每次出现之间的所有文本,即使它跨越多行。

如果我需要将<!--MENUITEMS--><!--MENUEND-->标签添加到实际的 nav.lbi 文件中,因为它进行了搜索和替换,如果这会更容易,那很好也。 这只是让我可以跨多个 html 文件更新导航栏,而无需触摸 Dreamweaver。

最后要注意的一件事是<!--MENUITEMS-->和结束<!--MENUEND-->因为页眉中的导航通常与页脚中的导航相同,所以我需要递归更新文件。

只需在正则表达式的末尾添加一个s即可使点匹配新行。 man perlre

# or use File::Slurp;
sub slurp {
    my $fn = shift;
    open(IN,$fn);
    return join('',<IN>);
}

my $_ = slurp("in.html");
my $new_menu_items = slurp("nav.lbi");

s/<!--MENUITEMS-->(.*)<!--MENUEND-->/$new_menu_items/s;
print;


要将输出保存到新文件而不只是打印它,您需要指定输出文件并打印到它,因此上面的代码将变为:

sub slurp {
     my $fn = shift;
     open(IN,$fn);
     return join('',<IN>);
 }   

 my $_ = slurp("in.html");
 my $new_menu_items = slurp("nav.lbi");
 open my $output_file, '>', 'output.html';

 s/<!--MENUITEM-->(.*)<!--MENUEND-->/$new_menu_items/s;
 print $output_file $_;

有关在 Perl 中啜饮的更多信息:
http://www.perl.com/pub/2003/11/21/slurp.html

以下内容基于 Dov Grobgelds 的回答,但在他的回答的评论中添加了额外的功能。 准确地说,它对目录中的所有 html 文件运行多行查找和替换并覆盖原始文件:

#!/usr/bin/perl

use strict;
use warnings;

sub slurp {
    my $fn = shift;
    open(IN,$fn);
    return join('',<IN>);
}

my @files = grep ( -f ,<*.html>);
for my $file (@files) {
    print "$file\n";
    my $_ = slurp("$file");
    my $new_menu_items = slurp("nav.lbi");
    open my $output_file, '>', "$file";
    s/<!--MENUITEMS-->(.*)<!--MENUEND-->/$new_menu_items/s;
    print $output_file $_;
}

解释:
第一个问题是如何将输出打印到文件,而不仅仅是终端。 (参考 Dov Grobgelds 的回答来比较原始代码)
1.指定输出文件是什么:

open my $output_file, '>', 'output.html';

然后将其输出打印到文件中:
print; - 来自原始答案
变成:

print $output_file $_;

现在为了让你的脚本在目录中的所有 html 文件上运行,我们需要把这个脚本变成更动态的东西。 我们需要将 vars 中的所有 .html 文件存储在一个数组中,然后遍历该数组,将每个 .html 存储到 var,然后在每个文件上运行 slurp/regex 代码。
首先声明保存 html 文件的变量并用当前目录中的 .html 文件填充它:

my @files = grep ( -f ,<*.html>);

剩下的魔法是通过将原始 slurp 和正则表达式代码包装在 for 循环中来实现的:

#for each file in files array, file is stored via my $file:
for my $file (@files) {
    #unecessary put prints out file filename loop is working on:
    print "$file\n";
    #default var becomes current file contents:
    my $_ = slurp("$file");
    #this part isn't dynamic, its same each loop, there might be a better
    #place to put this:
    my $new_menu_items = slurp("nav.lbi");
    #makes your output filename the same as the input:
    open my $output_file, '>', "$file";
    #runs the regex replace on the file contents of orig file
    s/<!--MENUITEMS-->(.*)<!--MENUEND-->/$new_menu_items/s;
    #saves the contents back out to the original file 
    #(overwriting it with original data loaded after running regex on it:)
    print $output_file $_;
}


现在假设您的结束标记不是唯一的,就像在 Dreamweaver 模板中一样,您可以添加 ? to (.*) 只匹配第一次出现,而不是最后一次:

s/<!--MENUITEMS-->(.*?)<!--LIBRARYITEMEND-->/$new_menu_items/s;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM