$_ = 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;
|