http://www.perlmonks.org?node_id=11108595

flieckster has asked for the wisdom of the Perl Monks concerning the following question:

Hello, i'm trying to remove the suffix from a list of filenames. what is the best way to apply the basename module to a list of files.?
#!/usr/bin/env perl use Net::FTP; use File::Copy; use Mail::Sender; use Net::SMTP; my $TIFFfolder = "/Volumes/photorepos/Partners/LTBrands/post/DONE_PSDs +/"; chdir( $TIFFfolder ) or warn "Cant chdir to $TIFFfolder $!"; my(@TIFFfolder_list) = glob "*.psd"; print "$date $time Removing any Thumbs.db files from $TIFFfolder\n"; if ( -e "Thumbs.db") { unlink ("Thumbs.db") or warn "thumbs.db is being a pain in the ass: $! +\n"; } my $TIFFapproval = @TIFFfolder_list; foreach $file (@TIFFfolder_list){ my $file = basename (@TIFFfolder_list, ".psd"); print "$file\n"; } $mail->send(-to=>"$to", -from=>"$from", -subject=>"$subject", -body=>" +$file", -contenttype=>"text/html"); $mail->bye;
when i run this the print function will print the same filename for as many times as there are files it globbed, and doesn't make it into my email. LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT LT-AG4437_AH66-FLAT

Replies are listed 'Best First'.
Re: using basename on a list of filesnames
by haukex (Archbishop) on Nov 13, 2019 at 04:58 UTC

    One of the many reasons you should always Use strict and warnings. Look closely at the variables in your loop:

    foreach $file (@TIFFfolder_list){ my $file = basename (@TIFFfolder_list, ".psd"); print "$file\n"; }

    You've got two $file variables there: the package variable you're using as the loop variable, and the new lexical one you're declaring with my. But you're not passing either of those to the basename function...

    BTW, the File::Basename doc says that fileparse is a bit safer - and where are you loading that module in the first place?

    You're printing the filenames to standard output, but then you're using -body=>"$file", but the variable should be empty at that point. Perhaps you want to set up a new variable into which you concatenate your output via .= instead (or use the map/join solutions others have alluded to).

    Also, note that the Mail::Sender docs say the module is deprecated, use Email::Sender instead.

Re: using basename on a list of filesnames
by roboticus (Chancellor) on Nov 13, 2019 at 03:46 UTC

    flieckster:

    If you have a list of values and want to call a function on each of the values to make a new list, then you want to use map. I don't know where you're getting basename, so I just made a trivial example:

    use strict; use warnings; sub rm_first_3_chars { my $name = shift; return substr($name, 3); } my @list_of_names = ("Fred", "Billiam", "Wozniapple"); @list_of_names = map { rm_first_3_chars($_) } @list_of_names; print join(", ", @list_of_names), "\n";

    When run, it gives:

    $ perl flp.pl d, liam, niapple

    Essentially, you put the code you want to execute for each list item in the map block. In that block, $_ refers to each list item, and whatever the code returns is the new value that will be in the output list.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: using basename on a list of filesnames
by 1nickt (Canon) on Nov 13, 2019 at 00:07 UTC

    You are in a loop of filenames. Try

    my $basename = basename ($file, ".psd"); print "$basename\n";


    The way forward always starts with a minimal test.
      that didn't seem to work
      Use of uninitialized value $_[0] in substitution (s///) at /System/Lib +rary/Perl/5.18/File/Basename.pm line 341, <GEN0> line 10. fileparse(): need a valid pathname
      i also tried.
      my $basename = basename (@TIFFfolder_list, ".psd"); print "$basename\n";
      that only gave one filename output from the array.

        Did you add a debug print statement inside the loop to see what you are passing to basename() in $file? It really works:

        $ perl -MFile::Basename -wE 'say basename("foo.psd",".psd")' foo
        ... but passing an undefined value does not:
        $ perl -MFile::Basename -wE 'say basename(undef,".psd")' Use of uninitialized value $_[0] in substitution (s///) at /Users/1nic +kt/perl5/perlbrew/perls/perl-5.30.0/lib/5.30.0/File/Basename.pm line +341. fileparse(): need a valid pathname at -e line 1.


        The way forward always starts with a minimal test.
Re: using basename on a list of filesnames
by jcb (Parson) on Nov 13, 2019 at 04:27 UTC

    This is an XY Problem. You are using basename correctly. Try changing print "$file\n"; to push @Files, $file; and adding my $file = join(' ', @Files); just before line 24. Meditate upon those changes and you will gain understanding.

      How is it correct usage to pass a list of filenames?! Only the first is handled as a filename; the rest are possible suffix matches.

      From the doc:

      $basename = basename($fullname,@suffixlist);

      $ perl -MFile::Basename -wE 'say basename("foo.psd","bar.psd",".psd")' foo


      The way forward always starts with a minimal test.

        You are right. I had misread the code because passing the entire list a loop is iterating over instead of the current item is not a mistake I have ever made.

        The change I suggested is still needed, since our questioner is currently iterating over a list, but only passing a single value to his output routine.