Вот сценарий Perl, который я написал несколько лет назад, и который делает именно это. Он использует символические ссылки, чтобы получить "канонический" путь:
#!/usr/bin/perl -w
use diagnostics;
require Cwd;
$CMD = $0;
$CMD =~ s,^.*/,,;
sub err {
print STDERR "\n",@_,"\n" if (scalar(@_));
exit(1);
}
sub warning {
print STDERR "\n",@_,"\n" if (scalar(@_));
}
sub syntax {
print STDERR @_,"\n" if (scalar(@_));
print STDERR "Use -h for help.\n";
exit(1);
}
sub help {
print(join("\n",
("Usage: $CMD [OPTIONS] file file ..",
" $CMD prints the 'real' path for each file by expanding all",
" symbolic links and resolving references to '.', '..' and",
" extra '/' characters. The resulting path will have no symbolic",
" link, '.' or '..' components.",
"Options:",
" -h // print this message",
" -v // show process of resolving file",
"")));
}
sub abspath {
my $path = shift(@_);
return ( ($path =~ m,^/,)
? $path
: ( (($#_ > -1) ? shift(@_) : Cwd::getcwd()) . "/" . $path )
);
}
sub realpath {
my $left_path = abspath(@_);
my @left;
my @right;
my @link;
my $link;
my $upcount = 0;
@right = ();
@left = split("/",$left_path);
shift(@left) if $#left;
while(@left) {
$left_path = "/" . join("/",@left);
printf("test: %s ## %s\n",$left_path,join("/",@right)) if $verbose;
if($left[$#left] eq "..") {
$upcount++;
pop(@left);
}
elsif($left[$#left] eq "." || $left[$#left] eq "") {
# a "/./" or a "//"
pop(@left);
}
elsif ($upcount) {
return undef unless $#left >= $upcount - 1;
$#left -= $upcount;
$upcount = 0;
}
else {
return undef unless ( -e $left_path );
if ( $link = readlink($left_path) ) {
printf(" : %s --> %s\n",$left_path,$link) if $verbose;
@link = split("/",$link);
if ($link[0] eq "") {
@left = @link;
shift(@left);
} else {
pop(@left);
push(@left,@link);
}
} else {
unshift(@right,pop(@left));
}
}
}
printf("done: /%s\n",join("/",@right)) if $verbose;
return("/" . join("/",@right));
}
$verbose = 0;
@files = ();
while ($arg = shift(@ARGV)) {
if ($arg eq "-h") {
help();
exit(9);
} elsif ($arg eq "-v") {
$verbose = 1;
} elsif ($arg eq "--") {
last;
} elsif ($arg =~ m/^-/) {
syntax("Unknown option: '$arg'");
} else {
unshift(@ARGV,$arg);
last;
}
}
@files = @ARGV;
@ARGV = ();
my $err = 0;
my $f;
my $p;
foreach $f (@files) {
print "\n" if $verbose;
$p = realpath($f);
if(defined($p)) {
print("$p\n");
}
else {
warning("$f: no such file\n");
$err++;
}
}
exit($err);