1

I try to replace directory part of file fullname in Perl. Something like: got filename 'D:\Texts1\text1' in directory 'D:\Texts1', want to replace it with 'D:\Texts2' and then filename will be 'D:\Texts2\text1'.

I try this code:

$filename = 'D:\Texts1\text1';
$i = 'D:\Texts1';
$o = 'D:\Texts2';
$filename =~ s'$i'$o'g;

And it does not take effect. $filename doesn't changes. I tried to use something like

$i = quotemeta('D:\Texts1');

but it also has not took effect.

2
  • Thank you all! My mistake was that I try to use quotemeta to $i and $o at one time. But only for $i it is needed. Commented Aug 17, 2012 at 14:30
  • Actually the single quotes were also a problem--see my answer. You probably changed from single quotes to another delimiter at some point, and this also helped fix it. Commented Aug 17, 2012 at 14:51

6 Answers 6

2

There are several valid answers here, I would like to compile a comprehensive answer along with mine to make this post easier to read:

Root Cause

$i = 'D:\Texts1';

when used as a regex pattern, "\" should be escaped - what the regex engine want is some ultimate format like: D:\\Texts1. So this doesn't work, however, there are at least 4 different ways to build this format as listed below.

Also to notice, when ' is used as delimiter for match or substitution statement, variable interpolation will be disabled, which renders $filename =~ s'$i'$o'g; almost useless. so first step, change it to use / or {}

Solution 1

use quotemeta, this will effectively escape the "\":

$filename = 'D:\Texts1\text1';
$i = quotemeta('D:\Texts1');
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;

Solution 2

use \Q .. \E, which has similar effects as quotemeta:

$filename = 'D:\Texts1\text1';
$i = 'D:\Texts1';
$o = 'D:\Texts2';
$filename =~ s/\Q$i\E/$o/g; # or s/\Q$i/$o/g also works

Solution 3

escape the "\" in the string explicitly, and use qr to quote the string as regex pattern.

$filename = 'D:\Texts1\text1';
$i = qr 'D:\\Texts1';
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;

Solution 4

escape to the extent that the string is ready for regex:

$filename = 'D:\Texts1\text1';
$i = 'D:\\\\Texts1';
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;
Sign up to request clarification or add additional context in comments.

Comments

1

this doesn't interpolate

$filename =~ s'$i'$o'g;

try using / instead of ', like this:

$filename =~ s/$i/$o/g;

that should work. ' prevents string interpolation, so the variable names appear as string literals. Also, make sure to use the quotemeta like you were doing before.

7 Comments

They should use forward slashes or escape their backslashes as well.
The s can be followed by any character, including a single quote, and that becomes the delimiter for the regex. Try: perl -p -e "s'a'b'g" and type some a's in the input.
Technically, you're right, but it's really bad practice... try running this, then this, and tell me which one works....
It is because of the special meaning of single quotes in this context: they prevent variable interpolation.
+1 @dan1111, I just read it here: If "'" is used as the delimiter, no interpolation is done
|
1

The \ in D:\Texts1 is the problem. You need to escape this metacharacter. For this purpose, the string should be wrapped by \Q and \E.

$filename =~ s/\Q$i\E/$o/g;

2 Comments

You know why escape expicitly doesn't work? $i = 'D:\\Texts'?
lzprgmr: They are essentially the same: perl -E 'say "OK" if q{a\b} eq q{a\\b}'
0

you need to add \Q:

$filename =~s{\Q$i}{$o};

1 Comment

There's only one variable. \E is not necessary but I guess it would be better style
0

In fact you are experiencing a combination of two problems:

  • Single quotes, while valid as a regexp delimiter, have a special meaning: they disable variable interpolation. Thus you are searching your string for the literal pattern $i (if you have warnings enabled, you get a clue to this--it tells you that the variables $i and $o are only used once in your program).
  • As others have pointed out, you also need the \Q...\E construct or quotemeta() in order to avoid interpreting the special characters in your variable as regexp operators.

Comments

-1

Updated code

use strict;
use warnings qw/all/;

my $filename = 'D:\Texts1\text1';
my $i = 'D:\\Texts1';
my $o = 'D:\\Texts2';
$filename =~ s/\Q$i\E/$o/;

print $filename;

Be happy!

4 Comments

Do you really want a regex for the replacement text? I suspect not.
the quotes in the search and replace are invalid, and $o should be q, not qr, it's not a regex
@JohnCorbett you know why $i=qr 'D:\\Texts1' works while $i= 'D:\\Texts1' won't work? we just add the escape ourselves why compared using quotemeta.
The code will not work as backslashes are escaped to \\ in regular expression pattern, after using \Q and \E to escape backslashes in pattern

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.