如何检索给定相对路径


159

给定相对路径时,是否有命令检索绝对路径?

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

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


1
不是很重复,但类似
mpapis 2010年


比迄今为止列出的任何一种方法更好的解决方案是如何将相对路径转换为unix中的绝对路径
Daniel Genin

Answers:


66

用:

find "$(pwd)"/ -type f

获取所有文件或

echo "$(pwd)/$line"

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


2
如果我在查找中指定相对路径该怎么办?
nubme 2010年

17
然后查看评论中有关您问题的链接,最好的方法可能是“ readlink -f $ line”
mpapis 2010年

3
为什么用$(pwd)代替$PWD?(是的,我知道pwd是内置的)
Adam Katz

178

尝试realpath

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

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

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


11
realpath在Mac上似乎不可用(OS X 10.11“ El Capitan”)。:-(
Laryx Decidua

realpath似乎在CentOS 6上也不可用
user5359531

6
在osx上,brew install coreutils将带来realpath
Kevin Chen

在我的Ubuntu 18.04 realpath上已经存在。我不必单独安装它。
Acumenus

令人惊讶的是,realpath它在Windows的Git上可用(至少对我而言)。
安德鲁·基顿

104

如果您安装了coreutils软件包,通常可以使用readlink -f relative_file_name它来检索绝对的软件包(解决所有符号链接)


3
这样做的行为与用户要求的有所不同,它还将遵循并解析路径中任意位置的递归符号链接。在某些情况下,您可能不希望这样做。
2013年

1
@BradPeabody如果从homebrew安装coreutils,则在Mac中可以使用brew install coreutils。但是,该可执行文件由ag开头:greadlink -f relative_file_name
Miguel Isla

2
请注意,readlink(1)的手册页作为其描述的第一句:“注意,realpath(1)是用于规范化功能的首选命令。”
josch

1
您可以使用-e而不是-f来检查文件/目录是否存在
Iceman

69
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

UPD一些解释

  1. 该脚本获取相对路径作为参数 "$1"
  2. 然后,我们获得该路径的目录名部分(您可以将目录或文件传递给此脚本):dirname "$1"
  3. 然后我们cd "$(dirname "$1")进入这个相对目录并通过运行pwdshell命令获取它的绝对路径
  4. 之后,我们将基本名称附加到绝对路径:$(basename "$1")
  5. 作为最后一步,我们echo

8
此答案使用了最佳的Bash成语
-Rondo

2
readlink是适用于Linux的简单解决方案,但该解决方案也适用于OSX,因此+1
thetoolman

1
此脚本不等同于做什么realpathreadlink -f做什么。例如,它在最后一个组件是符号链接的路径上不起作用。
josch

2
@josch:问题不在于符号链接解析。但是,如果您想这样做,则可以提供命令-P选项pwdecho "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Eugen Konkov,

4
我喜欢答案,但是只有在允许用户将CD插入目录的情况下,它才有效。这可能并不总是可能的。
Matthias B

34

对于它的价值,我投票选择了答案,但想分享一个解决方案。缺点是,它仅适用于Linux-我花了大约5分钟的时间试图找到与OSX相当的产品,然后才出现Stack Overflow。我敢肯定它在那里。

在Linux上,您可以readlink -e与一起使用dirname

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

产量

/etc/

然后使用dirname的姐姐basename来获取文件名

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

产量

passwd

全部放在一起。

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

产量

/etc/passwd

如果您以目录为目标,basename则很安全,将不返回任何内容,并且在最终输出中只会出现双斜杠。


优秀的入门与dirnamereadlinkbasename。这帮助我获得了符号链接的绝对路径,而不是它的目标。
kevinarpe13年

当您想返回符号链接的路径时不起作用(我恰好需要这样做...)。
托马什Zato -恢复莫妮卡

您怎么能找到一条不存在的绝对路径?
synthesizerpatel

@synthesizerpatel我很容易想到;如果我进入/home/GKFX并输入touch newfile,那么在按Enter之前,您可以算出我的意思是“创建/ home / GKFX / newfile”,这是一个尚不存在的文件的绝对路径。
GKFX

25

我认为这是最便携的:

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

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


3
无需再次CD。请参阅stackoverflow.com/a/21188136/1504556。恕我直言,您是此页面上的最佳答案。对于那些感兴趣的人,该链接提供了有关此解决方案为何起作用的解释。
peterh 2014年

这不是很可移植,dirname是GNU核心实用程序,我相信并非所有unixen都通用。
einpoklum

@einpoklum dirname是POSIX标准实用程序,请参见此处:pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
欧内斯特(Ernest)

哦,天哪,谢谢。我一直在尝试修复${1##*/}一天使用的版本,现在我用basename "$1"它替换了该垃圾桶似乎终于可以正确处理了以/结尾的路径。
l3l_aze

注意,这并没有以路径结尾做正确的事../..
Alex Coventry

16

realpath 可能是最好的

但是...

最初的问题从一开始就很困惑,其中一个例子与所述问题关系不大。

所选答案实际上回答了给定的示例,而不是标题中的问题。第一个命令就是那个答案(真的吗?我怀疑),并且不用'/'也可以做到。我看不到第二个命令在做什么。

几个问题是混杂的:

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

  • 可能有多个绝对路径名表示相同的文件(或潜在文件),这主要是由于路径上的符号链接(符号链接),但可能是由于其他原因(设备可能以只读方式安装两次)。一个人可能想要也可能不想明确地解析此类符号链接。

  • 到达非符号链接文件或名称的符号链接链的末尾。这可能会或可能不会产生绝对路径名,这取决于完成方式。并且可能会或可能不想将其解析为绝对路径名。

readlink foo不带选项的命令只有在其参数为实的情况下才能给出答案foo是符号链接且该答案是该符号链接的值。没有其他链接。答案可能是相对的:symlink参数的值是多少。

然而, readlink具有选项(-f -e或-m)适用于所有文件,并为该参数实际表示的文件提供一个绝对路径名(没有符号链接的绝对路径名)。

这对于不是符号链接的任何内容都可以正常工作,尽管可能希望使用绝对路径名而不解析路径上的中间符号链接。这是通过命令完成的realpath -s foo

对于symlink自变量,readlink其选项将再次解析到该自变量绝对路径上的所有符号链接,但这也将包括跟随该参数值可能遇到的所有符号链接。如果您需要一个指向符号symlink本身而不是其链接对象的绝对路径,则可能不希望这样做。同样,如果foo是符号链接,realpath -s foo将获得绝对路径而不解决符号链接,包括作为参数给出的符号链接。

如果不-s选择该选项,realpath则与几乎一样 readlink,除了简单地读取链接的值以及其他几件事。对我来说,readlink目前尚不清楚为什么会有它的选择,显然会给带来不希望的冗余 realpath

探究网络并没有多说什么,只是整个系统之间可能会有一些变化。

结论:realpath至少在这里要求的使用情况下,它是最好的命令,具有最大的灵活性。


10

我最喜欢的解决办法是在一个由@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
}

7

欧根(Eugen)的回答对我而言并不奏效,但这确实可以做到:

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

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


注意:这是脚本。其中$ 1是第一个参数
Eugen Konkov

5

最好的解决方案,恕我直言,是发布在这里的解决方案: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

3

在的情况下find,仅给出搜索的绝对路径可能是最简单的,例如:

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

3

如果您使用的是Mac OS X上的bash既不具有真实路径存在也没有了它的readlink可以打印的绝对路径,你可以有选择,只能自己编码的版本进行打印。这是我的实现:

(纯扑)

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

1

他们说的话,除了find $PWDor(用bash表示)find ~+要方便一些。


1

与@ ernest-a的答案类似,但是在不影响$OLDPWD或定义新功能的情况下,您可以触发子外壳(cd <path>; pwd)

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


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

3
感谢您提供此代码段,它可能会提供一些有限的短期帮助。通过说明为什么这是解决问题的好方法,适当的解释将大大提高其长期价值,对于其他存在类似问题的读者来说,这样做将更为有用。请编辑您的答案以添加一些解释,包括您所做的假设。
Toby Speight '18

1

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

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

这正确处理了路径的最后一个元素为的情况..,在这种情况下"$(pwd)/$(basename "$1")",@ ernest-a的答案将作为as出现accurate_sub_path/spurious_subdirectory/..


0

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

   dir=`cd "$dir"`

“ cd”回显而不更改工作目录,因为在此处在子外壳中执行。


2
在bash-4.3-p46上,这不起作用:当我运行时,shell打印一个空行dir=`cd ".."` && echo $dir
Michael

0

这是所有其他解决方案的链式解决方案,例如,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")" "${?}"

0

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

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

基本的字符串开头替换遵循公式
$ {string /#substring / replacement}
,在此处进行了很好的讨论:https : //www.tldp.org/LDP/abs/html/string-manipulation.html

\字符否定/的时候,我们希望它是我们查找/替换字符串的一部分。


0

我找不到能在Mac OS Catalina,Ubuntu 16和Centos 7之间进行整齐移植的解决方案,因此我决定使用python inline来完成该解决方案,并且对我的bash脚本非常有效。

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

to_abs_path "/some_path/../secrets"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.