Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Collapsing paths

by rvosa (Curate)
on Dec 05, 2007 at 12:28 UTC ( #655073=perlquestion: print w/ replies, xml ) Need Help??
rvosa has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I am trying to figure out how to go from a path such as foo/../bar/../baz/ to baz/. I hope I figured out the example correctly, but you see what I mean: collapse the up-and-down again stuff into a neat straight path. I read the pod for File::Spec three times over expecting it to be there but I don't see it. How do I do this without dodgy regexes?

Thanks!

Comment on Collapsing paths
Select or Download Code
Re: Collapsing paths
by lodin (Hermit) on Dec 05, 2007 at 12:45 UTC

    canonpath in File::Spec seems to do the trick.

    use File::Spec::Functions 'canonpath'; print canonpath('foo/../bar/../baz/'); __END__ baz

    lodin

    Update: added examplifying code, but see ikegami's reply below.

      canonpath in File::Spec seems to do the trick.

      Did you try it out?

      #!/usr/bin/perl use File::Spec; print File::Spec->canonpath("foo/../bar/../baz/")."\n";

      Output:

      foo/../bar/../baz

      I guess not. Did you read the docs? Quote:

      Note that this does *not* collapse x/../y sections into y. This is by design.
      I guess not.

      All dogma is stupid.
        Depends on your system.
        use File::Spec; print File::Spec->canonpath("foo/../bar/../baz/")."\n";
        \baz

        On system without symlinks, canonpath collapses x/../y (although not always correctly, as shown above).

        Update: Technically, NTFS does have symlinks, but most software act as if they don't exist, including File::Spec.

        Did you try it out?

        Yes I did. That's why I wrote "seems". I didn't know the behaviour was platform dependant. The documentation for File::Spec is contradicting the File::Spec::Win32 documentation which specifically says "foo/../bar" will become "bar".

        lodin

      I don't think it does, it looks like it removes extraneous slashes, but doesn't collapse ../../ path fragments.
Re: Collapsing paths
by tirwhan (Abbot) on Dec 05, 2007 at 12:45 UTC

    As also pointed out in File::Specs documentation, you can use the "realpath" function from Cwd for this. This will however return an absolute path and only resolve paths which actually exist in your filesystem (this is a feature, not a bug, because it's the only way to correctly handle symlinks).


    All dogma is stupid.
      Thanks, that solved it.
Re: Collapsing paths
by oha (Friar) on Dec 05, 2007 at 12:52 UTC
    if you are interested in doing it with regexp:
    { $path =~ s{(^|/)(\w+/\.\./|\./)}{$1} and redo }
    which collapses ./ and <dir>/../

    Oha

      It wouldn't hurt to make the loop more obvious.

      1 while $path =~ s{(^|/)(\w+/\.\./|\./)}{$1};

      It's even shorter.

Re: Collapsing paths
by johngg (Abbot) on Dec 05, 2007 at 14:34 UTC
    I have a module that includes this code ref. that you should be able to adapt to do what you want.

    # ---------- my $_cleanPath = sub # ---------- { my $absPath = shift; return $absPath if $absPath =~ m{^/$}; my @elems = split m{/}, $absPath; my $ptr = 1; while( $ptr <= $#elems ) { if( $elems[ $ptr ] eq q{} ) { splice @elems, $ptr, 1; } elsif( $elems[ $ptr ] eq q{.} ) { splice @elems, $ptr, 1; } elsif( $elems[ $ptr ] eq q{..} ) { if( $ptr < 2 ) { splice @elems, $ptr, 1; } else { $ptr --; splice @elems, $ptr, 2; } } else { $ptr ++; } } return $#elems ? join q{/}, @elems : q{/}; };

    I hope this is of use.

    Cheers,

    JohnGG

Re: Collapsing paths
by shmem (Canon) on Dec 05, 2007 at 14:44 UTC
    use Cwd qw(abs_path);
    qwurx [shmem] ~ > perl -MCwd=abs_path -le 'print abs_path("/usr/lib/pe +rl5/../../lib/perl5")' /usr/lib/perl5

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Collapsing paths
by merlyn (Sage) on Dec 05, 2007 at 17:02 UTC
    Just to be very specific, since the other answers in this thread only skirt around the issue (and some ignore it entirely and therefore do the wrong thing)...

    You do realize that "foo/../bar/bletch" cannot be reduced unless you're absolutely sure that "foo" is not a symlink, correct? Because if it is a symlink, then the ".." is relative to where "foo" points, not to where "foo" itself is located.

      Great point, thanks. There are no symlinks in the structure I'm moving around so it's not an issue in this case, though.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://655073]
Approved by tirwhan
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2014-12-20 08:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (95 votes), past polls