What's wrong with my perl script? - perl

Below is my code, basically if the answer is "Y" then the script runs a message if it's something else then it closes.
#! usr/bin/perl
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
if($answer == "Y") {
print "COOOL\n";
} else {
system "exit"
}

Perl will tell you exactly what the problem is, if you ask it. Just add "use warnings" to your code.
#!/usr/bin/perl
use warnings;
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
if($answer == "Y") {
print "COOOL\n";
} else {
system "exit"
}
Then running it, gives:
$ ./y
Do you wish to run Program? [Y/N]:Y
Argument "Y" isn't numeric in numeric eq (==) at ./y line 6, <> line 1.
Argument "Y\n" isn't numeric in numeric eq (==) at ./y line 6, <> line 1.
COOOL
It's even better if you add "use diagnostics" as well.
$ ./y
Do you wish to run Program? [Y/N]:Y
Argument "Y" isn't numeric in numeric eq (==) at ./y line 7, <> line 1 (#1)
(W numeric) The indicated string was fed as an argument to an operator
that expected a numeric value instead. If you're fortunate the message
will identify which operator was so unfortunate.
Argument "Y\n" isn't numeric in numeric eq (==) at ./y line 7, <> line 1 (#1)
COOOL
Programming in Perl is far easier if you let Perl help you find your errors.

Remove newline. == is for numerical equality, for string you need eq.
chomp($answer);
if($answer eq "Y") {

When you wonder what's going on, start tracing your input. Ensure it is what you think it is:
#!/usr/bin/perl
use strict;
use warnings;
print "Do you wish to run Program? [Y/N]:";
$answer = <>;
print "Answer is [$answer]\n";
Since you put the braces around the variable, you'll notice any extra whitespace. You should see extra stuff in $answer:
Answer is [Y
]
That's your clue that you need to do something to handle that.
And, strict and warnings help you find problems before they are problems.

Probably it will be better to use Term::Prompt or IO::Prompt. Don't reinvent the wheel :)
use IO::Prompt;
prompt -yn, 'Do you wish to run Program?' or exit;

You have newline character, chomp $answer
and $answer eq "Y"

You are using a numerical == to compare your strings.
You probably want to use "eq":
if($answer eq "Y") {
print "COOOL\n";
} else {
system "exit"
}
And as others have suggested you'll want to remove the newline at the end. Use chomp.

Besides the chomp/chop and eq vs ==, you also need to keep in mind the case of the answer. You are testing for UPPERCASE 'Y', I'm willing to bet you are entering lowercase 'y' and they are not equal. I would suggest using:
if (($answer eq 'y') || ($answer eq 'Y')) {
or use uc.

Related

Why do I get a syntax error in my compound if statement?

Why do I get a syntax error in the following script?
print "Enter Sequence:";
$a = <STDIN>;
if ($a=="A")|| ($a== "T")|| ( $a == "C")|| ($a== "G")
{
print $a;
}
else
{
print "Error";
}
First, you have a syntax error: The condition expression of an if statement must be in parens.
The second error is found by using use strict; use warnings;, something you should always do. The error is the use of numerical comparison (==) where string comparison (eq) is called for.
The final problem is that $a will almost surely contain a string ending with a newline, so a chomp is in order.
The immediate problem is that he entire logical expression for an if must be in parentheses.
In addition
You must use eq instead of == for comparing strings
Your input string will have a trailing newline, so it will look like "C\n" and will not match a simple one-character string. You need to chomp the input before you compare it
It is generally better to read from STDIN using <> rather than <STDIN>. That way you can specify an input file on the command line, or read from the STDIN if no input was provided
You must always put use strict and use warnings at the top of your program. That will catch many simple errors that you may otherwise overlook
You shouldn't use $a as a variable name. It is a symbol reserved by Perl itself, and says nothing about the purpose of the variable
It is best to use a regular expression for simple comparisons like this. It makes your code much easier to read and will usually make the execution very much faster
Please take a look at this program, which I think does what you want.
use strict;
use warnings;
print "Enter Sequence: ";
my $input = <>;
chomp $input;
if ( $input =~ /^[ATCG]$/i ) {
print $input, "\n";
}
else {
print "Error";
}

Why does "bob" == "godzilla" in Perl?

In Perl class today, a student turned in an assignment which vexes me. We are studying ARGV, but the result was not what I expected. His program (meme.pl) was:
#!/usr/bin/perl
$A = $ARGV[0];
chomp($A);
if ($A == "godzilla"){
print "$A\n";
}
else {
print "We need a monster's name\n";
}
If I type:
% ./meme.pl bob
the result is
% bob
So the variable assignment works, and but the condition ($A == "godzilla") is true no matter what is typed on the command line. I expected that since $ARGV[0] is "bob" and $A=$ARGV[0], then it should not true that $A="godzilla."
What am I missing? I have combed through this code for hours, and I know I am just overlooking some small thing.
Use eq, not ==, to test string equality:
if ($A eq "godzilla"){
More information is available at perldoc perlop.
Note: Adding use strict; and use warnings; to the top of your script would have led you in the right direction.
use strict; and use warnings; should be on...instant F in my book.
But no...evaluations of strings using "==" evaluate all strings - except those that start with a number like '123bob' (see comment below) - as numerical 0. That is why it is evaluating to true - it's "turning into" the statement 0 == 0. use warnings; would have told you something was up.
As many have said - use eq for strings.
More evidence and options can be found here: (http://perlmeme.org/howtos/syntax/comparing_values.html)
The pertinent excerpt (example program):
#!/usr/bin/perl
use strict;
use warnings;
my $string1 = 'three';
my $string2 = 'five';
if ($string1 == $string2) {
print "Equal\n";
} else {
print "Not equal\n";
}
From the above example, you would get warning messages and both strings would evaluate to zero:
Argument "five" isn't numeric in numeric eq (==) at ./test.pl line 8.
Argument "three" isn't numeric in numeric eq (==) at ./test.pl line 8.
Equal
You aren't getting those warnings...just the "Equal", thanks to the absence of use warnings; at the top of your - errr...your student's...cough... - code. ;)
When you are comparing strings, you must use "eq" instead of "==". So replace
($A == "godzilla")
by
($A eq "godzilla")
What the others said is correct about using eq to compare strings. However, the test passes, because when compared numerically with == the string 'bob' and the string 'godzilla' both evaluate to 0, so the test passes and you get bob.

Warnings on equality operators

Has something changed in Perl or has it always been this way, that examples like the second ($number eq 'a') don't throw a warning?
#!/usr/bin/env perl
use warnings;
use 5.12.0;
my $string = 'l';
if ($string == 0) {};
my $number = 1;
if ($number eq 'a') {};
# Argument "l" isn't numeric in numeric eq (==) at ./perl.pl line 6.
Perl will be try to convert a scalar to the type required by the context where it is used.
There is a valid conversion from any scalar type to a string, so this is always done silently.
Conversion to a number is also done silently if the string passes a looks_like_number test (accessible through Scalar::Util). Otherwise a warning is raised and a 'best guess' approximation is done anyway.
my $string = '9';
if ( $string == 9 ) { print "YES" };
Converts the string silently to integer 9, the test succeeds and YES is printed.
my $string = '9,8';
if ( $string == 9 ) { print "YES" };
Raises the warning Argument "9,8" isn't numeric in numeric eq (==), converts the string to integer 9, the test succeeds and YES is printed.
To my knowledge it has always been this way, at least since v5.0.
It has been that way.
In the first if, l is considered to be in numeric context. However, l cannot be converted to a number. Therefore, a warning is emitted.
In the second if, the number 1 is considered to be in string context. Therefore the number 1 is converted to the string '1' before comparison and hence no warnings are emitted.
Did you use a lowercase "L" on purpose? It's often hard to tell the difference between a lowercase "L" and one. You would have answered your own question if you had used a one instead.
>perl -wE"say '1' == 0;"
>perl -wE"say 1 eq 'a';"
>
As you can see,
If one needs a number, Perl will convert a string to a number without warning.
If one needs a string, Perl will convert a number to a string without warning.
Very consistent.
You get a warning when you try to convert a lowercase L to a number, but how is that surprising?

comparison of %2B in perl

I'm just trying to check if a string is equal to "%2B", and if it does I change it to "+".
The problem lies in comparison.
if ($lastItem == "%2B"){
$lastItem = "+";
}
When $lastItem is something completely different (like "hello"), it will still go into the statement. I've been wracking my brain and I just can't tell where I've gone wrong. Does %2B have some special meaning? I'm very new to perl.
Thanks
You need to use eq when comparing strings, or perl will try to convert the string to a number (which will be 0), and you will find such oddities as "a" == 0 to evaluate true. And when comparing two strings, you will of course effectively get if (0 == 0), which is the problem you are describing.
if ($lastItem eq "%2B") {
It is important to note that if you had used use warnings, this problem would have been easier to spot, as this one-liner will demonstrate:
$ perl -wE 'say "yes" if ("foo" == "bar")'
Argument "bar" isn't numeric in numeric eq (==) at -e line 1.
Argument "foo" isn't numeric in numeric eq (==) at -e line 1.
yes
I think you really want the following:
use URI::Escape qw( uri_unescape );
my $unescaped_last_item = uri_unescape($escaped_last_item);
URI::Escape
Please use use strict; use warnings;!
Another example where turning on use warnings would have made it simpler to work out what was wrong.
$ perl -Mwarnings -e'$l = "x"; if ($l == "%2B") { print "match\n" }'
Argument "%2B" isn't numeric in numeric eq (==) at -e line 1.
Argument "x" isn't numeric in numeric eq (==) at -e line 1.
match

Unexpected result for IF statement: "string" <= 72 is true?

In my code below, when I enter in some non-numeric letters at the input (ie. $temp), it responds with "Too cold!" instead of "invalid". What am I missing?
#!/usr/bin/perl
print "What is the temperature outside? ";
$temp=<>;
if ($temp > 72) {
print "Too hot!\n"; }
elsif ($temp <= 72) {
print "Too cold!\n"; }
else {
print "Temperature $temp is invalid.\n"; }
This is because it will be treated as 0 if it cannot be converted into a number. You should check before if the response has only numbers, or restrict the input in any other way so that only a valid number can be entered. Something along the lines:
print "invalid" if ($temp =~ /\D/);
(prints invalid if $temp contains any non-digit character. Note that this may invalidate "+" and "-", but you get the idea).
The numerical comparison operators expect their arguments to be numbers. If you try to compare a string like 'foo' using a numerical comparison, it will be converted silently to the number 0, which is less than 72.
If you had warnings turned on, you would have been told what was going on.
friedo$ perl -Mwarnings -E 'say "foo" < 72'
Argument "foo" isn't numeric in numeric lt (<) at -e line 1.
1
This is why you should always begin your programs with
use strict;
use warnings;
Casting an invalid numerical string to a number results in 0, therefor you could use something as the below to see if the input was indeed valid or not.
print "What is the temperature outside? ";
$temp=<>;
if ($temp == 0 && $temp ne '0') {
print "Temperature $temp is invalid.\n"; }
elsif ($temp > 72) {
print "Too hot!\n"; }
elsif ($temp <= 72) {
print "Too cold!\n"; }
Explanation: If the input string was casted into 0 (zero) though the string itself isn't equal to '0' (zero) the input is not numeric, hence; invalid.
You could also check to see if the input only consists of [0-9.] by using a regular expression, that would ensure that it's a valid number (also remember that numbers do not start with 0 (zero) and then have digits that follow, unless you are writing in octal.
Note: Remember to trim the input string from white spaces before the above check.
For precisely this reason (and many others), you're MUCH better off if you enable "use warnings":
#!/usr/bin/perl
use strict;
use warnings;
...
Try it after removing the trailing newline, which is probably what's causing Perl to treat it as a string rather than a number:
chomp( my $test = <> );