CheeseConQueso
CheeseConQueso

Reputation: 6041

Perl - Why does shift lose its value after being used?

This code works - It takes an array of full txt file paths and strips them so that when $exam_nums[$x] is called, it returns the file name

for (0..$#exam_nums)
{
 $exam_nums[$_] =~ s/\.txt$//; #remove extension
 $exam_nums[$_] =~ s/$dir//g; #remove path
}

When I try to do this for a single variable, it doesn't work. I'm calling a subroutine and sending it a present, but the variable is empty at the end. (It is getting into the if statement block, because the other lines in there run fine.) Here's the code:

Call to the sub:

notify($_);

The $_ is from a foreach(@files) loop that works

The sub:

sub notify
{
 if(shift)
 { 
  $ex_num = shift;
  $ex_num =~ s/\.txt$//; #remove extension
  $ex_num =~ s/$dir//g; #remove path
        print $ex_num;
        print "\nanything";
 }
}

I tried taking out the $ in the "remove extension" portion of the regex, but that didn't help.

Upvotes: 1

Views: 519

Answers (5)

Peter Stuifzand
Peter Stuifzand

Reputation: 5104

Uses a core module, local variables and Perl 5.10.

use 5.010;
use File::Basename;

sub notify {
    my $ex_num = shift;
    my $name = basename($ex_num, '.txt');
    say $name;
}

Upvotes: 0

Jim Garrison
Jim Garrison

Reputation: 86764

You're shifting TWICE. The first shift in the if statement removes the value, the second shift gets nothing. shift has a side-effect of actually modifying @_. in addition to returning the first element, it removes the first element permanently from @_.

EDIT: from man perlfunc

   shift ARRAY
   shift   Shifts the first value of the array off and returns it,
           shortening the array by 1 and moving everything down.  If there
           are no elements in the array, returns the undefined value.  If
           ARRAY is omitted, shifts the @_ array within the lexical scope
           of subroutines and formats, ...

Upvotes: 13

Ether
Ether

Reputation: 53966

You are attempting to extract your ex_num argument from @_ (the argument list) twice: shift (which alters @_) is not the same as $_[0] (which just looks at the first element of @_ but does not alter it). See perldoc -f shift.

Also, your function is closing over $dir, which may or may not be your intent. (See perldoc perlfaq7 for more information about closures.) I've taken that out and added it as an additional function parameter:

sub notify
{
    my ($ex_num, $dir) = @_;
    return unless $ex_num;

    $ex_num =~ s/\.txt$//; # remove extension
    $ex_num =~ s/$dir//g;  # remove path
    print $ex_num . "\n";
}

Upvotes: 6

CheeseConQueso
CheeseConQueso

Reputation: 6041

As per Jim Garrison's info, I pulled a switch to fix the problem:

sub notify
{
    $ex_num = shift;
    if($ex_num)
    {   
        $ex_num =~ s/\.txt$//; #remove extension
        $ex_num =~ s/$dir//g; #remove path
    }
}

Upvotes: 1

Nifle
Nifle

Reputation: 11923

I'd use File::Basename instead of rolling my own. It allows you to parse file paths into their directory, filename and suffix.

Upvotes: 5

Related Questions