Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: Unexpected match variables when using ?: operator

Trying to untaint some variables in Perl, and the following code works great:

if ($year =~ /^(\d{4})$/) {
        $year = $1;
} else {
        &invalid("year");
}

In the above instance, $1 contains $year if valid. However, when using the ?: operator, $1 contains "1" when valid:

($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");

Anyone see where I might be at fault? I'm confused why this is happening. It's only happening on this machine. Or rather, I have successfully used the ? operator for returning proper match variables for years. I haven't tried this piece of code on any other machine yet.

This is Perl, v5.8.8 built for x86_64-linux-thread-multi
like image 843
Jacob Avatar asked Dec 10 '22 06:12

Jacob


2 Answers

Be careful about precedence here.

Contrast http://codepad.org/vEPnhWfH which works as expected, with http://codepad.org/nXVU5CA7 which does not. Of course, I tweaked the code a little bit to avoid calling invalid, but I suspect this may be at the root of the problem.

In both cases, though $1 contains "2011", so perhaps you should show additional code, as requested in the first comment by mu.

UPDATE

I changed the codepad examples to use the call to &invalid and the error does not show up.

sub invalid {
    print("INVALID\n");
}

sub check_with_if {
    print("Trying with if-statement\n");
    my $year = shift;
    if ($year =~ /^(\d{4})$/) {
        $year = $1;
    } else {
        &invalid($year);
    }
    print("\$1 is $1 and \$year is $year"."\n");
}

sub check_with_conditional {
    print("Trying with conditional expression\n");
    my $year = shift;
    ($year =~ /^(\d{4})$/) ? $year = $1 : &invalid($year);
    print("\$1 is $1 and \$year is $year"."\n");
}

check_with_if("2011");
check_with_conditional("2011");

Output is

Trying with if-statement 
$1 is 2011 and $year is 2011
Trying with conditional expression
$1 is 2011 and $year is 2011

http://codepad.org/z22GMEcn

EDIT 2 Also works on 5.12.3 on the Mac.

I agree with Jonathan that you may have found a bug in 5.8.8 that has been fixed since. If you are incredibly curious you can work your way through the Perl changes, such as:

  • http://search.cpan.org/~jesse/perl-5.14.1/pod/perl5100delta.pod (5.8.8 -> 5.10.0)
  • http://search.cpan.org/~jesse/perl-5.11.1/pod/perl5110delta.pod (5.10.0 -> 5.11.0)
  • http://search.cpan.org/~jesse/perl-5.12.0/pod/perl5120delta.pod (5.10.0 -> 5.12.0)
like image 78
Ray Toal Avatar answered Jan 06 '23 01:01

Ray Toal


Damien Conway's Perl Best Practices explains some of the pitfalls of using the match variables $1 and so on, one of them is that:

  • if the regex doesn't match anything, $1 does not become undef, but it preserves its previous value (which of course, in most cases is undef)

My intuition tells me that you have a problem with contexts - your expression might be evaluated in scalar context, thus returning the number of matches when it is valid.

So, I would try rewriting the code like:

($year) = ( $year =~ /^(\d{4})$/) or &invalid("year");
like image 40
Tudor Constantin Avatar answered Jan 06 '23 03:01

Tudor Constantin