How can I use 's///' if my string contains a '/'?
I'm using Perl 5.10.6 on Mac 10.6.6. I want to execute a simple search and replace against a file so I tried:
my $searchAndReplaceCmd 开发者_StackOverflow= "perl -pi -e 's/\\Q${localTestDir}\\E//g' ${testSuiteFile}";
system( $searchAndReplaceCmd );
but the problem above is the variable $localTestDir
contains directory separators ("/"
), and this screws up the regular expression ...
Bareword found where operator expected at -e line 1, near "s/\Q/home/selenium"
Backslash found where operator expected at -e line 1, near "Live\" syntax error at -e line 1, near "s/\Q/home/selenium"
Search pattern not terminated at -e line 1.
How do I do a search and replace on a file when the variable in question contains regular expression characters? Thanks.
It seems that $localTestDir
has begins with a /
.
Remedy by changing the regex delimiter to something other than /
:
my $searchAndReplaceCmd = "perl -pi -e 's!\\Q${localTestDir}\\E!!g' ${testSuiteFile}";
From perldoc perlrequick
:
$x = "A 39% hit rate"; $x =~ s!(\d+)%!$1/100!e; # $x contains "A 0.39 hit rate"
The last example shows that
s///
can use other delimiters, such ass!!!
ands{}{}
, and evens{}//
. If single quotes are useds'''
, then the regex and replacement are treated as single-quoted strings.
Question is why you do a search and replace from within perl, through the shell, within perl. Seems like a roundabout way of doing things, and you'll run into problems with shell interpolation.
The \Q ... \E
should override the special characters in your string, so /
"should" not be an issue. From perlre:
\Q quote (disable) pattern metacharacters till \E
Here's an alternative (untested), all perl solution. If you want to be extra certain, exchange the /
delimiter to something else, such as s###
(you can use any character as a delimiter).
use strict;
use warnings;
use File::Copy;
open my $fh, '<', $testSuiteFile or die $!;
open my $out, '>', $testSuiteFile . ".bak" or die $!;
while (<$fh>) {
s/\Q${localTestDir}\E//g;
print $out $_;
}
move($testSuiteFile . ".bak", $testSuiteFile) or die $!;
Or use Tie::File
use strict;
use warnings;
use Tie::File;
tie, my @file, 'Tie::File', $testSuiteFile or die $!;
for (@file) {
s/\Q${localTestDir}\E//g;
}
untie @file;
Changing the delimiters is useful, but more generally you can put a backslash in front of any regular expression character to make it non-special.
So s/\/abc/\/xyz/ will work, although it is not very readable.
The problem is that the substitution of $localTestDir
is happening too soon.
Here is an approach that lets you use /
for your re-delimiters:
system('perl', '-pi',
'-e', 'BEGIN { $dir = shift(@ARGV) };',
'-e', 's/\\Q$dir\\E//g',
$localTestDir,
$suiteTestFile
);
Note that this also protects the contents of $suiteTestFile
from being interpreted by the shell.
精彩评论