perlscopelocalspecial-variables

In Perl the IRS ($/) is changed but it seems to scroll back the reading


I did a lot of searching on the scoping when using local.

I understand from here: http://www.perlmonks.org/?node_id=94007 that it changes temporarily the value of a variable in between when the "local" line is executed and when the next end of block is reached. However, it seems that it also "scrolls back" the perl reading cursor? It seems to keep the previous $_.

These are the requirements for my perl script:

On the std in, read a text file. if the current line matches ==, print the string on the line as html fragment with corresponding formating (different formating of each of ==, ===, ====) if it contains !, print the part before the ! as a property name and the part after the ! until the next ! as value(may span over multiple lines).

Here is a sample file:

$ cat dummy_spec.txt
== title1 (level 1) ==
=== title2 (level 2) ===
title2p2 !
2p2_1
!
title2p2 ! Line 1
!
title2p1 ! 
echo "$tam ta" 
!
title2p3 !
2p3
!
title2p2 ! Li
ne split
!
==== title3 (level 3) ====
title3p1 !
one
two
three
!

And here is my code:

#!/usr/bin/env perl 
my $propName='';
my $propVal='';
while (<>){
    #print "new loop number $., contents $_ \n";
    $propName='';
    $propVal='';

    my $str=$_;
    chomp $str;

    if ($str=~/==\s+.+\s+==/)#If line is a Category, print category
    {
        $str=~s/=//g;
        print "\t\t\t<tr class=\"category\"><td colspan=\"2\">$str</td></tr>\n";
    } elsif($str=~/===\s+.+\s+===/)#If line is a list, print list title
    {
        $str=~s/=//g;
        print "\t\t\t<tr class=\"list\"><td colspan=\"2\">$str</td></tr>\n";
    } elsif($str=~/====\s+.+\s+====/)#If line is an item, print item title
    {
        $str=~s/=//g;
        print "\t\t\t<tr class=\"item\"><td>$str</td></tr>\n";
    } elsif($str=~/!/)#If line is a property, print name and value
    {
        ($propName,$propVal)=split '!', $_;
        chop $propVal;
        print "This is a property ($.) with name : $propName and first line : $propVal \n";
        local $/ = "!\n";
        if(<>){#custom $/ should still be in scope here.            
            chomp;
            $propVal="$propVal -~- $_";
            print "This is a property ($.) with name : $propName and value : $propVal \n";
        }
    }

}

The output is not what it should be:

$ cat dummy_spec.txt |./multi 
            <tr class="category"><td colspan="2"> title1 (level 1) </td></tr>
            <tr class="category"><td colspan="2"> title2 (level 2) </td></tr>
This is a property (3) with name : title2p2  and first line :  
This is a property (4) with name : title2p2  and value :  -~- title2p2  
This is a property (5) with name : title2p2  and first line :  Line 1 
This is a property (6) with name : title2p2  and value :  Line 1 -~- title2p2 ! Line 1

This is a property (7) with name : title2p1  and first line :   
This is a property (8) with name : title2p1  and value :   -~- title2p1 ! 

This is a property (9) with name : title2p3  and first line :  
This is a property (10) with name : title2p3  and value :  -~- title2p3  
This is a property (11) with name : title2p2  and first line :  Li 
This is a property (12) with name : title2p2  and value :  Li -~- title2p2 ! Li

            <tr class="category"><td colspan="2"> title3 (level 3) </td></tr>
This is a property (14) with name : title3p1  and first line :  
This is a property (15) with name : title3p1  and value :  -~- title3p1  

it seems that it also "scrolls back" the perl reading cursor? It prints the previous $_.

I also apologize because my perl is very basic.


Solution

  • You're not assigning to $_ in the scope with your changed value of $/, only reading from the input and using the read line as the condition of an if statement.

    The magical assignment to the $_ variable when reading from a file only happens if the <> operator is the only contents of the condition of a while loop, it doesn't apply to the condition of an if statement. See the fourth paragraph in the documentation for I/O Operators.

    You likely want to change that if statement to:

    if(defined ($_ = <>))