perl exact string match - perl

I have following Perl code to prompt user for yes/no answer. If the user enters anything else than yes/no, keep prompting. No other word is acceptable. I don't know why this code doesn't work. I tested with answer "noooooo" and I was expecting it to prompt again but it does not enter the while loop.
Can anyone help find my mistake here?
#files = A, B, C;
foreach $c (#files) {
if (-e $c ) {
print " $c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
while ($file_yes_no !~ m/yes{1}|no{1}/i ) {
print "$c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
}
if ($file_yes_no =~ m/yes/i ) {
if (system ("cp -f /home/old_path/ /home/new_path/ == 0) {
print "$c successfully copied;
} else {
die "Error: Copy failed, Check the message above";
}
}
else { print "No files copied\n; }

I would just use the string equality operator eq instead of a regex.
if( $file_yes_no eq 'yes' ) ...
If I wanted it case insensitive I'd first convert to lowercase with lc.
The problem with your regex is it will happily match any string containing the letters yes sequentially. If you wish, you can match the start and end of the string like this:
if ($file_yes_no =~ m/^yes$/i ) ...
But I personally prefer the first option.
Oh, I missed the first part... Hmmmm. Same deal, if you must use regex.
m/^(yes|no)$/i
Once again I'd be more inclined to avoid regex

You should use following Perl regular expression for matching only yes or no (case insensitive):
m/^(yes|no)$/i
For yes only, use:
m/^yes$/i

Because you're using a regular expression. You could write the regular expression to match the beginning or end of the string ... like this:
while( $file_yes_no !~ /^(yes|no)$/ ) {
The ^ and $ are the beginning and end of the string. Also you can omit the m.
Or you could just check the values explicitly:
while( $file_yes_no ne "yes" and $file_yes_no ne "no" ) {
Also you have a typo in your system command but I'm assuming that was just copying it here. You really shouldn't branch out to a shell for that. Look into File::Copy which gives you a copy function

Related

Check if user input string is empty/undef?

Here is the entirety of my perl script:
#!/usr/bin/perl
use v5.10;
use strict;
#use P4;
print "enter location of master_testplan.conf:";
my $master_testplan_conf = <>;
if (chomp($master_testplan_conf) eq "")
{
$master_testplan_conf = 'suites/MAP/master_testplan.conf';
}
print ":" . $master_testplan_conf . ":";
referencing this answer, I thought this would work. However it's not getting the default value inside the if statement for some reason.
What am I doing wrong?
chomp does not work that way. It directly modifies the variable passed to it and returns the number of characters chomped off. Do this instead:
chomp $master_testplan_conf;
if ($master_testplan_conf eq "") {
# etc.
}
chomp modifies its argument and does not return it, so you have to rewrite your condition into something like:
chomp($master_testplan_conf);
if ($master_testplan_conf eq "") {
From the documentation on chomp:
..It returns the total number of characters removed from all its arguments..
So you need to chomp first and then compare to the empty string. For example:
chomp($master_testplan_conf = <>);
if ($master_testplan_conf eq "") {
// set default value
}
A few things:
Chomp changes the string, and returns the number of character chomped. After that input line, chomp $master_testplan_conf is most likely to 1, so you're comparing 1 to the null string.
You can do it this way:
chomp ( $master_testplan_conf = <> );
if you want to do everything on a single line.
That will read your input and do the chomp in one step. Also, the <> operator will take files from the command line and <> will be the first line of the first file on the command line. If you don't want to do that, use <STDIN>:
chomp ( $master_testplan_conf = <STDIN> );
You may want to sanitize your user's input. I would at least remove any leading and ending blanks:
$master_testplan_conf =~ s/^\s*(.*?)\s*$/$1/; # Oh, I wish there was a "trim" command!
This way, if the user accidentally presses spacebar a few times, you don't pick up the spaces. You also may want to test for the file's existence too:
if ( not -f $master_testplan_conf ) {
die qq(File "$master_testplan_conf" not found);
}
I also recommend to use:
if ( not defined $master_testplan_conf or $master_testplan_conf eq "" ) {
for your if statement. This will test whether $master_test_conf is actually defined and not merely a null string. Right now, this doesn't matter since the user has to at least enter a \n. The $master_testplan_conf stroll will never be null.
However, it may matter if you decide to use Getopt::Long.
You're interested in the file and not the string, per se, so use Perl file tests, instead. In this case, use the file test for existence (-e):
if (-e $master_testplan_conf) {
This gets to the heart of the matter and lets you know whether the input exists in the file system, or not.
A regex can be handy to check without altering anything:
if ($master_testplan_conf =~ /^\s*$/)
{
$master_testplan_conf = 'suites/MAP/master_testplan.conf';
}
to check undef also:
if (!defined $master_testplan_conf || $master_testplan_conf =~ /^\s*$/)
{
$master_testplan_conf = 'suites/MAP/master_testplan.conf';
}

grep in perl without array

If I have one variable : I assigned entire file text to it
$var = `cat file_name`
Suppose in the file , the word 'mine' comes in 17th line (location is not available but just giving example) and I want to search a pattern 'word' after N (eg 10) lines of word 'mine' if pattern 'word' exist in those lines or not. How can i do that in the regular expression without using array'
Example:
$var = "I am good in perl\n but would like to know about the \n grep command in details";
I want to search particular pattern in specific lines (lines 2 to 3 only). How can I do it without using array.
There is a valid case for not using arrays here - when files are prohibitively large.
This is a pretty specific requirement. Rather than beat around the bush to find that Perl idiom, I'd prescribe a subroutine:
sub n_lines_apart {
my ( $file, $n, $first_pattern, $second_pattern ) = #_;
open my $fh, '<', $file or die $!;
my $lines_apart;
while (<$fh>) {
$lines_apart++ if qr/$first_pattern/ .. qr/$second_pattern/;
}
return $lines_apart && $lines_apart <= $n+1;
}
Caveat
The sub above is not designed to handle multiple matches in a single file. Let that be an exercise for the reader.
You can do this with a regular expression match like this:
my $var = `cat $filename`;
while ( $var =~ /foo/g ) {
print $1, "\n";
print "match occurred at position ", pos($var), " in the string.\n";
}
This will print out all the matches of the string 'foo' from your string, similar to grep but not using an array (or list). The /$regexp/g syntax makes the regular expression iteratively match against the string from left to right.
I'd recommend reading perlrequick for a tutorial on matching with regular expressions.
Try this:
perl -ne '$m=$. if !$m && /first-pattern/;
print if $m && ($.-$m >= 2 && $.-$m <= 3) && /second-pattern/'

Perl comparison operation between a variable and an element of an array

I am having quite a bit of trouble with a Perl script I am writing. I want to compare an element of an array to a variable I have to see if they are true. For some reason I cannot seem to get the comparison operation to work correctly. It will either evaluate at true all the time (even when outputting both strings clearly shows they are not the same), or it will always be false and never evaluate (even if they are the same). I have found an example of just this kind of comparison operation on another website, but when I use it it doesn't work. Am I missing something? Is the variable type I take from the file not a string? (Can't be an integer as far as I can tell as it is an IP address).
$ipaddress = '192.43.2.130'
if ($address[0] == ' ')
{
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
#address = <FH>;
close(FH);
print $address[0];
print $address[1];
}
for ($i = 0; $i < #address; $i++)
{
print "hello";
if ($address[$i] eq $ipaddress)
{print $address[$i];
$file = "server_$i";
print "I got here first";
goto SENDING;}
}
SENDING:
print " I am here";
I am pretty weak in Perl, so forgive me for any rookie mistakes/assumptions I may have made in my very meager bit of code. Thank you for you time.
if ($address[0] == ' ')
{
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
#address = <FH>;
close(FH);
You have several issues with this code here. First you should use strict because it would tell you that #address is being used before it's defined and you're also using numeric comparison on a string.
Secondly you aren't creating an array of the address in the file. You need to loop through the lines of the file to add each address:
my #address = ();
while( my $addr = <FH> ) {
chomp($addr); # removes the newline character
push(#address, $addr);
}
However you really don't need to push into an array at all. Just loop through the file and find the IP. Also don't use goto. That's what last is for.
while( my $addr = <FH> ) {
chomp($addr);
if( $addr eq $ipaddress ) {
$file = "server_$i";
print $addr,"\n";
print "I got here first"; # not sure what this means
last; # breaks out of the loop
}
}
When you're reading in from a file like that, you should use chomp() when doing a comparison with that line. When you do:
print $address[0];
print $address[1];
The output is on two separate lines, even though you haven't explicitly printed a newline. That's because $address[$i] contains a newline at the end. chomp removes this.
if ($address[$i] eq $ipaddress)
could read
my $currentIP = $address[$i];
chomp($currentIP);
if ($currentIP eq $ipaddress)
Once you're familiar with chomp, you could even use:
chomp(my $currentIP = $address[$i]);
if ($currentIP eq $ipaddress)
Also, please replace the goto with a last statement. That's perl's equivalent of C's break.
Also, from your comment on Jack's answer:
Here's some code you can use for finding how long it's been since a file was modified:
my $secondsSinceUpdate = time() - stat('filename.txt')->mtime;
You probably are having an issue with newlines. Try using chomp($address[$i]).
First of all, please don't use goto. Every time you use goto, the baby Jesus cries while killing a kitten.
Secondly, your code is a bit confusing in that you seem to be populating #address after starting the if($address[0] == '') statement (not to mention that that if should be if($address[0] eq '')).
If you're trying to compare each element of #address with $ipaddress for equality, you can do something like the following
Note: This code assumes that you've populated #address.
my $num_matches=0;
foreach(#address)
{
$num_matches++ if $_ eq $ipaddress;
}
if($num_matches)
{
#You've got a match! Do something.
}
else
{
#You don't have any matches. This may or may not be bad. Do something else.
}
Alternatively, you can use the grep operator to get any and all matches from #address:
my #matches=grep{$_ eq $ipaddress}#address;
if(#matches)
{
#You've got matches.
}
else
{
#Sorry, no matches.
}
Finally, if you're using a version of Perl that is 5.10 or higher, you can use the smart match operator (ie ~~):
if($ipaddress~~#address)
{
#You've got a match!
}
else
{
#Nope, no matches.
}
When you read from a file like that you include the end-of-line character (generally \n) in each element. Use chomp #address; to get rid of it.
Also, use last; to exit the loop; goto is practically never needed.
Here's a rather idiomatic rewrite of your code. I'm excluding some of your logic that you might need, but isn't clear why:
$ipaddress = '192.43.2.130'
open (FH, "serverips.txt") or die "Crossroads could not find a list of backend servers";
while (<FH>) { # loop over the file, using the default input space
chomp; # remove end-of-line
last if ($_ eq $ipaddress); # a RE could easily be used here also, but keep the exact match
}
close(FH);
$file = "server_$."; # $. is the line number - it's not necessary to keep track yourself
print "The file is $file\n";
Some people dislike using perl's implicit variables (like $_ and $.) but they're not that hard to keep track of. perldoc perlvar lists all these variables and explains their usage.
Regarding the exact match vs. "RE" (regular expression, or regexp - see perldoc perlre for lots of gory details) -- the syntax for testing a RE against the default input space ($_) is very simple. Instead of
last if ($_ eq $ipaddress);
you could use
last if (/$ipaddress/);
Although treating an ip address as a regular expression (where . has a special meaning) is probably not a good idea.

Why can't I match my string from standard input in Perl?

Why will my script not work correctly?
I follow a YouTube video and worked for the guy.
I am running Perl on Windows using ActiveState ActivePerl 5.12.2.1202
Here is my tiny tiny code block.
print "What is your name?\n";
$name = <STDIN>;
if ($name eq "Jon") {
print "We have met before!\n";
} else {
print "We have not met before.\n";
}
The code automatically jumps to the else statement and does not even check the if statement.
The statement $name = <STDIN>; reads from standard input and includes the terminating newline character "\n". Remove this character using the chomp function:
print "What is your name?\n";
$name = <STDIN>;
chomp($name);
if ($name eq "Jon") {
print "We have met before!\n";
} else {
print "We have not met before.\n";
}
The trick in programming is to know what your data are. When something's not acting like you expect, look at the data to see if they are what you expect. For instance:
print "The name is [$name]\n";
You put the braces around it so you can see any extra whitespace that might be there. In this case, you would have seen:
The name is [Jon
]
That's your clue that there is extra stuff. Since the eq has to match exactly, it fails to match.
If you're just starting with Perl, try Learning Perl. It's much better than random videos from YouTube. :)
When you read the name standard input as $name = <STDIN>;
$name will have a trailing newline. So if I enter foo , $name will actually have foo\n.
To get rid of this newline you an make use of the chomp function as:
chomp($name = <STDIN>);

perl split on empty file

I have basically the following perl I'm working with:
open I,$coupon_file or die "Error: File $coupon_file will not Open: $! \n";
while (<I>) {
$lctr++;
chomp;
my #line = split/,/;
if (!#line) {
print E "Error: $coupon_file is empty!\n\n";
$processFile = 0; last;
}
}
I'm having trouble determining what the split/,/ function is returning if an empty file is given to it. The code block if (!#line) is never being executed. If I change that to be
if (#line)
than the code block is executed. I've read information on the perl split function over at
http://perldoc.perl.org/functions/split.html and the discussion here about testing for an empty array but not sure what is going on here.
I am new to Perl so am probably missing something straightforward here.
If the file is empty, the while loop body will not run at all.
Evaluating an array in scalar context returns the number of elements in the array.
split /,/ always returns a 1+ elements list if $_ is defined.
You might try some debugging:
...
chomp;
use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper( { "line is" => $_ } );
my #line = split/,/;
print Dumper( { "split into" => \#line } );
if (!#line) {
...
Below are a few tips to make your code more idiomatic:
The special variable $. already holds the current line number, so you can likely get rid of $lctr.
Are empty lines really errors, or can you ignore them?
Pull apart the list returned from split and give the pieces names.
Let Perl do the opening with the "diamond operator":
The null filehandle <> is special: it can be used to emulate the behavior of sed and awk. Input from <> comes either from standard input, or from each file listed on the command line. Here's how it works: the first time <> is evaluated, the #ARGV array is checked, and if it is empty, $ARGV[0] is set to "-", which when opened gives you standard input. The #ARGV array is then processed as a list of filenames. The loop
while (<>) {
... # code for each line
}
is equivalent to the following Perl-like pseudo code:
unshift(#ARGV, '-') unless #ARGV;
while ($ARGV = shift) {
open(ARGV, $ARGV);
while (<ARGV>) {
... # code for each line
}
}
except that it isn't so cumbersome to say, and will actually work.
Say your input is in a file named input and contains
Campbell's soup,0.50
Mac & Cheese,0.25
Then with
#! /usr/bin/perl
use warnings;
use strict;
die "Usage: $0 coupon-file\n" unless #ARGV == 1;
while (<>) {
chomp;
my($product,$discount) = split /,/;
next unless defined $product && defined $discount;
print "$product => $discount\n";
}
that we run as below on Unix:
$ ./coupons input
Campbell's soup => 0.50
Mac & Cheese => 0.25
Empty file or empty line? Regardless, try this test instead of !#line.
if (scalar(#line) == 0) {
...
}
The scalar method returns the array's length in perl.
Some clarification:
if (#line) {
}
Is the same as:
if (scalar(#line)) {
}
In a scalar context, arrays (#line) return the length of the array. So scalar(#line) forces #line to evaluate in a scalar context and returns the length of the array.
I'm not sure whether you're trying to detect if the line is empty (which your code is trying to) or whether the whole file is empty (which is what the error says).
If the line, please fix your error text and the logic should be like the other posters said (or you can put if ($line =~ /^\s*$/) as your if).
If the file, you simply need to test if (!$lctr) {} after the end of your loop - as noted in another answer, the loop will not be entered if there's no lines in the file.