Unix 列出绝对文件名

Unix 列出绝对文件名

给定一个代表文件(或目录、设备等)的任意单个参数,如何获取该参数的绝对路径?

我看过很多关于这个问题的答案,其中涉及 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);

相关内容