Учитывая произвольный единственный аргумент, представляющий файл (или каталог, устройство и т.д.), Как мне получить абсолютный путь аргумента?

Я видел много ответов на этот вопрос, включая find/ls/stat/readlink и $ PWD, но ни один не отвечал моим потребностям. Похоже, что ближайший ответ - команда откуда-то ksh, но мне нужно, чтобы она работала в 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, Мэтью

2 ответа2

5

В 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

1

Вот сценарий 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);

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .