2014-09-10 127 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; 

。這將節省您的時間和頭痛。