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

dirname($_).'/'.basename($_) should yield the original value of $_, right? (This is under Solaris, or Linux, before comments are made about the direction of the slash).

Well, as I found whilst working on some web stuff, if $_ is a directory, and ends in a /, it doesn't.

perl -MFile::Basename -e 'foreach (@ARGV) { print "$_: ", dirname ($_) +, " : ", basename($_), "\n"}' /home/oed /home/oed/. /home/oed/ /home/oed: /home : oed /home/oed/.: /home/oed : . /home/oed/: /home :

So either it's a bug, or I've missed something in the documentation, and am being blatently dumb. Please tell me it's the latter ;-)

--
Tommy
Too stupid to live.
Too stubborn to die.

Replies are listed 'Best First'.
Re: Bug? in File::Basename
by sauoq (Abbot) on Aug 06, 2003 at 09:37 UTC

    It's the latter. :-)

    The relevant portion of the docs is under the explanation of dirname() where it states:

    When using Unix or MSDOS syntax, the return value conforms to th +e behav- ior of the Unix shell command dirname(1). This is usually +the same as the behavior of fileparse(), but differs in some cases. + For example, for the input file specification lib/, fileparse() + consid- ers the directory name to be lib/, while dirname() consider +s the directory name to be .).
    Have a look at how dirname (the unix command) behaves:
    $ dirname foo/bar foo $ dirname foo/bar/ foo $ dirname foo/bar/. foo/bar
    So, File::Basename is working as advertised. You should probably be using fileparse() rather than dirname() to get what you want. The docs guarantee that you can concatenate the components returned by fileparse() and get back the original filename.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Bug? in File::Basename
by adrianh (Chancellor) on Aug 06, 2003 at 09:37 UTC
    irname($_).'/'.basename($_) should yield the original value of $_, right?

    Nope :-)

    It returns the dirname(), a slash, and the basename() appended together.

    File::Basename just deals with strings. It doesn't do any file system checks. So when it sees:

    /foo/bar/

    It knows that bar is a directory because of the trailing slash. So the basename is the empty string.

    With:

    /foo/bar

    there is no trailing slash so the basename is bar.

    To quote from the docs:

    The fileparse() routine divides a file specification into three parts: a leading path, a file name, and a suffix. The path contains everything up to and including the last directory separator in the input file specification. The remainder of the input file specification is then divided into name and suffix based on the optional patterns you specify in @suf- fixlist. Each element of this list can be a qr-quoted pattern (or a string which is interpreted as a regular expression), and is matched against the end of name. If this succeeds, the matching portion of name is removed and prepended to suffix. By proper use of @suffixlist, you can remove file types or versions for examination.

    You are guaranteed that if you concatenate path, name, and suffix together in that order, the result will denote the same file as the input file specification.