4
use strict;
use warnings;

my $newPasswd = 'abc123';
my @lines = ( "pwd = abc", "pwd=abc", "password=def", "name= Mike" );

my %passwordMap = (
    'pwd(\\s*)=.*'      => 'pwd\\1= $newPasswd',
    'password(\\s*)=.*' => 'password\\1= $newPasswd',
);

print "@lines\n";

foreach my $line (@lines) {
    while ( my ( $key, $value ) = each(%passwordMap) ) {
        if ( $line =~ /$key/ ) {
            my $cmdStr = "\$line =~ s/$key/$value/";
            print "$cmdStr\n";
            eval($cmdStr);
            last;
        }
    }
}

print "@lines";

run it will give me the correct results:

pwd = abc pwd=abc password=def name= Mike
$line =~ s/pwd(\s*)=.*/pwd\1= $newPasswd/
\1 better written as $1 at (eval 2) line 1 (#1)
$line =~ s/password(\s*)=.*/password\1= $newPasswd/
\1 better written as $1 at (eval 3) line 1 (#1)
pwd = abc123 pwd=abc password= abc123 name= Mike

I don't want to see the warnings, tried to use $1 instead of \1, but it does not work. What should I do? Thanks a lot.

John
  • 842
  • 1
  • 8
  • 12
  • I don't have time to try it now, but try using regex references instead of uninterpolated strings. `'pwd(\\s*)=.*' => qr/pwd$1= $newPasswd/,`. Further, if you need backreferences in the first part of your tuple, consider just using an array and stripping two items at once, OR if this gives you the willies, use an array of array refs like so: `my @pwMap = ([ 'foo','bar'], ['baz', 'buz']); These methods are better than a hash in that any scalar value can be stored in the first element of each ordered pair. – daotoad Apr 21 '11 at 17:13
  • Possible duplicate of [What is the difference between using $1 vs \1 in Perl regex substitutions?](http://stackoverflow.com/questions/3068236/what-is-the-difference-between-using-1-vs-1-in-perl-regex-substitutions) – Eliah Kagan Feb 03 '17 at 09:04

3 Answers3

7

\1 is a regex pattern that means "match what was captured by the first set of capturing parens." It makes absolutely no sense to use that in a replacement expression. To get the string captured by the first set of capturing parens, use $1.

$line =~ s/pwd(\s*)=.*/pwd\1= $newPasswd/

should be

$line =~ s/pwd(\s*)=.*/pwd$1= $newPasswd/

so

'pwd(\\s*)=.*'      => 'pwd\\1= $newPasswd',
'password(\\s*)=.*' => 'password\\1= $newPasswd',

should be

'pwd(\\s*)=.*'      => 'pwd$1= $newPasswd',
'password(\\s*)=.*' => 'password$1= $newPasswd',

or better yet

qr/((?:pwd|password)\s*=).*/ => '$1= $newPasswd',
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

I see a lot of repetition in your code.

Assuming you're using Perl 5.10 or later, this is how I would have written your code.

use strict;
use warnings;
use 5.010;

my $new_pass = 'abc123';
my @lines = ( "pwd = abc", "pwd=abc", "password=def", "name= Mike" );

my @match = qw'pwd password';
my $match = '(?:'.join( '|', @match ).')';

say for @lines;
say '';

s/$match \s* = \K .* /$new_pass/x for @lines;
# which is essentially the same as:
# s/($match \s* =) .* /$1$new_pass/x for @lines;

say for @lines;
Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
0

Assuming that the pattern of your pattern matching map stays the same, why not get rid of it and say simply:

$line =~ s/\s*=.*/=$newPassword/
Ingo
  • 36,037
  • 5
  • 53
  • 100
  • Looks like he's trying to *preserve* the whitespace. You might need a `\K` after the `\s*`--or perhaps you meant to start the match at `=` – Axeman Apr 21 '11 at 20:37