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

Hi,

I need to retrieve the filename from the fullpath name please. For example, the full pathname is:

/home/username/public_html/somepath/2004-04-14-0002136

and I would like to put the value:

'2004-04-14-0002136' into a variable. No doubt if there is a Perl command to find the position of the last "/", then it would be a simple matter of getting the filename from _that_ position, after finding the length of the full string (full pathname), no doubt.

Well, that's how it's done in Clipper. :D

Peter

  • Comment on Getting the filename from full path name ?

Replies are listed 'Best First'.
Re: Getting the filename from full path name ?
by davido (Cardinal) on Apr 15, 2004 at 03:12 UTC
    See File::Basename. It's a core module, if I'm not mistaken, so you don't even have to install it before using it.

    use File::Basename; my $path = '/home/username/public_html/somepath/2004-04-14-0002136'; my $filename = basename($path); print $filename, "\n";


    Dave

      Actually File::Basename is to say bluntly a turd. You are much better off using File::Spec and or the functional equivelents in File::Spec::Functions. File::Basename was effectvely deprecated when File::Spec was created (which it was to fix a whole bunch of know problems in the Basename, some outstanding to this day) but it never was marked as such or removed for hysterical porpoises. File::Spec is in the core and it should be used as a general rule for manipulating paths and directories and the like.

      Anyway, the File::Spec::Functions way would be:

      use File::Spec::Functions qw(splitpath); my (undef,undef,$file)=splitpath($path);
      Or:
      use File::Spec; my ($vol,$path,$file)=File::Spec->splitpath($path);

      Also IMO its not worth ignoring the $vol just becuase _your_ OS doesnt have such a concept. If you use File::Spec and assume there will be a volume then everything will work out regardless of where youtr code ends up.


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


        It would be nice if File::Spec had a basename function, too. E.g. sub basename { (splitpath($_[0]))[-1] }. And probably also dirname.
      Hi Dave,

      The module File::Basename worked perfectly, just what I needed.

      Thanks,

      :) Peter

Re: Getting the filename from full path name ?
by etcshadow (Priest) on Apr 15, 2004 at 03:48 UTC
    For portability's sake, use the File::Spec or File::Basename module (because different OSs have different rules for what separates the path, and such). However, in answer to the general question of how one does this sort of string manipulation in perl:
    my @path_pieces = split('/', $path); my $last_path_piece = $path_pieces[$#path_pieces];
    or
    my ($last_path_piece) = $path =~ m|/(.*?)$|;
    my ($last_path_piece) = $path =~ m|([^/]*)$|;
    to give a couple of the more idiomatic methods.
    ------------ :Wq Not an editor command: Wq
      Your second solution matches the first / in the path. As Chris stated above you probably would rather do:

      my ($file) = $a =~ m|/([^/]+)$|;

      satchm0h
        Yes, sorry. I made the classic mistake of assuming that non-greedy matching was, in fact, non-greedy. In general, I avoid the use of non-greedy matching (instead, being explicit about what I don't want the match to bleed into) for this basic reason. I was just being sloppy.
        ------------ :Wq Not an editor command: Wq
Re: Getting the filename from full path name ?
by thor (Priest) on Apr 15, 2004 at 03:38 UTC
    While File::Basename is the way to go, I thought that your proposed solution was a bit thought provoking. So, I decided to see if I could do it. Here's what I came up with:
    $a = "foo/bar/baz"; my $pos = my $save_pos = 0; do { $pos = index($a, "/", $pos + 1 ); $save_pos = $pos if $pos != -1; } while ($pos != -1); print substr($a, $save_pos + 1);
    I'm sure that someone can whittle that down by quite a bit, but it was a fun diversion...:)

    thor

      m{/([^/]+)$}
      (split/\//)[-1]

      This is not a real solution - it is not portable. Please use File::Basename. Having said that...

      my $a = "foo/bar/baz"; my ($file) = $a =~ /\/(.*?)$/;
      Update: Thank you Chris, the code should be:
      my ($file) = $a =~ m|/([^/]+)$|;

        Actually something like my ($file) = $path =~ m|([^/\\]+)$| will work on Win32 or *nix and will also not fail to find the filname of 'file.txt'

        cheers

        tachyon

        I believe you mean
        my ($file) = $a =~ m|/([^/]+)$|;
        Your exp matches starting with the first slash

        Chris