如何检索给定相对路径的绝对路径

发布于 2024-10-02 07:03:55 字数 167 浏览 1 评论 0原文

是否有命令可以检索给定相对路径的绝对路径?

例如我希望 $line 包含 dir ./etc/ 中每个文件的绝对路径

find ./ -type f | while read line; do
   echo $line
done

Is there a command to retrieve the absolute path given a relative path?

For example I want $line to contain the absolute path of each file in dir ./etc/

find ./ -type f | while read line; do
   echo $line
done

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(26

夏末的微笑 2024-10-09 07:03:55

尝试真实路径

~ $ sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

要避免扩展符号链接,请使用realpath -s

答案来自“bash/fish 命令打印绝对路径文件路径”。

Try realpath.

~ $ sudo apt-get install realpath  # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc

To avoid expanding symlinks, use realpath -s.

The answer comes from "bash/fish command to print absolute path to a file".

·深蓝 2024-10-09 07:03:55

如果您安装了 coreutils 软件包,通常可以使用 readlink -frelative_file_name 来检索绝对文件名(已解析所有符号链接)

If you have the coreutils package installed you can generally use readlink -f relative_file_name in order to retrieve the absolute one (with all symlinks resolved)

木槿暧夏七纪年 2024-10-09 07:03:55
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPD 一些解释

  1. 该脚本获取相对路径作为参数 "$1"
  2. 然后我们获取该路径的 dirname 部分(您可以传递 dir 或 file到此脚本): dirname "$1"
  3. 然后我们 cd "$(dirname "$1") 进入这个相对目录,并通过运行 pwd 获取它的绝对路径 shell 命令
  4. 之后,我们将basename附加到绝对路径:$(basename "$1")
  5. 作为最后一步,我们echo
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPD Some explanations

  1. This script get relative path as argument "$1"
  2. Then we get dirname part of that path (you can pass either dir or file to this script): dirname "$1"
  3. Then we cd "$(dirname "$1") into this relative dir and get absolute path for it by running pwd shell command
  4. After that we append basename to absolute path: $(basename "$1")
  5. As final step we echo it
流年里的时光 2024-10-09 07:03:55

使用:

find "$(pwd)"/ -type f

获取所有文件或

echo "$(pwd)/$line"

显示完整路径(如果相对路径很重要)

use:

find "$(pwd)"/ -type f

to get all files or

echo "$(pwd)/$line"

to display full path (if relative path matters to)

不喜欢何必死缠烂打 2024-10-09 07:03:55

不管怎样,我投票给了所选择的答案,但想分享一个解决方案。缺点是,它仅限于 Linux - 在进入 Stack Overflow 之前,我花了大约 5 分钟试图找到 OSX 的等效版本。我确信它就在那里。

在 Linux 上,您可以将 readlink -edirname 结合使用。

$(dirname $(readlink -e ../../../../etc/passwd))

然后你使用

/etc/

dirname 的姐妹 basename 来获取
文件名

$(basename ../../../../../passwd)

产生

passwd

把它们放在一起..

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

产生

/etc/passwd

如果你的目标是目录,那么你是安全的,basename不会返回任何内容
你最终会在最终输出中得到双斜杠。

For what it's worth, I voted for the answer that was picked, but wanted to share a solution. The downside is, it's Linux only - I spent about 5 minutes trying to find the OSX equivalent before coming to Stack overflow. I'm sure it's out there though.

On Linux you can use readlink -e in tandem with dirname.

$(dirname $(readlink -e ../../../../etc/passwd))

yields

/etc/

And then you use dirname's sister, basename to just get
the filename

$(basename ../../../../../passwd)

yields

passwd

Put it all together..

F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"

yields

/etc/passwd

You're safe if you're targeting a directory, basename will return nothing
and you'll just end up with double slashes in the final output.

眼泪淡了忧伤 2024-10-09 07:03:55

我认为这是最可移植的:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

如果路径不存在,它将失败。

I think this is the most portable:

abspath() {                                               
    cd "$(dirname "$1")"
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
    cd "$OLDPWD"
}

It will fail if the path does not exist though.

诗笺 2024-10-09 07:03:55

realpath 可能是最好的

但是......

最初的问题一开始就非常混乱,示例很差
与所述问题相关。

所选答案实际上回答了给出的示例,但根本不回答
标题中的问题。第一个命令就是答案(是吗?
真的 ?我怀疑),并且没有“/”也可以做到。我看不到
第二个命令正在做什么。

有几个问题是混合在一起的:

  • 将相对路径名更改为绝对路径名,无论它是什么
    表示,可能什么也没有。
    通常,如果您发出诸如 touch foo/bar 之类的命令,
    路径名 foo/bar 必须存在,并且可能用于
    在实际创建文件之前进行计算。
    )

  • 可能有多个绝对路径名表示同一个文件
    (或潜在文件),特别是因为符号链接(symlinks)
    在路径上,但可能出于其他原因(设备可能是
    以只读方式安装两次)。人们可能想也可能不想解决
    明确这样的符号链接。

  • 到达非符号链接的符号链接链的末尾
    文件或名称。这可能会也可能不会产生绝对路径名,
    取决于它是如何完成的。人们可能愿意,也可能不愿意
    将其解析为绝对路径名。

不带选项的命令 readlink foo 仅在以下情况下给出答案
参数 foo 是一个符号链接,答案就是它的值
符号链接。没有其他链接被跟随。答案可能是相对路径:
无论符号链接参数的值是什么。

但是,readlink 具有适用于所有内容的选项(-f -e 或 -m)
文件,并给出一个绝对路径名(没有符号链接的路径名)
参数实际表示的文件。

这对于任何不是符号链接的东西都适用,尽管有人可能会这样做
希望使用绝对路径名而不解析中间路径
路径上的符号链接。这是通过命令 realpath -s foo 完成的。

在符号链接参数的情况下,readlink 及其选项将
再次解析参数绝对路径上的所有符号链接,但是
它还将包括可能遇到的所有符号链接
跟随参数值。如果您想要一个,您可能不希望这样
参数符号链接本身的绝对路径,而不是任何内容的绝对路径
它可能链接到。同样,如果 foo 是符号链接,则 realpath -s foo
获取绝对路径而不解析符号链接,包括符号链接
作为参数给出。

如果没有 -s 选项,realpath 的作用与
readlink,除了简单的读取一个链接的值,还有几个
其他事情。我只是不清楚为什么 readlink 有它的
选项,显然造成了不受欢迎的冗余
真实路径。

探索网络就不多说了,除了可能有
不同系统之间存在一些差异。

结论:realpath 是最好用的命令,使用最多
灵活性,至少对于此处要求的使用而言。

realpath is probably best

But ...

The initial question was very confused to start with, with an example poorly
related to the question as stated.

The selected answer actually answers the example given, and not at all
the question in the title. The first command is that answer (is it
really ? I doubt), and could do as well without the '/'. And I fail to see
what the second command is doing.

Several issues are mixed :

  • changing a relative pathname into an absolute one, whatever it
    denotes, possibly nothing.
    (Typically, if you issue a command such as touch foo/bar, the
    pathname foo/bar must exist for you, and possibly be used in
    computation, before the file is actually created.
    )

  • there may be several absolute pathname that denote the same file
    (or potential file), notably because of symbolic links (symlinks)
    on the path, but possibly for other reasons (a device may be
    mounted twice as read-only). One may or may not want to resolve
    explicity such symlinks.

  • getting to the end of a chain of symbolic links to a non-symlink
    file or name. This may or may not yield an absolute path name,
    depending on how it is done. And one may, or may not want to
    resolve it into an absolute pathname.

The command readlink foo without option gives an answer only if its
argument foo is a symbolic link, and that answer is the value of that
symlink. No other link is followed. The answer may be a relative path:
whatever was the value of the symlink argument.

However, readlink has options (-f -e or -m) that will work for all
files, and give one absolute pathname (the one with no symlinks) to
the file actually denoted by the argument.

This works fine for anything that is not a symlink, though one might
desire to use an absolute pathname without resolving the intermediate
symlinks on the path. This is done by the command realpath -s foo

In the case of a symlink argument, readlink with its options will
again resolve all symlinks on the absolute path to the argument, but
that will also include all symlinks that may be encountered by
following the argument value. You may not want that if you desired an
absolute path to the argument symlink itself, rather than to whatever
it may link to. Again, if foo is a symlink, realpath -s foo will
get an absolute path without resolving symlinks, including the one
given as argument.

Without the -s option, realpath does pretty much the same as
readlink, except for simply reading the value of a link, as well as several
other things. It is just not clear to me why readlink has its
options, creating apparently an undesirable redundancy with
realpath.

Exploring the web does not say much more, except that there may be
some variations across systems.

Conclusion : realpath is the best command to use, with the most
flexibility, at least for the use requested here.

乙白 2024-10-09 07:03:55

我最喜欢的解决方案是@EugenKonkov 的这个,因为它并不意味着存在其他实用程序(coreutils 包)。

但相对路径“。”失败了。和“..”,所以这里是处理这些特殊情况的稍微改进的版本。

但是,如果用户没有 cd 权限进入相对路径的父目录,它仍然会失败。

#! /bin/sh

# Takes a path argument and returns it as an absolute path. 
# No-op if the path is already absolute.
function to-abs-path {
    local target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

My favourite solution was the one by @EugenKonkov because it didn't imply the presence of other utilities (the coreutils package).

But it failed for the relative paths "." and "..", so here is a slightly improved version handling these special cases.

It still fails if the user doesn't have the permission to cd into the parent directory of the relative path, though.

#! /bin/sh

# Takes a path argument and returns it as an absolute path. 
# No-op if the path is already absolute.
function to-abs-path {
    local target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}
冰葑 2024-10-09 07:03:55

尤金的答案对我来说不太有效,但这确实有效:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

旁注,您当前的工作目录不受影响。

Eugen's answer didn't quite work for me but this did:

    absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"

Side note, your current working directory is unaffected.

绝不放开 2024-10-09 07:03:55

恕我直言,最好的解决方案是在这里发布的:https://stackoverflow.com/a/3373298/9724628

它确实需要 python 才能工作,但它似乎涵盖了所有或大部分边缘情况,并且是非常可移植的解决方案。

  1. 解析符号链接:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
  1. 或不解析:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

The best solution, imho, is the one posted here: https://stackoverflow.com/a/3373298/9724628.

It does require python to work, but it seems to cover all or most of the edge cases and be very portable solution.

  1. With resolving symlinks:
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
  1. or without it:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
七秒鱼° 2024-10-09 07:03:55

对 @ernest-a 相当不错的版本的改进:

absolute_path() {
    cd "$(dirname "$1")"
    case $(basename $1) in
        ..) echo "$(dirname $(pwd))";;
        .)  echo "$(pwd)";;
        *)  echo "$(pwd)/$(basename $1)";;
    esac
}

这可以正确处理路径最后一个元素是 .. 的情况,在这种情况下 "$(pwd)/$( @ernest-a 的答案中的 basename "$1")" 将显示为 accurate_sub_path/spurious_subdirectory/..

An improvement to @ernest-a's rather nice version:

absolute_path() {
    cd "$(dirname "$1")"
    case $(basename $1) in
        ..) echo "$(dirname $(pwd))";;
        .)  echo "$(pwd)";;
        *)  echo "$(pwd)/$(basename $1)";;
    esac
}

This deals correctly with the case where the last element of the path is .., in which case the "$(pwd)/$(basename "$1")" in @ernest-a's answer will come through as accurate_sub_path/spurious_subdirectory/...

天气好吗我好吗 2024-10-09 07:03:55

对于find,最简单的方法可能是只给出要搜索的绝对路径,例如:

find /etc
find `pwd`/subdir_of_current_dir/ -type f

In case of find, it's probably easiest to just give the absolute path for it to search in, e.g.:

find /etc
find `pwd`/subdir_of_current_dir/ -type f
窗影残 2024-10-09 07:03:55

如果您在 Mac OS X 上使用 bash,既没有 realpath 存在,也没有 readlink 可以打印绝对路径,您可以选择编写自己的版本来打印它。
这是我的实现:

(纯 bash)

abspath(){
  local thePath
  if [[ ! "$1" =~ ^/ ]];then
    thePath="$PWD/$1"
  else
    thePath="$1"
  fi
  echo "$thePath"|(
  IFS=/
  read -a parr
  declare -a outp
  for i in "${parr[@]}";do
    case "$i" in
    ''|.) continue ;;
    ..)
      len=${#outp[@]}
      if ((len==0));then
        continue
      else
        unset outp[$((len-1))] 
      fi
      ;;
    *)
      len=${#outp[@]}
      outp[$len]="$i"
      ;;
    esac
  done
  echo /"${outp[*]}"
)
}

(使用 gawk)

abspath_gawk() {
    if [[ -n "$1" ]];then
        echo $1|gawk '{
            if(substr($0,1,1) != "/"){
                path = ENVIRON["PWD"]"/"$0
            } else path = $0
            split(path, a, "/")
            n = asorti(a, b,"@ind_num_asc")
            for(i in a){
                if(a[i]=="" || a[i]=="."){
                    delete a[i]
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            m = 0
            while(m!=n){
                m = n
                for(i=1;i<=n;i++){
                    if(a[b[i]]==".."){
                        if(b[i-1] in a){
                            delete a[b[i-1]]
                            delete a[b[i]]
                            n = asorti(a, b, "@ind_num_asc")
                            break
                        } else exit 1
                    }
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            if(n==0){
                printf "/"
            } else {
                for(i=1;i<=n;i++){
                    printf "/"a[b[i]]
                }
            }
        }'
    fi
}

(纯 bsd awk)

#!/usr/bin/env awk -f
function abspath(path,    i,j,n,a,b,back,out){
  if(substr(path,1,1) != "/"){
    path = ENVIRON["PWD"]"/"path
  }
  split(path, a, "/")
  n = length(a)
  for(i=1;i<=n;i++){
    if(a[i]==""||a[i]=="."){
      continue
    }
    a[++j]=a[i]
  }
  for(i=j+1;i<=n;i++){
    delete a[i]
  }
  j=0
  for(i=length(a);i>=1;i--){
    if(back==0){
      if(a[i]==".."){
        back++
        continue
      } else {
        b[++j]=a[i]
      }
    } else {
      if(a[i]==".."){
        back++
        continue
      } else {
        back--
        continue
      }
    }
  }
  if(length(b)==0){
    return "/"
  } else {
    for(i=length(b);i>=1;i--){
      out=out"/"b[i]
    }
    return out
  }
}

BEGIN{
  if(ARGC>1){
    for(k=1;k<ARGC;k++){
      print abspath(ARGV[k])
    }
    exit
  }
}
{
  print abspath($0)
}

示例:

$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war

If you are using bash on Mac OS X which neither has realpath existed nor its readlink can print the absolute path, you may have choice but to code your own version to print it.
Here is my implementation:

(pure bash)

abspath(){
  local thePath
  if [[ ! "$1" =~ ^/ ]];then
    thePath="$PWD/$1"
  else
    thePath="$1"
  fi
  echo "$thePath"|(
  IFS=/
  read -a parr
  declare -a outp
  for i in "${parr[@]}";do
    case "$i" in
    ''|.) continue ;;
    ..)
      len=${#outp[@]}
      if ((len==0));then
        continue
      else
        unset outp[$((len-1))] 
      fi
      ;;
    *)
      len=${#outp[@]}
      outp[$len]="$i"
      ;;
    esac
  done
  echo /"${outp[*]}"
)
}

(use gawk)

abspath_gawk() {
    if [[ -n "$1" ]];then
        echo $1|gawk '{
            if(substr($0,1,1) != "/"){
                path = ENVIRON["PWD"]"/"$0
            } else path = $0
            split(path, a, "/")
            n = asorti(a, b,"@ind_num_asc")
            for(i in a){
                if(a[i]=="" || a[i]=="."){
                    delete a[i]
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            m = 0
            while(m!=n){
                m = n
                for(i=1;i<=n;i++){
                    if(a[b[i]]==".."){
                        if(b[i-1] in a){
                            delete a[b[i-1]]
                            delete a[b[i]]
                            n = asorti(a, b, "@ind_num_asc")
                            break
                        } else exit 1
                    }
                }
            }
            n = asorti(a, b, "@ind_num_asc")
            if(n==0){
                printf "/"
            } else {
                for(i=1;i<=n;i++){
                    printf "/"a[b[i]]
                }
            }
        }'
    fi
}

(pure bsd awk)

#!/usr/bin/env awk -f
function abspath(path,    i,j,n,a,b,back,out){
  if(substr(path,1,1) != "/"){
    path = ENVIRON["PWD"]"/"path
  }
  split(path, a, "/")
  n = length(a)
  for(i=1;i<=n;i++){
    if(a[i]==""||a[i]=="."){
      continue
    }
    a[++j]=a[i]
  }
  for(i=j+1;i<=n;i++){
    delete a[i]
  }
  j=0
  for(i=length(a);i>=1;i--){
    if(back==0){
      if(a[i]==".."){
        back++
        continue
      } else {
        b[++j]=a[i]
      }
    } else {
      if(a[i]==".."){
        back++
        continue
      } else {
        back--
        continue
      }
    }
  }
  if(length(b)==0){
    return "/"
  } else {
    for(i=length(b);i>=1;i--){
      out=out"/"b[i]
    }
    return out
  }
}

BEGIN{
  if(ARGC>1){
    for(k=1;k<ARGC;k++){
      print abspath(ARGV[k])
    }
    exit
  }
}
{
  print abspath($0)
}

example:

$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
但可醉心 2024-10-09 07:03:55

我发现Eugen Konkov的答案是最好的它不需要安装任何程序。但是,对于不存在的目录,它将失败。

我写了一个适用于不存在目录的函数:

function getRealPath()
{
    local -i traversals=0
    currentDir="$1"
    basename=''
    while :; do
        [[ "$currentDir" == '.' ]] && { echo "$1"; return 1; }
        [[ $traversals -eq 0 ]] && pwd=$(cd "$currentDir" 2>&1 && pwd) && { echo "$pwd/$basename"; return 0; }
        currentBasename="$(basename "$currentDir")"
        currentDir="$(dirname "$currentDir")"
        [[ "$currentBasename" == '..' ]] && (( ++traversals )) || { [[ traversals -gt 0 ]] && (( traversals-- )) || basename="$currentBasename/$basename"; }
    done
}

它解决了不存在目录的问题,通过用dirname向上遍历,直到cd成功,然后返回当前目录加上被 dirname 删除的所有内容。

I found Eugen Konkov's answer to be the best as it doesn't require installing any program. However, it will fail for non-existent directories.

I wrote a function that works for non-existent directories:

function getRealPath()
{
    local -i traversals=0
    currentDir="$1"
    basename=''
    while :; do
        [[ "$currentDir" == '.' ]] && { echo "$1"; return 1; }
        [[ $traversals -eq 0 ]] && pwd=$(cd "$currentDir" 2>&1 && pwd) && { echo "$pwd/$basename"; return 0; }
        currentBasename="$(basename "$currentDir")"
        currentDir="$(dirname "$currentDir")"
        [[ "$currentBasename" == '..' ]] && (( ++traversals )) || { [[ traversals -gt 0 ]] && (( traversals-- )) || basename="$currentBasename/$basename"; }
    done
}

It solves the problem of non-existent directories by traversing up with dirname until cd succeeds, then returning the current directory plus everything that was removed by dirname.

面如桃花 2024-10-09 07:03:55

与 @ernest-a 的答案类似,但不影响 $OLDPWD 或定义一个新函数,您可以触发子 shell (cd; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups

Similar to @ernest-a's answer but without affecting $OLDPWD or define a new function you could fire a subshell (cd <path>; pwd)

$ pwd
/etc/apache2
$ cd ../cups 
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
自控 2024-10-09 07:03:55

您可以对任何相对路径 $line 使用 bash 字符串替换:

line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line

基本的字符串前面替换遵循以下公式
${string/#substring/replacement}
这里讨论得很好: https://www.tldp.org/ LDP/abs/html/string-manipulation.html

当我们希望它成为我们查找/替换的字符串的一部分时,\ 字符会否定 /

You could use bash string substitution for any relative path $line:

line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line

The basic front-of-string substitution follows the formula
${string/#substring/replacement}
which is discussed well here: https://www.tldp.org/LDP/abs/html/string-manipulation.html

The \ character negates the / when we want it to be part of the string that we find/replace.

亣腦蒛氧 2024-10-09 07:03:55

我无法找到在 Mac OS Catalina、Ubuntu 16 和 Centos 7 之间完美移植的解决方案,因此我决定使用 python 内联来实现,它非常适合我的 bash 脚本。

to_abs_path() {
  python -c "import os; print os.path.abspath('$1')"
}

to_abs_path "/some_path/../secrets"

I was unable to find a solution that was neatly portable between Mac OS Catalina, Ubuntu 16 and Centos 7, so I decided to do it with python inline and it worked well for my bash scripts.

to_abs_path() {
  python -c "import os; print os.path.abspath('$1')"
}

to_abs_path "/some_path/../secrets"
白况 2024-10-09 07:03:55

BLUF

cd $relative_path ; pwd

这是符合 POSIX 标准的解释(我认为),所以它应该适用于任何平台。

当然,这是可以编写脚本的,但我认为将其分解可能会让某些人更容易理解/修改特定的用例。

您可以使用 whichlocatefind、完整路径等。

x=your_file_name

$ x="nvi" 

file 轻松指示符号链接

$ file -h `which $x`
/usr/local/bin/nvi: symbolic link to ../Cellar/nvi/1.81.6_5/bin/nvi

接下来,稍微整理一下输出,以便我们获得“完整”相对路径。
我们只需要删除这个例子中的中间部分。

注意大写 Y 与小写 x。可能有一种更干净的方法可以做到这一点。

$ Y=$(file -h `which $x` | sed "s/$x: symbolic link to //")


$ echo $Y
/usr/local/bin/../Cellar/nvi/1.81.6_5/bin/nvi

使用dirname我们只得到路径部分。 cd 到它,名称应该会自行清理。

$ cd `dirname $Y` ; pwd
/usr/local/Cellar/nvi/1.81.6_5/bin

这引导我们使用古老的 UNIX 技巧,通过在括号/子 shell 中完成所有操作来“幽灵行走”目录。完成后,这实际上使我们返回到当前目录。

为了完整起见,我们可以将实际的文件名贴回末尾。

ls 甚至可以确保绝对路径有效,以获得奖励积分。

$ ( cd `dirname ${Y}` ; ls `pwd`/${x} )
/usr/local/Cellar/nvi/1.81.6_5/bin/nvi

所以 /usr/local/bin/nvi 实际上是 /usr/local/Cellar/nvi/1.81.6_5/bin/nvi

快速“转换”路径的简化示例:

$ (cd /usr/local/bin/../Cellar/nvi/1.81.6_5/bin ; pwd)
/usr/local/Cellar/nvi/1.81.6_5/bin

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/file .html
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname。 html

BLUF:

cd $relative_path ; pwd

Here is an explanation that is POSIX compliant (I think), so it should work on any platform.

This is scriptable, of course, but I think breaking it down might make it easier for some people to understand / modify to a particular use case.

You can use which, locate, find, full paths, whatever.

x=your_file_name

$ x="nvi" 

file easily indicates symlinks

$ file -h `which $x`
/usr/local/bin/nvi: symbolic link to ../Cellar/nvi/1.81.6_5/bin/nvi

Next, munge the output a bit so we get a "complete" relative path.
We just need to remove the middle part in this example.

Note UPPERCASE Y vs lowercase x. There is probably a cleaner way to do this.

$ Y=$(file -h `which $x` | sed "s/$x: symbolic link to //")


$ echo $Y
/usr/local/bin/../Cellar/nvi/1.81.6_5/bin/nvi

Using dirname we get just the path portion. cd to it and the name should clean itself up.

$ cd `dirname $Y` ; pwd
/usr/local/Cellar/nvi/1.81.6_5/bin

That leads us to the old UNIX trick to "ghost walk" a directory by doing it all in parenthesis / a sub-shell. This effectively returns us to our current directory when done.

We can stick the actual file name back on the end for completeness.

ls even makes sure the absolute path is valid, for bonus points.

$ ( cd `dirname ${Y}` ; ls `pwd`/${x} )
/usr/local/Cellar/nvi/1.81.6_5/bin/nvi

So /usr/local/bin/nvi is really /usr/local/Cellar/nvi/1.81.6_5/bin/nvi

Simplified example to quickly "convert" a path:

$ (cd /usr/local/bin/../Cellar/nvi/1.81.6_5/bin ; pwd)
/usr/local/Cellar/nvi/1.81.6_5/bin

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html

飘过的浮云 2024-10-09 07:03:55

他们所说的,除了 find $PWD 或(在 bash 中)find ~+ 更方便一些。

What they said, except find $PWD or (in bash) find ~+ is a bit more convenient.

是你 2024-10-09 07:03:55

如果相对路径是目录路径,那么试试我的,应该是最好的:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath

If the relative path is a directory path, then try mine, should be the best:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)

echo $absPath
自由如风 2024-10-09 07:03:55

这是一个相当短的函数,可用于仅使用 POSIX shell 和 readlink 语义来完全绝对化和规范化任何给定的输入路径:

canonicalize_path() {
    (
        FILEPATH="$1"
        for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
        do
            cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
            if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
            then
                break
            fi
        done
        cd -P "." || return $?
        echo "$(pwd)/${FILEPATH}"
    )
}

如果引用的文件不存在,则仅使用通向最终文件名的目录路径将会得到解决。如果通向文件路径的任何目录不存在,则会返回 cd 错误。这恰好是它试图模仿的 GNU/Linux readlink -f 命令的确切语义。

在 bash/zsh 中,您还可以将数字列表压缩为 {1..8} 或类似内容。选择 8 的数量是因为这是 Linux 多年来的最大限制,之后在版本 4.2 中将其更改为整个路径的 40 个分辨率的总限制。如果达到分辨率限制,代码不会失败,而是返回最后考虑的路径 - 可以添加显式的 [ -L "${FILEPATH}" ] 检查来检测这个条件不过。

通过简单地删除函数和子 shell 包装器,此代码还可以轻松地重用,以确保当前工作目录与文件系统中执行的脚本的位置匹配(shell 脚本的常见要求):

FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
do
    cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
    if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
    then
        break
    fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"

Here's a rather short function that can be used to fully absolutize and canonicalize any given input path using only POSIX shell and readlink semantics:

canonicalize_path() {
    (
        FILEPATH="$1"
        for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
        do
            cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
            if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
            then
                break
            fi
        done
        cd -P "." || return $?
        echo "$(pwd)/${FILEPATH}"
    )
}

If the referenced file does not exist only the directory path leading up to the final filename will be resolved. If the any of the directories leading up to the file path does not exist a cd error will be returned. This happens to be the exact semantics of the GNU/Linux readlink -f command it tries to mimic.

In bash/zsh you can also compact the list of numbers to just {1..8} or similar. The number of 8 was chosen as this was maximum limit in Linux for many years before the changed it a total limit of 40 resolution for the entire path in version 4.2. If the resolution limit is reached the code will not fail, but instead return last considered path instead – an explicit [ -L "${FILEPATH}" ] check could be added to detect this condition however.

This code can also be easily reused for ensuring the current working directory matches the executed script's location in the filesystem (a common requirement for shell scripts), by simply removing the function and subshell wrapper:

FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8;  # Maximum symlink recursion depth
do
    cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/"  # cd $(dirname)
    if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
    then
        break
    fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"
动听の歌 2024-10-09 07:03:55
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

在上面的代码片段中,awk 用于分割从 echo (通过管道)接收到的目录字符串并创建一个数组。

以下是所发生情况的分步列表:

  • 以空格分隔的目录列表通过管道传输到 awk
  • awk 通过空格将管道文本拆分为数组
  • awk 对数组中的每个项目(变量 i)进行循环
  • awk 使用 awk 系统命令运行 cli 命令
  • CLI 命令将目录 (cd) 更改为数组中项目“i”指定的路径,然后使用环境变量 $PWD 打印(回显)工作目录”

使用 cd 移动到每个目录,然后打印 $PWD 的值是让操作系统确定绝对路径的好方法

echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

In the snippet above awk is used to split the string of directories received from echo (via the pipeline) and creates an array.

Here is a step-by-step list of what is happening:

  • Space delimited list of directories is piped to awk
  • awk splits piped text into an array by spaces
  • awk loops over each item in the array (variable i)
  • awk uses the awk system command to run a cli command
  • The CLI command changes directory (cd) to the path specified by item "i" in the array and then prints (echo) the working directory using the environment variable $PWD"

Using cd to travel to each directory and then print the value of $PWD is a great way to have the OS do the work of determining an absolute path

白况 2024-10-09 07:03:55

如果您想将包含相对路径的变量转换为绝对路径,则可以这样做:

   dir=`cd "$dir"`

“cd”会在不更改工作目录的情况下进行回显,因为在子 shell 中执行。

If you want to transform a variable containing a relative path into an absolute one, this works :

   dir=`cd "$dir"`

"cd" echoes without changing the working directory, because executed here in a sub-shell.

日久见人心 2024-10-09 07:03:55

这是来自所有其他解决方案的链式解决方案,例如,当 realpath 失败时,要么因为未安装,要么因为它以错误代码退出,然后尝试下一个解决方案,直到获得正确的路径。

#!/bin/bash

function getabsolutepath() {
    local target;
    local changedir;
    local basedir;
    local firstattempt;

    target="${1}";
    if [ "$target" == "." ];
    then
        printf "%s" "$(pwd)";

    elif [ "$target" == ".." ];
    then
        printf "%s" "$(dirname "$(pwd)")";

    else
        changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
        firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
        firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;

        # If everything fails... TRHOW PYTHON ON IT!!!
        local fullpath;
        local pythoninterpreter;
        local pythonexecutables;
        local pythonlocations;

        pythoninterpreter="python";
        declare -a pythonlocations=("/usr/bin" "/bin");
        declare -a pythonexecutables=("python" "python2" "python3");

        for path in "${pythonlocations[@]}";
        do
            for executable in "${pythonexecutables[@]}";
            do
                fullpath="${path}/${executable}";

                if [[ -f "${fullpath}" ]];
                then
                    # printf "Found ${fullpath}\\n";
                    pythoninterpreter="${fullpath}";
                    break;
                fi;
            done;

            if [[ "${pythoninterpreter}" != "python" ]];
            then
                # printf "Breaking... ${pythoninterpreter}\\n"
                break;
            fi;
        done;

        firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
        # printf "Error: Could not determine the absolute path!\\n";
        return 1;
    fi
}

printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"

This is a chained solution from all others, for example, when realpath fails, either because it is not installed or because it exits with error code, then, the next solution is attempted until it get the path right.

#!/bin/bash

function getabsolutepath() {
    local target;
    local changedir;
    local basedir;
    local firstattempt;

    target="${1}";
    if [ "$target" == "." ];
    then
        printf "%s" "$(pwd)";

    elif [ "$target" == ".." ];
    then
        printf "%s" "$(dirname "$(pwd)")";

    else
        changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
        firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
        firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;

        # If everything fails... TRHOW PYTHON ON IT!!!
        local fullpath;
        local pythoninterpreter;
        local pythonexecutables;
        local pythonlocations;

        pythoninterpreter="python";
        declare -a pythonlocations=("/usr/bin" "/bin");
        declare -a pythonexecutables=("python" "python2" "python3");

        for path in "${pythonlocations[@]}";
        do
            for executable in "${pythonexecutables[@]}";
            do
                fullpath="${path}/${executable}";

                if [[ -f "${fullpath}" ]];
                then
                    # printf "Found ${fullpath}\\n";
                    pythoninterpreter="${fullpath}";
                    break;
                fi;
            done;

            if [[ "${pythoninterpreter}" != "python" ]];
            then
                # printf "Breaking... ${pythoninterpreter}\\n"
                break;
            fi;
        done;

        firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
        # printf "Error: Could not determine the absolute path!\\n";
        return 1;
    fi
}

printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
对岸观火 2024-10-09 07:03:55

基于此答案,作者:@EugenKonkov此答案,作者:@HashChange,我的回答结合了前者的简洁性和后者对 ... 的处理。我相信下面的所有选项都依赖于基本的 Shell 命令语言 POSIX标准

使用dirnamebasename,一个选项是:

absPathDirname()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
    echo "`cd "${1}"; pwd`${2}";
}

如果不使用dirnamebasename,另一个简短的选项是:

absPathMinusD()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
    echo "`cd "${1:-.}"; pwd`${2}";
}

我会推荐上面两个选项之一,其余的只是为了好玩...

Grep 版本:

absPathGrep()
{
    echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}

作为“使用 shell 的有限正则表达式可以做什么”的有趣示例:

absPathShellReplace()
{
    E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
    DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
    echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}

Based on this answer by @EugenKonkov and this answer by @HashChange, my answer combines the brevity of the former with the handling of . and .. of the latter. I believe that all the options below rely upon nothing more than the basic Shell Command Language POSIX standards.

Using dirname and basename, an option is:

absPathDirname()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
    echo "`cd "${1}"; pwd`${2}";
}

Without using dirname or basename, another brief option is:

absPathMinusD()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
    echo "`cd "${1:-.}"; pwd`${2}";
}

I would recommend one of the two options above, the rest are just for fun...

Grep version:

absPathGrep()
{
    echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}

As an interesting example of "what can be done with the shell's limited RegEx":

absPathShellReplace()
{
    E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
    DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
    echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}
套路撩心 2024-10-09 07:03:55

更新之前的答案

to_abs_path() {
  python -c "import os; print (os.path.abspath('$1'))"
}

使用 python 3允许

to_abs_path "./../some_file.txt"

An update on previous answer using python 3

to_abs_path() {
  python -c "import os; print (os.path.abspath('$1'))"
}

Allowing

to_abs_path "./../some_file.txt"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文