给定一个代表文件(或目录、设备等)的任意单个参数,如何获取该参数的绝对路径?
我看过很多关于这个问题的答案,其中涉及 find/ls/stat/readlink 和 $PWD,但没有一个符合我的需要。看起来最接近的答案是 ksh 的“whence”命令,但我需要它在 sh/bash 中工作。
假设文件 foo.txt 位于我的主目录 /Users/matthew/foo.txt 中。无论我当前的工作目录是什么(我将命令称为“abs”),我都需要以下行为:
(PWD is ~)
$ abs foo.txt
/Users/matthew/foo.txt
$ abs ~/foo.txt
/Users/matthew/foo.txt
$ abs ./foo.txt
/Users/matthew/foo.txt
$ abs /Users/matthew/foo.txt
/Users/matthew/foo.txt
“腹肌”到底是什么?
TIA,马修
答案1
在 Linux(使用 GNU coreutils)上,readlink -f
可以完成您需要的操作。
对于 BSD 衍生系统或 OS X,你可能需要做更多工作。请参阅https://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
答案2
这是我几年前写的一个 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);