2014-09-10 68 views
1

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

我如何搜索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"; 
} 
+0

替换中的'。*'使得整行匹配并被替换,并且我敢肯定你不想删除整行。只匹配你想要替换的东西。 – TLP 2014-09-10 11:46:21

回答

1

如果你想匹配和替换任何子域,那么你应该设计一个特定的正则表达式来匹配它们。

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

以下是脚本的重写使用更现代的Perl技术,包括Path::Class在跨平台的方式和$INPLACE_EDIT自动处理一个文件的编辑处理的文件和目录操作。

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 
} 
0

关注的点星:它匹配的是围绕旧的URL一切,所以剩下的就行了唯一的将是新的网址:

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

更好:

s/$oldUrl/$newUrl/; 

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

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

+0

因此,如果$ oldURl =“someServer”,整行将被替换为“someNewVal.someNewServer.com”? – bart2puck 2014-09-10 11:50:23

+0

它总是将'$ oldUrl'放在'\ Q' ...'\ E'中是个好主意。 URL往往至少有点,我们不希望'wwwxexample.com'匹配'www.example.com'。 – tuomassalo 2014-09-10 12:10:55

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。否则使用空白边界。我们做一个双重否定的原因是负面的断言断言和否定的字符类是允许行匹配的开始和结束。

所以,总结起来,我会做:

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开关,这将使脚本打印到标准输出(您的终端)。然后,您可以在文件上运行差异来检查差异。例如: -

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

你将不得不决定文字边界是否是更加希望的,在这种情况下,你\b更换环视断言。

即使在这样小的脚本始终使用

use strict; 
use warnings; 

。这将节省您的时间和头痛。