perl搜索并替换子字符串

时间:2014-09-10 11:43:09

标签: regex perl

我正在尝试搜索子字符串,并在找到子字符串时替换整个字符串。在下面的示例中,someVal可以是我不知道的任何值。

我如何搜索someServer.com并替换整个字符串$ oldUrl和$ newUrl?

我可以在整个字符串上做到这一点:

$directory = "/var/tftpboot";

my $oldUrl = "someVal.someServer.com";
my $newUrl = "someNewVal.someNewServer.com";

opendir( DIR, $directory ) or die $!;
while ( my $files = readdir(DIR) ) {
    next unless ( $files =~ m/\.cfg$/ );
    open my $in,  "<", "$directory/$files";
    open my $out, ">", "$directory/temp.txt";
    while (<$in>) {
        s/.*$oldUrl.*/$newUrl/;
        print $out $_;
    }
    rename "$directory/temp.txt", "$directory/$files";
}

3 个答案:

答案 0 :(得分:2)

您的脚本会删除大部分内容,因为您正在使用.*进行匹配。这将匹配除换行之外的任何字符,从每行的开始到结束,尽可能多次匹配,并替换它。

你所使用的功能已经存在于Perl中,使用-pi命令行开关,因此最好使用它而不是尝试制作自己的功能,一样的方法。您不需要使用单线程来使用就地编辑。你可以这样做:

perl -pi script.pl *.cfg

脚本应包含名称定义和替换,以及您需要的任何错误检查。

my $old = "someVal.someServer.com";
my $new = "someNewVal.someNewServer.com";

s/\Q$old\E/$new/g;

这是使用-pi开关运行时最简单的解决方案,如上所示。 \Q ... \E是quotemeta转义符,它会转义字符串中的元字符(强烈推荐)。

您可能希望阻止部分匹配。如果您匹配foo.bar,则可能不希望与foo.bar.bazsnafoo.bar匹配。为防止部分匹配,您可以放入不同类型的锚点。

  • (?<!\S) - 匹配前不允许任何非空格
  • \b - 匹配字边界

如果您要在上面的示例中替换server1.foo.bar,而不是snafoo.bar,则Word边界将是合适的。否则使用空白边界。我们使用负面外观断言和否定字符类进行双重否定的原因是允许行匹配的开始和结束。

所以,总结一下,我会这样做:

use strict;
use warnings;

my $old = "someVal.someServer.com";
my $new = "someNewVal.someNewServer.com";

s/(?<!\S)\Q$old\E(?!\S)/$new/g;

运行它
perl -pi script.pl *.cfg

如果您想事先尝试一下(强烈推荐!),只需删除-i开关,这将使脚本打印到标准输出(您的终端)。然后,您可以在文件上运行差异以检查差异。 E.g:

$ perl -p script.pl test.cfg > test_replaced.cfg
$ diff test.cfg test_replaced.cfg

您必须决定是否更需要字边界,在这种情况下,您将使用\b替换外观断言。

始终使用

use strict;
use warnings;

即使是像这样的小脚本。它将为您节省时间和麻烦。

答案 1 :(得分:1)

如果要匹配并替换任何子域,则应设计特定的正则表达式以匹配它们。

\b(?i:(?!-)[a-z0-9-]+\.)*someServer\.com

以下是使用更多Modern Perl技术重写脚本,包括Path::Class以跨平台方式处理文件和目录操作,$INPLACE_EDIT自动处理文件编辑。< / p>

use strict;
use warnings;
use autodie;

use Path::Class;

my $dir = dir("/var/tftpboot");

while (my $file = $dir->next) {
    next unless $file =~ m/\.cfg$/;

    local @ARGV = "$file";
    local $^I = '.bak';
    while (<>) {
        s/\b(?i:(?!-)[a-z0-9-]+\.)*someServer\.com\b/someNewVal.someNewServer.com/;
        print;
    }
    #unlink "$file$^I"; # Optionally delete backup
}

答案 2 :(得分:0)

关注Dot-Star:它匹配旧网址周围的所有内容,因此唯一剩下的就是新网址:

s/.*$oldUrl.*/$newUrl/; 

更好:

s/$oldUrl/$newUrl/;

此外,在尝试重命名之前,您可能需要close输出文件。

如果旧网址包含特殊字符(点,星号,美元符号......),则可能需要使用\Q$oldUrl来抑制它们在正则表达式模式中的特殊含义。