Search two perl regex and store in one declared variable - regex

So this is the scenario:
I perform a perl regex search on a string and store it in a variable
I then want to retrieve a substring from that variable and save it under the same variable
When I try with two separate variables it works, but I would like to minimies my variable declaration. What currently works:
my ($temp1) = ($buildLog =~ /(build\_version.*\d*)/);
my ($buildVersion) = ($temp1 =~ /(\d.*)/);
print "$temp1\n"; #$temp1 contains: build_version = 1411450178
print "$buildVersion\n"; #$buildVersion contains: 1411450178
But when I try to do it with one variable it only prints out the 1 ie that it found the match, but I would like the actual value. See below:
my ($temp2) = ($buildLog =~ /(build\_version.*\d*)/);
$temp2 = ($temp2 =~ /(\d.*)/);
print "$temp2\n"; #$temp1 just prints out 1
Could anybody please provide a quick explination of the behaviour and if it is indeed possible to use only one variable to get the content of the search ?
Thanks,
CJ

Only one regex is needed:
use strict;
use warnings;
my $buildLog = 'foobar build_version = 1411450178 bazbiz';
my ($buildVersion) = $buildLog =~ /build_version\D*(\d+)/;
print "$buildVersion\n";
Outputs:
1411450178

This answer has the correct solution for what you are trying to do. I just wanted to provide a quick explanation of why your code is not working as you expect.
You are confusing scalar and list modes. Your code
$temp2 = ($temp2 =~ /(\d.*)/);
is taking the results of the match (in a list context) and assigning it to a scalar. This assigns the number of elements in the list to the scalar.
You could also have used
$temp2 = ($temp2 =~ /(\d.*)/)[0];
to pick up the first match result.
#jm666's answer works because it assigns the list of match results to a list of variables.

The following
my ($temp2) = ($buildLog =~ /(build\_version.*\d*)/);
($temp2) = ($temp2 =~ /(\d.*)/);
will print
1411450178

Related

How do I know if there is a number in my list?

My code looks like this:
#!/usr/bin/perl
$counter=0;
#list=<STDIN>;
chomp(#list);
if (#list==~ /^[+-]?\d+$/ )
{
$counter++;
}
print $counter;
So I write datas like: a b c d 1 2
And then it should print 2
beacuse of the 1,2
But it does not matter what datas i write into the list i get back 0.
So what is the problem with my if?
Always use strict; use warnings;. If your goal is to count the number of digit elements in your list, you can use grep to filter the list elements, then apply scalar to get its length (or use $length directly instead of storing the filtered list in #matches):
#!/usr/bin/perl
use strict;
use warnings;
my #list = <STDIN>;
chomp(#list);
my #matches = grep /^[+-]?\d+$/, #list;
print scalar #matches . "\n";
Sample run:
$ ./count.pl
-62
a
b
4
c
+91023
d
3
Honestly, it looks like you just guessed at this syntax. That's not really a great way to write a program :-)
Your main problems are on this line:
if (#list==~ /^[+-]?\d+$/ )
There are two pretty big problems here. Firstly, the #list. The match operator (=~) works on a single string at a time. So you need to use it on a scalar value. And if you give it an array (as you have done here) then Perl will (silently) evaluate the array as a scalar - which means that rather than getting the contents of the array, you'll get the number of elements in the array - which will be an integer so your regex will always match.
But, you say, it doesn't match. Yes, I realise that. And that's down to your second error - you have the match operator wrong. The operator is =~ and you're using ==~ (see the extra =). You would hope that an error like that cause a syntax error, but you've accidentally used a version that is syntactically valid but just doesn't do what you want. Perl interprets your code as:
if (#list = =~ /^[+-]?\d+$/ )
Note the spaces I've added. The one between the two = characters is important. This is saying "perform the match operation and assign the results to #list". But what is the match operator matching against? Well, it hasn't been given an explicit variable to match against, and in that case it matches against the default variable, $_. You haven't put anything into $_ so the match fails.
At this point, I should point out that if you had use warnings in your code, then you would be getting all sorts of useful warnings about what you're doing wrong. All Perl programmers (including the most experienced ones) should always have use strict and use warnings in their code.
There's another point of confusion here. It's the way you read your input.
#list = <STDIN>;
You haven't made it clear, but I suspect that you type in your list all in one line. In that case, you don't want to store your input in an array; you should store it in a scalar.
chomp($list = <STDIN>);
You can then convert it to a list (to be stored in an array) using split().
#list = split /\s+/, $list;
You can then get the count of numbers in your array using grep.
my $count = grep { /^[-+]\d+$/ } #list;
When evaluated as a scalar, grep returns the number of times the block of code was true.
Putting those together (and adding strict and warnings) we get the following:
#!/usr/bin/perl
use strict;
use warnings;
chomp(my $list = <STDIN>);
my $count = grep { /^[-+]\d+$/ } split /\s+/, $list;
Which, to my mind at least, looks simpler than your version.

Perl regex substitution using external parameters

Consider the following example:
my $text = "some_strange_thing";
$text =~ s/some_(\w+)_thing/no_$1_stuff/;
print "Result: $text\n";
It prints
"Result: no_strange_stuff"
So far so good.
Now, I need to get both the match and replacement patterns from external sources (user input, config file, etc).
Naive solution appears to be like this:
my $match = "some_(\\w+)_thing";
my $repl = "no_\$1_stuff";
my $text = "some_strange_thing";
$text =~ s/$match/$repl/;
print "Result: $text\n";
However:
"Result: no_$1_stuff".
What's wrong? How can I get the same outcome with externally supplied patterns?
Solution 1: String::Substitution
Use String::Substitution package:
use String::Substitution qw(gsub_modify);
my $find = 'some_(\w+)_thing';
my $repl = 'no_$1_stuff';
my $text = "some_strange_thing";
gsub_modify($text, $find, $repl);
print $text,"\n";
The replacement string only interpolates (term used loosely) numbered match vars (like $1 or ${12}). See "interpolate_match_vars" for more information.
This module does not save or interpolate $& to avoid the "considerable performance penalty" (see perlvar).
Solution 2: Data::Munge
This is a solution mentioned by Grinnz in the comments below.
The Data::Munge can be used the following way:
use Data::Munge;
my $find = qr/some_(\w+)_thing/;
my $repl = 'no_$1_stuff';
my $text = 'some_strange_thing';
my $flags = 'g';
print replace($text, $find, $repl, $flags);
# => no_strange_stuff
Solution 3: A quick'n'dirty way (if replacement won't contain double quotes and security is not considered)
DISCLAIMER: I provide this solution as this approach can be found online, but its caveats are not explained. Do not use it in production.
With this approach, you can't have a replacement string that includes a " double quotation mark and, since this is equivalent to handing whoever is writing the configuration file direct code access, it should not be exposed to Web users (as mentioned by Daniel Martin).
You can use the following code:
#!/usr/bin/perl
my $match = qr"some_(\w+)_thing";
my $repl = '"no_$1_stuff"';
my $text = "some_strange_thing";
$text =~ s/$match/$repl/ee;
print "Result: $text\n";
See IDEONE demo
Result:
Result: no_strange_stuff
You have to
Declare the replacement in '"..."' so as $1 could be later evaluated
Use /ee to force the double evaluation of the variables in the replacement.
A modifier available specifically to search and replace is the s///e evaluation modifier. s///e treats the replacement text as Perl code, rather than a double-quoted string. The value that the code returns is substituted for the matched substring. s///e is useful if you need to do a bit of computation in the process of replacing text.
You can use qr to instantiate pattern for the regex (qr"some_(\w+)_thing").
Essentially the same approach as the accepted solution, but I kept the initial lines the same as the problem statement, since I thought that might make it easier to fit into more situations:
my $match = "some_(\\w+)_thing";
my $repl = "no_\$1_stuff";
my $qrmatch = qr($match);
my $code = $repl;
$code =~ s/([^"\\]*)(["\\])/$1\\$2/g;
$code = qq["$code"];
if (!defined($code)) {
die "Couldn't find appropriate quote marks";
}
my $text = "some_strange_thing";
$text =~ s/$qrmatch/$code/ee;
print "Result: $text\n";
Note that this works no matter what is in $repl, whereas the naive solution has issues if $repl contains a double quote character itself, or ends in a backslash.
Also, assuming that you're going to run the three lines at the end (or something like it) in a loop, do make sure that you don't skip the qr line. It will make a huge performance difference if you skip the qr and just use s/$match/$code/ee.
Also, even though it's not as trivial to get arbitrary code execution with this solution as it is with the accepted one, it wouldn't surprise me if it's still possible. In general, I'd avoid solutions based on s///ee if the $match or $repl come from untrusted users. (e.g., don't build a web service out of this)
Doing this kind of replacement securely when $match and $repl are supplied by untrusted users should be asked as a different question if your use case includes that.

substitution within fields perl

I need to parse through many delimited files and I had a question. Within a while loop, how do I make a substitution within a field? Let me give some example code and data.
Data Example:
Word,Name,Number,You/Me,Data
Hello,Josh,123,Me,Data
Hello,Joe,222,Me,Data
GOAL:
In this example what I would like to do is do a substitution on $[2] and $[3].
In other words, the field $[2] would be the number field. $[3] would be the You/Me field.
What I have in my code is this:
my #F = split;
while (<>) {
if ($F[3] =~ /^You$/ print "Me";) next;
if ($F[2] =~ /^222$/ print "P";) next;
if ($F[2] =~ /^123$/ print "P";) next;
print #F;
}
I can't seem to find the correct syntax to make substitutions in specific fields and was hoping someone would have a suggestion. The goal of my results is below.
DESIRED RESULTS:
Word,Name,Number,You/Me,Data
Hello,Josh,P,Me,Data
Hello,Joe,P,Me,Data
I would just like to perform substitutions on the fields specified and leave everything else the same.
Additionally I may have a scenario where I would like to delimit certain fields by something completely different while leaving everything else the same. I would also like to use scalars.
Data Example:
Word,Name,Number,You/Me,Data
Hello,Josh,123,Me,Data
Hello,Joe,222,Me,Data
DESIRED RESULTS:
Word,Name,Number,You/Me,Data
Hello,Josh,P-Me,Data
Hello,Joe,P-Me,Data
EXAMPLE with scalar
my $numbers = qw/222|123/;
my #F = split;
while (<>) {
if ($F[3] =~ /^You$/ print "Me";) next;
if ($F[2] = /^$numbers$/ print "P";) next;
join ("-",$[2],$[3]);
print #F;
}
So in the end, I would like to know how to substitute in fields, join specific fields with a different delimiter, and implement scalars into this sort of split/join script.
This perl script does what you need.
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my $file = "file";
local #ARGV = $file;
local $^I = '.bak';
my $numbers = qr/^(?:222|123)$/;
while (<>) {
print and next if /^\s+$/ or $.==1;
my #flds = split /,/;
$flds[2] = "P" if $flds[2] =~ $numbers;
$flds[3] = "Me" if $flds[3] eq "You";
print join ",", #flds[0,1], join ("-", #flds[2,3]), #flds[4..$#flds];
}
#unlink "$file$^I";
Outputs:
Word,Name,Number,You/Me,Data
Hello,Josh,P-Me,Data
Hello,Joe,P-Me,Data
We use $^I variable to do in-place changes while creating a backup of the original with extension of .bak. We split the line on , and populate an array called #flds.
Then we do two checks and if they are successful we make the changes to the fields. Once the checks are done we print the array which has the modified fields for every successful test.
unlink is commented out. You can uncomment it if you don't want back up file.
You can remove print and next unless /\S/; line if you do not have any blank lines in your file or if you do not wish to retain them in the output.
Would you like to change "You" to "Me"? Your desired results show you won't like to change, but you code is otherwise.
I think you can use $F[2] =~ s/^123$/P/. See perldoc perlre
And maybe you would like to use $^I variable if you want in-place edition.
See perldoc perlval

Can anyone explain me this regex meaning

I would like to understand this expression meaning.
$req_msg =~ s/ \${$toReplace}/$replacements->{$toReplace}/g;
Prerequisite for this to work are two variables:
$toReplace - contains an arbitrary value
$replacements - a HASH ref containing, erm, replacements
Given $toReplace contains "foo", the contents of $req_msq are searched for ${foo} (with a leading single space) with every occurence of this being replaced with $replacements->{foo}.
$req_msg =~ s/ \${$toReplace}/$replacements->{$toReplace}/g;
s is used for substitution. $content=~ s/old_value/new_value/modifier; (modifier can be i, g, x, along or combination)
Ex:
$content = "Hi I am a coder and I like coding very much!";
$content =~ s/i/eye/i;
now $content will contain "Heye eye am a coders and eye like coding very much"
In the same way ${$toReplace} which simply means a scalar reference is the old value which needs to be replace and $replacements->{$toReplace} means $replacements is a hash reference whose key is $toReplace .
It is smiliar to $hash_value = hash_ref->{key};
whereever it finds the value returned by scalar reference , gets replace by hash reference's key with the corresponding value found in $req_msg
But I guess you asked this question because you got blank replacement. That may be due to scalar reference problem.
This code snippet may help in removing your doubt.
#!/usr/bin/perl
use strict;
use warnings;
my $value = "Jassi";
my $scalar_ref = \$value;
print "scalar_ref = $scalar_ref \n and value = $value and ${$scalar_ref}\n";
my %hash = ("Jassi", "aliencoders");
my $hash_ref = \%hash;
my $reg_msg = "Hi this is Jassi";
print "reg_msg = $reg_msg \n";
$reg_msg =~ s/${$scalar_ref}/$hash_ref->{${$scalar_ref}}/;
print "reg_msg after s = $reg_msg\n";
See the second last line!
It replaces every occurance of the text ${blabla} with whatever is stored in the hash reference $replacements with the key blabla, e.g.:
$replacements = { 'blabla' => 'blubb' };
will make every ${blabla} being replaced by blubb in $req_msg.

Perl regex match ignoring order

I am trying to compare a variable and another variable in regex form. If the contents of the variables are exactly the same, match happens fine but if the order of the values are different, I want the match to happen.
Example:
#!/usr/bin/perl
my $output = "test0 test1";
my $expected_output = "test1 test0";
my $expected_regex = qr/^$expected_output\s*$/;
print "Expected_regex :: $expected_regex\n";
if ($output =~ $expected_regex) {
print "pass\n";
}
In my example, what can I do to make $output = $expected_regex even though they contain the same values but not in the same order?
Assuming your inputs are really "that simple", i.e. words separated by spaces, you can do something like this:
#! /usr/bin/perl -w
use strict;
use warnings;
my $output = "test0 test1";
my $expected_output = "test1 test0";
# Store the sorted pieces of each string in a list
my #o = sort(split(/ /, $output));
my #e = sort(split(/ /, $expected_output));
# Compare both arrays for equality of each member
print "pass\n" if (#o ~~ #e);
See smart matching in detail for the funny ~~ operator.
If your inputs are not that simple, the / / in the splits could possibly be elaborated, or a similar technique could be derived.
If not, just keep the first two lines of this and put it in all your non-trivial scripts. That's sure to help you.