开发者

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 as s!!! and s{}{}, and even s{}//. If single quotes are used s''', 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜