Using a Perl variable as the substitute expression - perl

I want to read the substitute expressions flexibly from external data,
so my problem could be reduced to the following:
my $pattern = "s/a/b/g";
my $string = "abcd";
$string =~ $pattern;
print("$string\n");
This is not functioning, but where is my problem?
Or does it even have no solution?

$pattern doesn't contain a regex pattern; it contains a bit of Perl source code. To evaluate Perl code, you need eval EXPR (or do or require).
Requiring Perl code from a user is a bad idea, though. Instead, I recommend the requiring the pattern and the replacement separately, as in the following:
my $pattern = 'a';
my $replacement = 'b';
my $string = 'abcd';
$string =~ s/$pattern/$replacement/g;
Use a function from String::Substitution if you want to allow $1 and such to be allowed in the replacement expression.
use String::Substitution qw( gsub_modify );
my $pattern = '(a)(b)';
my $replacement = '$2$1';
my $string = 'abcd';
gsub_modify($string, $pattern, $replacement);

Related

Trivial search/replacement in Perl without error?

How to simply do a search/replace in Perl? The following example doesn't work:
#!/usr/bin/env perl
use strict;
use warnings;
my $text = '***/**/*abc*/***//*';
my $search = '/*abc*/';
my $replace = '#def#';
print "$text\n";
$text =~ s/$search/$replace/g;
print "$text\n";
$search is not treated as a string, but as a regular expression. The character * has a special meaning in regular expressions so you need to quote it. Try:
$search = quotemeta '/*abc*/';
Make sure that content of $search has quoted meta-chars by using \Q or quotemeta in order to be treated as literal string,
$text =~ s/\Q$search/$replace/g;

Unmatched ) in reg when using lc function

I am trying to run the following code:
$lines = "Enjoyable )) DAY";
$lines =~ lc $lines;
print $lines;
It fails on the second line where I get the error mentioned in the title. I understand the brackets are causing the trouble. I think I could use "quotemeta", but the thing is that my string contains info that I go on to process later, so I would like to keep the string intact as far as possible and not tamper with it too much.
You have two problems here.
1. =~ is used to execute a specific set of operations
The =~ operator is used to either match with //, m//, qr// or a string; or to substitute with s/// or tr///.
If all you want to do is lowercase the contents of $lines then you should use = not =~.
$lines = "Enjoyable )) DAY";
$lines = lc $lines;
print $lines;
2. Regular expressions have special characters which must be escaped
If you want to match $lines against a lower case version of $Lines, which should return true if $lines was already entirely lower case and false otherwise, then you need to escape the ")" characters.
#!/usr/bin/env perl
use strict;
use warnings;
my $lines = "enjoyable )) day";
if ($lines =~ lc quotemeta $lines) {
print "lines is lower case\n";
}
print $lines;
Note this is a toy example trying to find a reason for doing $lines =~ lc $lines - It would be much better (faster, safer) to solve this with eq as in $lines eq lc $lines.
See perldoc -f quotemeta or http://perldoc.perl.org/functions/quotemeta.html for more details on quotemeta.
=~ is used for regular expressions. "lc" is not part of regex, it's a function like this: $new = lc($old);
I don't recall the regex operator for lowercase, because I use lc() all the time.

regular expression to match file path in perl

I'm new to Perl and i'm trying to extract the path of a file. Please help me with a suitable regular expression, this is my code:
$string = "D:/EZ-VPN/NKEMSL0-V02.txt------vpnclient server 156.37.253.97";
I want to extract "D:/EZ-VPN/NKEMSL0-V02.txt" and "156.37.253.97" and store it in 2 scalar variables. Please suggest a regular expression to extract these.
thanks in advance
#!/usr/bin/perl
use strict;
my $string = "D:/EZ-VPN/NKEMSL0-V02.txt------vpnclient server 156.37.253.97";
$string =~ m/(.*?)--+.* (\d+\.\d+\.\d+\.\d+)/;
print $1."\n";
print $2."\n";
This should work for you.
Perl gathers the results from the regex's brackes (so called capture groups) in the $1, $2 ... $n variables.
The filename is in $1, the IP adress is in $2.
Using the string of 6 consecutive dashes to mark the end of the path:
my($path, $ipaddress) = ($string =~ m/(.*?)------.* (\d+\.\d+\.\d+\.\d+)/);
Test script:
#!/usr/bin/env perl
use strict;
use warnings;
my $string = "D:/EZ-VPN/NKEMSL0-V02.txt------vpnclient server 156.37.253.97";
my($path, $ipaddress) = ($string =~ m/(.*?)------.* (\d+\.\d+\.\d+\.\d+)/);
print "path = $path; IP = $ipaddress\n";
Output:
path = D:/EZ-VPN/NKEMSL0-V02.txt; IP = 156.37.253.97
my ($x, $y) = split /------/, $string;
my ($z) = $y =~ /(\S+)\z/;

How to substitute arbitrary fixed strings in Perl

I want to replace a fixed string within another string using Perl. Both strings are contained in variables.
If it was impossible for the replaced string to contain any regex meta-characters, I could do something like this:
my $text = 'The quick brown fox jumps over the lazy dog!';
my $search = 'lazy';
my $replace = 'drowsy';
$text =~ s/$search/$replace/;
Alas, I want this to work for arbitrary fixed strings. E.g., this should leave $text unchanged:
my $text = 'The quick brown fox jumps over the lazy dog!';
my $search = 'dog.';
my $replace = 'donkey.';
$text =~ s/$search/$replace/;
Instead, this replaces dog! with donkey., since the dot matches the exclamation mark.
Assuming that the variable contents themselves are not hardcoded, e.g., they can come from a file or from the command line, is there a way to quote or otherwise markdown the contents of a variable so that they are not interpreted as a regular expression in such substitution operations?
Or is there a better way to handle fixed strings? Preferably something that would still allow me to use regex-like features such as anchors or back-references.
Run your $search through quotemeta:
my $text = 'The quick brown fox jumps over the lazy dog!';
my $search = quotemeta('dog.');
my $replace = 'donkey.';
$text =~ s/$search/$replace/;
This will unfortunately not allow you to use other regex features. If you have a select set of features you want to escape out, perhaps you can just run your $search through a first "cleaning" regex or function, something like:
my $search = 'dog.';
$search = clean($search);
sub clean {
my $str = shift;
$str =~ s/\./\\\./g;
return $str;
}
Wrap your search string with \Q...\E, which quotes any meta characters within.
$text =~ s/\Q$search\E/$replace/;
#Replace a string without using RegExp.
sub str_replace {
my $replace_this = shift;
my $with_this = shift;
my $string = shift;
my $length = length($string);
my $target = length($replace_this);
for(my $i=0; $i<$length - $target + 1; $i++) {
if(substr($string,$i,$target) eq $replace_this) {
$string = substr($string,0,$i) . $with_this . substr($string,$i+$target);
return $string; #Comment this if you what a global replace
}
}
return $string;
}

Is there a Perl idiom which is the functional equivalent of calling a subroutine from within the substitution operator?

Perl allows ...
$a = "fee";
$result = 1 + f($a) ; # invokes f with the argument $a
but disallows, or rather doesn't do what I want ...
s/((fee)|(fie)|(foe)|(foo))/f($1)/ ; # does not invoke f with the argument $1
The desired-end-result is a way to effect a substitution geared off what the regex matched.
Do I have to write
sub lala {
my $haha = shift;
return $haha . $haha;
}
my $a = "the giant says foe" ;
$a =~ m/((fee)|(fie)|(foe)|(foo))/;
my $result = lala($1);
$a =~ s/$1/$result/;
print "$a\n";
See perldoc perlop. You need to specify the e modifier so that the replacement part is evaluated.
#!/usr/bin/perl
use strict; use warnings;
my $x = "the giant says foe" ;
$x =~ s/(f(?:ee|ie|o[eo]))/lala($1)/e;
print "$x\n";
sub lala {
my ($haha) = #_;
return "$haha$haha";
}
Output:
C:\Temp> r
the giant says foefoe
Incidentally, avoid using $a and $b outside of sort blocks as they are special package scoped variables special-cased for strict.