#!/usr/bin/perl use 5.018003; use warnings; use Cwd qw( abs_path ); use Path::Tiny; use File::Spec; my @pth = qw( /a/b/c/d/../../../e /a/../b/./c//d ../scripting ./tmp /tmp/../../../tmp ); sub resolves { my ($p, $r) = @_; printf "%-20s -> %s\n", $p, $r // "$p does not resolve"; } # resolves say "OP"; foreach my $pth (@pth) { my @c = reverse split m{/+}, $pth; # /+ removes empty elements my @c_new; while (@c) { my $component = shift @c; next unless length ($component); $component eq "." and @c and next; if ($component eq ".." and @c) { my $i = 0; while ($i <= $#c && $c[$i] =~ m/^\.{0,2}$/) { $i++; } splice @c, $i, 1; next; } push @c_new => $component; } @c = reverse @c_new; $c[0] =~ m/^\.\.?$/ or unshift @c => ""; resolves $pth, join "/" => @c; } say "Cwd::abs_path"; foreach my $pth (@pth) { resolves $pth, abs_path ($pth); } say "Path::Tiny::path"; foreach my $pth (@pth) { resolves $pth, Path::Tiny::path ($pth); } say "File::Spec::canonpath"; foreach my $pth (@pth) { resolves $pth, File::Spec->canonpath ($pth); }