【问题标题】:Expand a possible relative path in bash在 bash 中展开可能的相对路径
【发布时间】:2011-10-30 21:26:06
【问题描述】:

作为我脚本的参数,有一些文件路径。当然,这些可以是相对的(或包含〜)。但是对于我编写的函数,我需要绝对路径,但没有解析它们的符号链接。

这个有什么功能吗?

【问题讨论】:

标签: linux bash absolute-path


【解决方案1】:

如果您的操作系统支持,请使用:

realpath "./some/dir"

并在变量中使用它:

some_path="$(realpath "./some/dir")"

这将扩展您的路径。在 Ubuntu 和 CentOS 上测试,您的可能不可用。有些人推荐 readlink,但 readlink 的文档说:

注意 realpath(1) 是用于规范化功能的首选命令。

如果人们想知道我为什么引用我的变量,那是为了在路径中保留空格。就像做realpath some path 会给你两个不同的路径结果。但是realpath "some path" 会返回一个。引用参数ftw :)

【讨论】:

  • 迄今为止最好的答案。其余的要么忽略不遵循符号链接的要求,要么太复杂了。
【解决方案2】:

还有另一种方法。您可以在 bash 脚本中使用 python 嵌入来解析相对路径。

abs_path=$(python3 - <<END
from pathlib import Path
path = str(Path("$1").expanduser().resolve())
print(path)
END
)

【讨论】:

    【解决方案3】:

    http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.html 有以下内容

    function abspath {
        if [[ -d "$1" ]]
        then
            pushd "$1" >/dev/null
            pwd
            popd >/dev/null
        elif [[ -e "$1" ]]
        then
            pushd "$(dirname "$1")" >/dev/null
            echo "$(pwd)/$(basename "$1")"
            popd >/dev/null
        else
            echo "$1" does not exist! >&2
            return 127
        fi
    }
    

    它使用pushd/popd 进入pwd 有用的状态。

    【讨论】:

    • 可悲的是,这确实是这里最好的答案,它没有被投票给它所属的地方。 +1 用于准确回答 OP 的问题。他希望符号链接不被解析,这实际上是 bash 的本地 DIRSTACK 的问题,仅此而已。不错的解决方案!
    • 针对标准sh兼容性时,pushd; cd &lt;dir&gt;; &lt;cmd&gt;; popd可以替换为(cd &lt;dir&gt;; &lt;cmd&gt;)
    【解决方案4】:

    使用readlink -f &lt;relative-path&gt;,例如

    export FULLPATH=`readlink -f ./`
    

    【讨论】:

      【解决方案5】:

      这在 OS X 上对我有用:$(cd SOME_DIRECTORY 2&gt; /dev/null &amp;&amp; pwd -P)

      它应该可以在任何地方工作。其他解决方案似乎太复杂了。

      【讨论】:

        【解决方案6】:

        简单的单行:

        function abs_path {
          (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}")
        }
        

        用法:

        function do_something {
            local file=$(abs_path $1)
            printf "Absolute path to %s: %s\n" "$1" "$file"
        }
        do_something $HOME/path/to/some\ where
        

        我仍在试图弄清楚如何让它完全忘记路径是否存在(因此也可以在创建文件时使用它)。

        【讨论】:

        • 这不会改变当前工作目录吗?之后我应该cd - 重置它吗?
        • 不需要。注意括号,它们会导致里面的命令在子shell中运行。子外壳的状态(例如工作目录)不会泄漏到其父外壳。在这里阅读更多:tldp.org/LDP/abs/html/subshells.html
        • 不错的单线。但是有一个问题:如果 $1 的扩展值没有任何斜杠(即它是当前目录中的文件名),那么 ${1%/*} 会扩展为文件名本身并且 cd 命令失败。您可能想改用 $(dirname $1) ,它将扩展为 '.'在那种情况下。
        • 你是对的,更新了。我在将${1##*/} 替换为basename 时遇到了一些引用问题,但如果不将其放入单独的变量中,带有空格的路径将无法正常工作。
        • @BryanP 实际上,对于我正在维护的 dotfile 同步器,我最终完全过火了。它有一个 clean_path() 函数,可以删除不需要的路径部分,如果你在 $PWD 的末尾粘贴一个相对路径并运行它,它应该工作得很好:github.com/andsens/homeshick/blob/…
        【解决方案7】:

        自我编辑,我刚刚注意到 OP 说他不是在寻找已解决的符号链接:

        “但是对于我编写的函数,我需要绝对路径,但没有解析它们的符号链接。”

        所以猜想这毕竟不是他的问题。 :)

        由于这些年来我遇到过很多次,而这一次我需要一个可以在 OSX 和 linux 上使用的纯 bash 便携版本,所以我继续写了一个:

        活版在这里:

        https://github.com/keen99/shell-functions/tree/master/resolve_path

        但为了 SO,这里是当前版本(我觉得它已经过很好的测试......但我愿意接受反馈!)

        让它适用于普通的 bourne shell (sh) 可能并不难,但我没有尝试......我太喜欢 $FUNCNAME 了。 :)

        #!/bin/bash
        
        resolve_path() {
            #I'm bash only, please!
            # usage:  resolve_path <a file or directory> 
            # follows symlinks and relative paths, returns a full real path
            #
            local owd="$PWD"
            #echo "$FUNCNAME for $1" >&2
            local opath="$1"
            local npath=""
            local obase=$(basename "$opath")
            local odir=$(dirname "$opath")
            if [[ -L "$opath" ]]
            then
            #it's a link.
            #file or directory, we want to cd into it's dir
                cd $odir
            #then extract where the link points.
                npath=$(readlink "$obase")
                #have to -L BEFORE we -f, because -f includes -L :(
                if [[ -L $npath ]]
                 then
                #the link points to another symlink, so go follow that.
                    resolve_path "$npath"
                    #and finish out early, we're done.
                    return $?
                    #done
                elif [[ -f $npath ]]
                #the link points to a file.
                 then
                    #get the dir for the new file
                    nbase=$(basename $npath)
                    npath=$(dirname $npath)
                    cd "$npath"
                    ndir=$(pwd -P)
                    retval=0
                    #done
                elif [[ -d $npath ]]
                 then
                #the link points to a directory.
                    cd "$npath"
                    ndir=$(pwd -P)
                    retval=0
                    #done
                else
                    echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
                    echo "opath [[ $opath ]]" >&2
                    echo "npath [[ $npath ]]" >&2
                    return 1
                fi
            else
                if ! [[ -e "$opath" ]]
                 then
                    echo "$FUNCNAME: $opath: No such file or directory" >&2
                    return 1
                    #and break early
                elif [[ -d "$opath" ]]
                 then 
                    cd "$opath"
                    ndir=$(pwd -P)
                    retval=0
                    #done
                elif [[ -f "$opath" ]]
                 then
                    cd $odir
                    ndir=$(pwd -P)
                    nbase=$(basename "$opath")
                    retval=0
                    #done
                else
                    echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
                    echo "opath [[ $opath ]]" >&2
                    return 1
                fi
            fi
            #now assemble our output
            echo -n "$ndir"
            if [[ "x${nbase:=}" != "x" ]]
             then
                echo "/$nbase"
            else 
                echo
            fi
            #now return to where we were
            cd "$owd"
            return $retval
        }
        

        这是一个经典的例子,感谢 brew:

        %% ls -l `which mvn`
        lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn
        

        使用这个函数,它会返回-real-路径:

        %% cat test.sh
        #!/bin/bash
        . resolve_path.inc
        echo
        echo "relative symlinked path:"
        which mvn
        echo
        echo "and the real path:"
        resolve_path `which mvn`
        
        
        %% test.sh
        
        relative symlinked path:
        /usr/local/bin/mvn
        
        and the real path:
        /usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
        

        【讨论】:

          【解决方案8】:

          也许这更具可读性并且不使用子shell并且不更改当前目录:

          dir_resolve() {
            local dir=`dirname "$1"`
            local file=`basename "$1"`
            pushd "$dir" &>/dev/null || return $? # On error, return error code
            echo "`pwd -P`/$file" # output full, link-resolved path with filename
            popd &> /dev/null
          }
          

          【讨论】:

            【解决方案9】:

            MY_PATH=$(readlink -f $YOUR_ARG) 将解析 "./""../" 等相对路径

            也考虑一下 (source):

            #!/bin/bash
            dir_resolve()
            {
            cd "$1" 2>/dev/null || return $?  # cd to desired directory; if fail, quell any error messages but return exit status
            echo "`pwd -P`" # output full, link-resolved path
            }
            
            # sample usage
            if abs_path="`dir_resolve \"$1\"`"
            then
            echo "$1 resolves to $abs_path"
            echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected
            else
            echo "Could not reach $1"
            fi
            

            【讨论】:

            • 这当然很好,但问题是 simlink 已解决,这会导致用户产生一些混淆。
            • man pwd: "-P 显示当前物理工作目录(所有符号链接都已解析)",只需去掉-P
            • 要在 OS X 上获得readlink -f 功能,您可以使用Homebrew 然后运行:$ brew install coreutils 然后您可以使用greadlink -f
            【解决方案10】:

            您必须专门使用 bash 吗?我需要这样做,并且厌倦了 Linux 和 OS X 之间的差异。所以我使用 PHP 作为一个快速而肮脏的解决方案。

            #!/usr/bin/php <-- or wherever
            <?php
            {
               if($argc!=2)
                  exit();
               $fname=$argv[1];
               if(!file_exists($fname))
                  exit();
               echo realpath($fname)."\n";
            }
            ?>
            

            我知道这不是一个非常优雅的解决方案,但它确实有效。

            【讨论】:

            • 如果您已经知道文件的路径或文件在当前目录中,则此操作只会返回文件的路径...在 PATH 设置中找不到文件
            【解决方案11】:

            在 OS X 上你可以使用

            stat -f "%N" YOUR_PATH
            

            在 linux 上你可能有 realpath 可执行文件。如果没有,以下可能有效(不仅适用于链接):

            readlink -c YOUR_PATH
            

            【讨论】:

            • OS X 答案不起作用。 stat -f "%N" PATH 给了我完全相同的路径。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-01-02
            • 1970-01-01
            • 2014-02-23
            • 2022-06-10
            • 1970-01-01
            • 2016-01-09
            • 1970-01-01
            相关资源
            最近更新 更多