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

时间:2015-07-22 17:56:55

标签: regex perl

我是网站开发人员。我工作的代理商使用Dreamweaver作为其模板/库项目功能。库项目真的很方便用于更新导航栏或每页上相同的一些内容。我们大多数时候都使用静态HTML。因此,要更改菜单项,我们使用Dreamweaver库项目,我们更新该项目一次,我们按更新,并在项目的每个html页面上更改它。

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

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

 $destinationPath = base_path() . '/Folder/Subfolder1/Subfolder2'; // replace with your required destination directory

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

<!--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-->

perl脚本需要替换每个文件,它会扫描<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> 之间的所有文本 和<!--MENUITEMS-->的内容为nav.lbi。

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

如果我需要将<!--MENUEND--><!--MENUITEMS-->标记添加到实际的nav.lbi文件中,因为它会进行搜索和替换,如果这样可以更容易,那也没关系。这样我就可以在多个html文件中更新导航栏,而无需触摸Dreamweaver。

最后要注意的是<!--MENUEND-->和结束<!--MENUITEMS-->有多次出现,因为标题中的导航通常与页脚中的导航相同,所以我需要递归更新文件。 / p>

2 个答案:

答案 0 :(得分:1)

只需在正则表达式的末尾添加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

答案 1 :(得分:0)

以下内容建立在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文件上运行,我们需要将这个脚本变成更动态的东西。我们需要将所有.html文件存储在数组中的变量中,然后遍历数组,将每个.html存储到var,然后在每个文件上运行slurp / regex代码。
首先声明保存html文件的变量,并使用当前目录中的.html文件填充它:

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

其余的魔法通过将原始slurp和regex代码包装在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模板中一样,您可以添加吗?到(。*)只匹配第一次出现,而不是最后一次:

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