Understanding Perl variables - perl

I just joined a project and I'm supposed to understand the previous person's scripts.
I am new to Perl. Can someone please tell me what this statement would mean?
my $name = $1;
How can a variable be assigned a value $something? I tried to print it but it gives me an error message.

The $1 is a special variable used to return the first capture group in a regex.
For example you might see something like:
my $string = 'this is an example of 34 a match! 99 foo bar';
$string =~ /\d+\s(.*?)(\d+)/;
In this case the first captured group (a match!) will be stored in the variable $1, and the second (99) in $2.
print "first capture group = $1\nsecond capture group = $2\n";
You might want to reassign these:
my $match = $1;
my $number = $2;

Beside #fugu said, I want to express more. When you used $3, there will be nothing in $3, because in that example, only match 2 times. In perl, many variables are used, like $1, $2, $3, there are $_ for current variables in the loop; $_[0] , $_[1],.. for variables in the function; ARGV[0].. for argv of the script, and so on. Use these to save you manual work.

Related

Regular expression to print a string from a command outpout

I have written a function that uses regex and prints the required string from a command output.
The script works as expected. But it's does not support a dynamic output. currently, I use regex for "icmp" and "ok" and print the values. Now, type , destination and return code could change. There is a high chance that command doesn't return an output at all. How do I handle such scenarios ?
sub check_summary{
my ($self) = #_;
my $type = 0;
my $return_type = 0;
my $ipsla = $self->{'ssh_obj'}->exec('show ip sla');
foreach my $line( $ipsla) {
if ( $line =~ m/(icmp)/ ) {
$type = $1;
}
if ( $line =~ m/(OK)/ ) {
$return_type = $1;
}
}
INFO ($type,$return_type);
}
command Ouptut :
PSLAs Latest Operation Summary
Codes: * active, ^ inactive, ~ pending
ID Type Destination Stats Return Last
(ms) Code Run
-----------------------------------------------------------------------
*1 icmp 192.168.25.14 RTT=1 OK 1 second ago
Updated to some clarifications -- we need only the last line
As if often the case, you don't need a regex to parse the output as shown. You have space-separated fields and can just split the line and pick the elements you need.
We are told that the line of interest is the last line of the command output. Then we don't need the loop but can take the last element of the array with lines. It is still unclear how $ipsla contains the output -- as a multi-line string or perhaps as an arrayref. Since it is output of a command I'll treat it as a multi-line string, akin to what qx returns. Then, instead of the foreach loop
my #lines = split '\n', $ipsla; # if $ipsla is a multi-line string
# my #lines = #$ipsla; # if $ipsla is an arrayref
pop #lines while $line[-1] !~ /\S/; # remove possible empty lines at end
my ($type, $return_type) = (split ' ', $lines[-1])[1,4];
Here are some comments on the code. Let me know if more is needed.
We can see in the shown output that the fields up to what we need have no spaces. So we can split the last line on white space, by split ' ', $lines[-1], and take the 2nd and 5th element (indices 1 and 4), by ( ... )[1,4]. These are our two needed values and we assign them.
Just in case the output ends with empty lines we first remove them, by doing pop #lines as long as the last line has no non-space characters, while $lines[-1] !~ /\S/. That is the same as
while ( $lines[-1] !~ /\S/ ) { pop #lines }
Original version, edited for clarifications. It is also a valid way to do what is needed.
I assume that data starts after the line with only dashes. Set a flag once that line is reached, process the line(s) if the flag is set. Given the rest of your code, the loop
my $data_start;
foreach (#lines)
{
if (not $data_start) {
$data_start = 1 if /^\s* -+ \s*$/x; # only dashes and optional spaces
}
else {
my ($type, $return_type) = (split)[1,4];
print "type: $type, return code: $return_type\n";
}
}
This is a sketch until clarifications come. It also assumes that there are more lines than one.
I'm not sure of all possibilities of output from that command so my regular expression may need tweaking.
I assume the goal is to get the values of all columns in variables. I opted to store values in a hash using the column names as the hash keys. I printed the results for debugging / demonstration purposes.
use strict;
use warnings;
sub check_summary {
my ($self) = #_;
my %results = map { ($_,undef) } qw(Code ID Type Destination Stats Return_Code Last_Run); # Put results in hash, use column names for keys, set values to undef.
my $ipsla = $self->{ssh_obj}->exec('show ip sla');
foreach my $line (#$ipsla) {
chomp $line; # Remove newlines from last field
if($line =~ /^([*^~])([0-9]+)\s+([a-z]+)\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+([[:alnum:]=]+)\s+([A-Z]+)\s+([^\s].*)$/) {
$results{Code} = $1; # Code prefixing ID
$results{ID} = $2;
$results{Type} = $3;
$results{Destination} = $4;
$results{Stats} = $5;
$results{Return_Code} = $6;
$results{Last_Run} = $7;
}
}
# Testing
use Data::Dumper;
print Dumper(\%results);
}
# Demonstrate
check_summary();
# Commented for testing
#INFO ($type,$return_type);
Worked on the submitted test line.
EDIT:
Regular expressions allow you to specify patterns instead of the exact text you are attempting to match. This is powerful but complicated at times. You need to read the Perl Regular Expression documentation to really learn them.
Perl regular expressions also allow you to capture the matched text. This can be done multiple times in a single pattern which is how we were able to capture all the columns with one expression. The matches go into numbered variables...
$1
$2

Changes in CGI.pm function param() behavior? [duplicate]

I am a little confused as which way to test parameters. Here are two examples from source code posted below. First is this
if(!defined($DBHdl) || !defined($acct_no));
the way to test for undefined parameters?
Second, after assigning to a hashref
$ptMtrRecRef = $ptSelHdl->fetchrow_hashref;
is the best way to test for $ptMtrRecRef being defined to use
if(!$ptMtrRecRef)
or
if(!defined($ptMtrRecRef))?
###############################################################################
# Returns count of meters per account number.
# $PkNam -- package name discarded
# $DBHdl -- ICS database handle
# $acct_no -- water account number
sub mgbl_get_meter_count
{
my ($PkNam, $DBHdl, $acct_no) = #_;
die("mgbl_get_meter_count passed undef handles.\n")
if(!defined($DBHdl) || !defined($acct_no));
my $ptSelHdl;
my $ptMtrRecRef;
my $sql_statement =
"select count(*) from meter m where m.acct_no = ".$acct_no.";";
$ptSelHdl = $DBHdl->prepare($sql_statement);
die("Cannot prepare select count(*) from meter m\n")
if(!$ptSelHdl || !$ptSelHdl->execute);
$ptMtrRecRef = $ptSelHdl->fetchrow_hashref;
return $ptMtrRecRef;
}
$sth->fetchrow_hashref will either return undef or a reference to a hash. As such
if (defined($row))
and
if ($row)
are equivalent here. (undef is false, and reference is always true.) I opt for the simpler alternative.
Same idea for $dbh->prepare.
In the case of the code you posted, I would also do as ikegami said, and use the shorter form.
There are occasions when that isn't suitable, however, for example if a variable could have a legitimate value that would be treated as false if simply used in a true/false test. For example:
my $value = 0;
print "defined\n" if defined $value; # prints 'defined'
print "true\n" if $value; # does not print anything
Well , in perl script language, defined($a) is just a sub routine to test if $a is "undef",nothing else. So ,you will ask ,what is undef?
To be accurate, it is a perl subroutine ,the same as defined.But when it has no parameter, it can be considered as a perl-special scalar . For example , when you pop a value from an empty array ,it will return an undef.When you call subroutine "undef $b",then $b will become undef($b must be an left value),nothing else. Only in this case, defined($b) will return false.But if $c is an empty string like "" ,number zero ,or string "0" ,defined($c) will still return true;
But if you use a simple boolean expression instead of defined,it becomes totally different. A simple Boolean test will not distinguish among undef, zero, the empty string, and "0" .So , it absolutely depends on your pratical requirement when determining using defined() or just a boolean test.

When to use defined

I am a little confused as which way to test parameters. Here are two examples from source code posted below. First is this
if(!defined($DBHdl) || !defined($acct_no));
the way to test for undefined parameters?
Second, after assigning to a hashref
$ptMtrRecRef = $ptSelHdl->fetchrow_hashref;
is the best way to test for $ptMtrRecRef being defined to use
if(!$ptMtrRecRef)
or
if(!defined($ptMtrRecRef))?
###############################################################################
# Returns count of meters per account number.
# $PkNam -- package name discarded
# $DBHdl -- ICS database handle
# $acct_no -- water account number
sub mgbl_get_meter_count
{
my ($PkNam, $DBHdl, $acct_no) = #_;
die("mgbl_get_meter_count passed undef handles.\n")
if(!defined($DBHdl) || !defined($acct_no));
my $ptSelHdl;
my $ptMtrRecRef;
my $sql_statement =
"select count(*) from meter m where m.acct_no = ".$acct_no.";";
$ptSelHdl = $DBHdl->prepare($sql_statement);
die("Cannot prepare select count(*) from meter m\n")
if(!$ptSelHdl || !$ptSelHdl->execute);
$ptMtrRecRef = $ptSelHdl->fetchrow_hashref;
return $ptMtrRecRef;
}
$sth->fetchrow_hashref will either return undef or a reference to a hash. As such
if (defined($row))
and
if ($row)
are equivalent here. (undef is false, and reference is always true.) I opt for the simpler alternative.
Same idea for $dbh->prepare.
In the case of the code you posted, I would also do as ikegami said, and use the shorter form.
There are occasions when that isn't suitable, however, for example if a variable could have a legitimate value that would be treated as false if simply used in a true/false test. For example:
my $value = 0;
print "defined\n" if defined $value; # prints 'defined'
print "true\n" if $value; # does not print anything
Well , in perl script language, defined($a) is just a sub routine to test if $a is "undef",nothing else. So ,you will ask ,what is undef?
To be accurate, it is a perl subroutine ,the same as defined.But when it has no parameter, it can be considered as a perl-special scalar . For example , when you pop a value from an empty array ,it will return an undef.When you call subroutine "undef $b",then $b will become undef($b must be an left value),nothing else. Only in this case, defined($b) will return false.But if $c is an empty string like "" ,number zero ,or string "0" ,defined($c) will still return true;
But if you use a simple boolean expression instead of defined,it becomes totally different. A simple Boolean test will not distinguish among undef, zero, the empty string, and "0" .So , it absolutely depends on your pratical requirement when determining using defined() or just a boolean test.

Backreferences undefined after finding pattern in perl v5.14.2

I found it strange that backreferences ($1,$2,$3) were not working in my original code, so I ran this example from the web:
#!/usr/bin/perl
# matchtest2.plx
use warnings;
use strict;
$_ = '1: A silly sentence (495,a) *BUT* one which will be useful. silly (3)';
my $pattern = "silly";
if (/$pattern/) {
print "The text matches the pattern '$pattern'.\n";
print "\$1 is '$1'\n" if defined $1;
print "\$2 is '$2'\n" if defined $2;
print "\$3 is '$3'\n" if defined $3;
print "\$4 is '$4'\n" if defined $4;
print "\$5 is '$5'\n" if defined $5;
}
else {
print "'$pattern' was not found.\n";
}
Which only gave me:
The text matches the pattern 'silly'.
Why are the backreferences still undefined after the pattern was found? I am using Wubi (Ubuntu 12.04 64-bit) and my perl version is 5.14.2. Thank you in advance for your help.
You are not capturing any strings: No parentheses in your pattern. If you had done:
my $pattern = "(silly)";
You would have gotten something in $1.
In case you do not know, $1 is the text captured in the first parentheses, $2 the second parentheses, and so on.
This is expected behaviour! It is obvious that you pattern will match, so it is no suprise that the corresponding if-block is executed.
The term “backreferences” for $1, $2, ... may be slightly suboptimal, let's call them “capture groups”.
In a regex, you can enclose parts of the pattern with parens to be remembered later:
/(silly)/
This pattern has one group. The contents of this group will be stored in $1 if it matches.
All capture group variables for groups that don't exists in the pattern or were not populated are set to undef on an otherwise successfull match, so for above pattern $2, $3, ... would all be undef.

MySQL Query with variable in Perl

My Perl is fairly rusty so please forgive. Trying to write a query using a variable. Have tried reformatting, just can't seem to get it correctly. Here is my code, not sure what I'm doing wrong.
my $d_var = "$3\n";
my $query="SELECT id FROM `accounts` WHERE (`accounts`.`named` = ?) LIMIT 1";
my $st_h = $db_h->prepare($query);
$st_h->bind_param(1, '$d_var');
$st_h->execute;
my $row = $st_h->fetchrow_array();
Please double check:
$3 contains something reasonable
concatenating $3's value and "\n" (by interpolation) is correct ("\n" in field?)
as ' doesn't interpolate => my $st_ht->bind_param(1, $d_var);
(I don't understand the DBI Docs as Chris Ledet does.)
On 2nd thought:
This code snippet:
my $v = "nix nix 1001";
print "$v\n";
print '$v\n', "\n";
if ($v =~ m/(nix) (nix) (\d+)/) {
print 'found: ', $3, "\n";
$sth = $dbh->prepare('SELECT * FROM sample01.csv WHERE GRUPPE=?');
$sth->bind_param(1, $3);
$sth->execute;
while(my #row = $sth->fetchrow_array()) {
print '|', join( '|', #row ), "|\n";
}
} else {
print "no match\n";
}
and the output:
DBI: 1.616 DBD::CSV: 0.33
|00000089-6d83-486d-9ddf-30bbbf722583|2011-09-17 16:25:09|1001|
|000004c9-92c6-4764-b320-b1403276321e|2011-11-09 13:52:30|2000|
nix nix 1001
$v\n
found: 1001
|00000089-6d83-486d-9ddf-30bbbf722583|2011-09-17 16:25:09|1001|
should illustrate:
' does not interpolate, your '$d_var' will pass this variable name literally to DBI
a valid match needs no "\n" to 'work'
the param sequence for bind_param is number, value
Not sure why you're even using bind_param. In my opinion, it's far simpler to just pass extra values into execute.
my $d_var = "$3\n";
my $query = 'SELECT id FROM accounts` WHERE (`accounts`.`named` = ?) LIMIT 1';
my $st_h = $db_h->prepare($query);
$st_h->execute($d_var);
my $row = $st_h->fetchrow_array();
Have you considered switching to DBIx::Class?
What does this mean?
my $st_ht->bind_param(1, '$d_var');
There is no variable being introduced, so why the my?
I have $3 printing out prior to execution and there is data contained within the string.
I have d_var="$3\n" as the variable $3 is being generated by a Regex string and doesn't seem to work without \n.
Tried what Chris suggested above, however did not work.
Aside from what Dan said (removing the single quote), you binded your param to a possibly none Statement handle object $st_ht->bind_param(...) it should be $st_h->bind_param(...).