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

Dear Monks,

I have sometimes found myself looking for a way to create pathnames with a trailing slash ("foo/bar/") and/or a "./" prefix ("./foo/bar") that is a little "safer" (platform-independent, etc.) than "./$path/". To name just a few examples of when this is needed: The behavior of rsync sometimes depends on whether a pathname has a trailing slash or not. In some cases, the "./" prefix makes a difference to find. Often file patterns, such as in gitignore, will match only directories if there is a trailing slash on the name.

I usually use File::Spec for all path manipulation, but that and Path::Class won't work because they always try to "clean up" the path they return. In Linux, File::Spec->catdir(".","foo","") simply returns "foo". Using File::Spec->catfile(".","foo","") only returns "foo/" and also feels a little hackish.

It would be nice if there were two functions that provided the "./" prefix and trailing separator. I have so far only found File::Util's SL constant which provides the directory separator, but that's only half the battle and I'd love to find a way to solve this using a core module, if at all possible.

I feel like I'm missing something. Any wisdom you may have on this topic would be greatly appreciated!

P.S. From what I've read there are some systems (VMS?) where the concept of a directory separator is not as simple as doing $path1 . $DIRSEP . $path2, but for starters a solution for the not-so-compilcated cases would be nice.

Replies are listed 'Best First'.
Re: Directory Separator (none)
by tye (Sage) on May 28, 2013 at 16:48 UTC

    I actually doubt that there is a Perl platform where '/' isn't as good a choice as anything. There are certainly systems out there that can run Perl that don't have "a directory separator". But, in Perl, '/' often works as a directory separator in at least some cases on even those platforms. Similarly, '/' works as a directory separator in most places in Perl even on platforms that have "a directory separator" that isn't (just) '/'.

    I believe that "use Foo::Bar;" sets $INC{'Foo/Bar.pm'} on every Perl platform, even ones with no "directory separator" and ones with a non-'/' primary directory separator.

    In particular, I bet rsync understands '/' even when run under Windows (as most things in Windows do as well). (It does for me, but my rsync on Windows is part of cygwin.)

    "Append a trailing directory separator" just isn't a portable concept. That's why the portable path manipulation modules don't support it. So doing portable path manipulation portably is a good idea. Appending a trailing directory separator in Perl for rsync might as well just be .= '/';.

    The somewhat portable concept is "give me a directory path (not a file path)". That is why File::Spec::Unix (etc.) has catdir() separate from catfile(). Sadly, File::Spec::Unix::catdir('foo','bar') returns "foo/bar" not "foo/bar/". You might consider filing a bug against that. I have no idea if those who would be notified of such a bug report would have any sympathy toward our point of view on that, however.

    - tye        

      Sadly, File::Spec::Unix::catdir('foo','bar') returns "foo/bar" not "foo/bar/". You might consider filing a bug against that. I have no idea if those who would be notified of such a bug report would have any sympathy toward our point of view on that, however.
      It's documented, so not a bug:
      catdir - Concatenate two or more directory names to form a complete path ending with a directory. But remove the trailing slash from the resulting string, because it doesn't look good, isn't necessary and confuses OS/2. Of course, if this is the root directory, don't cut off the trailing slash :-)

        Documentation doesn't have the power to make something "not a bug". It shows that the behavior was not unintentional. But with such "powerful" justifications, can one ever hope to get the bug fixed?

        But remove the trailing slash from the resulting string, because it doesn't look good,

        The only "look" about it is that it makes it look like a Unix directory path, not a non-directory path. Though, I've run into enough things that just ignore trailing slashes that I've learned to append "/." (not just "/") to my directories when it really matters.

        isn't necessary,

        rsync, for one, disagrees. Specifying the intent that a directory is meant is often important and only "not necessary" if "in a perfect world". "mv foo bar/" is only the same as "mv foo bar" if you are 100% sure that bar already exists and is a directory.

        and confuses OS/2.

        Rather vague, there. That sounds like it might be a reason to strip the final '/' when on OS/2. So it might be a reason to impact the behavior of File::Spec::OS2. It is almost silly to mention that in File::Spec::Unix documentation (yes, they are two separate modules).

        But I suspect the first justification is the real one and will actually end up being a powerful block against getting this improved. So more literal '/'s for all.

        - tye        

Re: Directory Separator
by vsespb (Chaplain) on May 28, 2013 at 16:44 UTC
    I think you can use File::Spec for platform independent work with file paths. But for rsync/find/file patterns there is no (should not be) such thing as platform independant patterns.
    Does rsync use different pattern language for different platforms? I doubt. So if you want patterns - use platform dependent code..