in reply to Yet another substitution problem

TMTOWTDI:
$_ = my $original = "/one/two///three/four/..//fourX/five////six/seven +"; # you can do job 1 alone like this: tr!/!/!s; print "1: $original -> $_\n"; # OR you can do jobs 1 and 2 together like this; my @srcpaths = split m!/+!, $original; my @outpaths = (); for my $p ( @srcpaths ) { if ( $p eq '..' ) { pop @outpaths; } else { push @outpaths, $p; } } $_ = join( "/", @outpaths ); print "2: $original -> $_\n";
The latter is more verbose, but it's easier to see and understand what's being done; also, it will work on things like /one/two/three/../../two/three (which the regex solutions suggested above don't handle). If you want, you can even test for existence as you go (with -d join("/",@outpaths) (*)), and test for paths that are just wrong (too many "/.."):
pop @outpaths or die "Bad path: $original\n";

The regex solutions were assuming you only want to remove the "/.." in cases where the next part of the path matched the previous part of the path, but it seems to me that you really should have a solution that handles all the "/.." cases -- it's entirely possible that you'll run into a lot of different ones.

(*) Update: footnote about -d join("/",@outpaths) -- with an absolute path like "/one/two", $p is an empty string on the first iteration, and you don't want to use "-d" on that. Putting full error checks into the loop would be something like this:

my @outpath = (); for my $p ( split m!/+!, $original ) { if ( $p eq '..' ) { pop @outpath or die "Bad path: $original"; } else { if ( @outpath ) { my $chk = join "/", @outpath, $p; die "Path not found: $chk" unless ( -d $chk ); } push @outpath, $p; } } my $goodpath = join "/", @outpath;

Replies are listed 'Best First'.
Re^2: 2 substitution problems
by parv (Parson) on Mar 28, 2009 at 16:10 UTC

    If the original path started out with ../, program will die too soon for no good reason. In this case, ../ should be preserved.

      Yup, good point. And the best way to fix it would be to "use Cwd", which makes all that code unnecessary anyway:
      #!/usr/bin/perl use Cwd qw/abs_path/; print abs_path( $_ ) for ( @ARGV );
      That prints an empty string (no error message) whenever any non-final path component is non-existent or unreadable (due to permissions).