【问题标题】:How to resolve symbolic links in a shell script如何解析 shell 脚本中的符号链接
【发布时间】:2010-09-05 16:18:19
【问题描述】:

给定一个绝对或相对路径(在类 Unix 系统中),我想在解析任何中间符号链接后确定目标的完整路径。同时解决〜用户名符号的奖励积分。

如果目标是一个目录,则可以将 chdir() 放入该目录,然后调用 getcwd(),但我真的想通过 shell 脚本来执行此操作,而不是编写 C 帮助程序。不幸的是,shell 倾向于尝试向用户隐藏符号链接的存在(这是 OS X 上的 bash):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

我想要的是一个函数 resolve(),这样当从上面示例中的 tmp 目录执行时,resolve("foo") == "/Users/greg/tmp/bar"。

【问题讨论】:

    标签: bash shell scripting symlink


    【解决方案1】:

    这是 Bash 中的符号链接解析器,无论链接是目录还是非目录,它都可以工作:

    function readlinks {(
      set -o errexit -o nounset
      declare n=0 limit=1024 link="$1"
    
      # If it's a directory, just skip all this.
      if cd "$link" 2>/dev/null
      then
        pwd -P
        return 0
      fi
    
      # Resolve until we are out of links (or recurse too deep).
      while [[ -L $link ]] && [[ $n -lt $limit ]]
      do
        cd "$(dirname -- "$link")"
        n=$((n + 1))
        link="$(readlink -- "${link##*/}")"
      done
      cd "$(dirname -- "$link")"
    
      if [[ $n -ge $limit ]]
      then
        echo "Recursion limit ($limit) exceeded." >&2
        return 2
      fi
    
      printf '%s/%s\n' "$(pwd -P)" "${link##*/}"
    )}
    

    请注意,所有 cdset 内容都发生在子 shell 中。

    【讨论】:

    • 实际上,( ) 周围的 { } 是不必要的,因为 ( ) 与 { } 一样算作“复合命令”。但是函数名后面还是需要()。
    • @ChrisCogdon 如果函数名后面没有(),则() 周围的{} 是不必要的。 Bash 接受不带 () 的函数声明,因为 shell 在声明中没有参数列表,也不使用 () 进行调用,所以带有 () 的函数声明没有多大意义。
    • 如果符号链接中有路径,这将中断,例如,../dir1/dir2/file
    • 你能举个例子来说明它坏掉的设置吗?它最终会打印出物理路径(pwd -P),所以我不确定你的意思是损坏的;它并不总是打印路径的最短方法。
    【解决方案2】:

    如果 pwd 无法使用(例如,从不同位置调用脚本),请使用 realpath(带或不带 dirname):

    $(dirname $(realpath $PATH_TO_BE_RESOLVED))
    

    通过(多个)符号链接调用或直接调用脚本时都有效 - 从任何位置。

    【讨论】:

      【解决方案3】:

      我的回答在这里Bash: how to get real path of a symlink?

      但简而言之,在脚本中非常方便:

      script_home=$( dirname $(realpath "$0") )
      echo Original script home: $script_home
      

      这些是 GNU coreutils 的一部分,适用于 Linux 系统。

      为了测试所有内容,我们将符号链接放入 /home/test2/,修改一些附加内容并从根目录运行/调用它:

      /$ /home/test2/symlink
      /home/test
      Original script home: /home/test
      

      在哪里

      Original script is: /home/test/realscript.sh
      Called script is: /home/test2/symlink
      

      【讨论】:

        【解决方案4】:

        在这里,我提出了一个我认为是跨平台(至少是 Linux 和 macOS)的解决方案,以解决目前对我来说效果很好的答案。

        crosspath()
        {
            local ref="$1"
            if [ -x "$(which realpath)" ]; then
                path="$(realpath "$ref")"
            else
                path="$(readlink -f "$ref" 2> /dev/null)"
                if [ $? -gt 0 ]; then
                    if [ -x "$(which readlink)" ]; then
                        if [ ! -z "$(readlink "$ref")" ]; then
                            ref="$(readlink "$ref")"
                        fi
                    else
                        echo "realpath and readlink not available. The following may not be the final path." 1>&2
                    fi
                    if [ -d "$ref" ]; then
                        path="$(cd "$ref"; pwd -P)"
                    else
                        path="$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
                    fi
                fi
            fi
            echo "$path"
        }
        

        这是一个 macOS(仅?)解决方案。可能更适合原始问题。

        mac_realpath()
        {
            local ref="$1"
            if [[ ! -z "$(readlink "$ref")" ]]; then
                ref="$(readlink "$1")"
            fi
            if [[ -d "$ref" ]]; then
                echo "$(cd "$ref"; pwd -P)"
            else
                echo "$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
            fi
        }
        

        【讨论】:

          【解决方案5】:

          您的路径是目录还是文件?如果是目录,那就简单了:

          (cd "$DIR"; pwd -P)
          

          但是,如果它可能是一个文件,那么这将不起作用:

          DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"
          

          因为符号链接可能会解析为相对路径或完整路径。

          在脚本上我需要找到真正的路径,这样我就可以引用配置或与之一起安装的其他脚本,我使用这个:

          SOURCE="${BASH_SOURCE[0]}"
          while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
            DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
            SOURCE="$(readlink "$SOURCE")"
            [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
          done
          

          您可以将SOURCE 设置为任何文件路径。基本上,只要路径是符号链接,它就会解析该符号链接。诀窍在于循环的最后一行。如果解析的符号链接是绝对的,它将使用 SOURCE。但是,如果它是相对的,它将为其添加 DIR,通过我第一次描述的简单技巧将其解析为真实位置。

          【讨论】:

            【解决方案6】:

            以下是如何使用内联 Perl 脚本在 MacOS/Unix 中获取文件的实际路径:

            FILE=$(perl -e "use Cwd qw(abs_path); print abs_path('$0')")
            

            同样,获取符号链接文件的目录:

            DIR=$(perl -e "use Cwd qw(abs_path); use File::Basename; print dirname(abs_path('$0'))")
            

            【讨论】:

              【解决方案7】:

              注意:我相信这是一个可靠、便携、现成的解决方案,正因为如此,它总是冗长

              下面是一个完全符合 POSIX 的脚本/函数,因此是跨平台(也适用于 macOS,其 readlink 仍然不支持 @987654327 @ as of 10.12 (Sierra)) - 它仅使用 POSIX shell language features 并且仅使用符合 POSIX 的实用程序调用。

              它是 GNU 的 readlink -ereadlink -f 的更严格版本)的可移植实现。

              您可以使用sh运行脚本,或bashksh获取函数,以及zsh

              例如,在脚本中,您可以按如下方式使用它来获取运行脚本的真实原始目录,并解析符号链接:

              trueScriptDir=$(dirname -- "$(rreadlink "$0")")
              

              rreadlink脚本/函数定义:

              代码改编自 this answer 的感谢。
              我还创建了一个基于bash 的独立实用程序版本here,如果您安装了Node.js,您可以使用
              npm install rreadlink -g 安装它。

              #!/bin/sh
              
              # SYNOPSIS
              #   rreadlink <fileOrDirPath>
              # DESCRIPTION
              #   Resolves <fileOrDirPath> to its ultimate target, if it is a symlink, and
              #   prints its canonical path. If it is not a symlink, its own canonical path
              #   is printed.
              #   A broken symlink causes an error that reports the non-existent target.
              # LIMITATIONS
              #   - Won't work with filenames with embedded newlines or filenames containing 
              #     the string ' -> '.
              # COMPATIBILITY
              #   This is a fully POSIX-compliant implementation of what GNU readlink's
              #    -e option does.
              # EXAMPLE
              #   In a shell script, use the following to get that script's true directory of origin:
              #     trueScriptDir=$(dirname -- "$(rreadlink "$0")")
              rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.
              
                target=$1 fname= targetDir= CDPATH=
              
                # Try to make the execution environment as predictable as possible:
                # All commands below are invoked via `command`, so we must make sure that
                # `command` itself is not redefined as an alias or shell function.
                # (Note that command is too inconsistent across shells, so we don't use it.)
                # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not 
                # even have an external utility version of it (e.g, Ubuntu).
                # `command` bypasses aliases and shell functions and also finds builtins 
                # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for
                # that to happen.
                { \unalias command; \unset -f command; } >/dev/null 2>&1
                [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
              
                while :; do # Resolve potential symlinks until the ultimate target is found.
                    [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
                    command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
                    fname=$(command basename -- "$target") # Extract filename.
                    [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
                    if [ -L "$fname" ]; then
                      # Extract [next] target path, which may be defined
                      # *relative* to the symlink's own directory.
                      # Note: We parse `ls -l` output to find the symlink target
                      #       which is the only POSIX-compliant, albeit somewhat fragile, way.
                      target=$(command ls -l "$fname")
                      target=${target#* -> }
                      continue # Resolve [next] symlink target.
                    fi
                    break # Ultimate target reached.
                done
                targetDir=$(command pwd -P) # Get canonical dir. path
                # Output the ultimate target's canonical path.
                # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
                if [ "$fname" = '.' ]; then
                  command printf '%s\n' "${targetDir%/}"
                elif  [ "$fname" = '..' ]; then
                  # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
                  # AFTER canonicalization.
                  command printf '%s\n' "$(command dirname -- "${targetDir}")"
                else
                  command printf '%s\n' "${targetDir%/}/$fname"
                fi
              )
              
              rreadlink "$@"
              

              与安全相关:

              jarno,关于确保内置 command 不会被同名的别名或 shell 函数遮蔽的函数,在评论中询问:

              如果将unaliasunset[ 设置为别名或shell 函数会怎样?

              rreadlink 确保command 具有其本意的动机是使用它绕过(良性)方便 别名和函数,这些别名和函数通常用于隐藏交互式 shell 中的标准命令,例如重新定义 ls 以包含最喜欢的选项。

              我认为可以肯定地说,除非您正在处理不受信任的恶意环境,否则担心unaliasunset - 或者就此而言,whiledo、... -被重新定义不是问题。

              函数必须依赖于某些东西才能具有其原始含义和行为 - 没有办法解决这个问题。
              类似 POSIX 的 shell 允许重新定义内置函数甚至语言关键字本质上是一种安全风险(而且编写偏执的代码通常很困难)。

              具体解决您的问题:

              该函数依赖于 unaliasunset 具有它们的原始含义。以改变其行为的方式将它们重新定义为 shell 函数 将是一个问题;重新定义为 alias 是 不必担心,因为引用(部分)命令名称(例如,\unalias)会绕过别名。

              但是,引用 不是 shell 关键字whileforifdo、...)的选项,而shell 关键字确实优先于 shell functions,在 bashzsh 别名具有最高优先级,因此为了防止 shell 关键字重新定义,您必须使用它们的名称运行 unalias(尽管在非交互式bash shell(例如脚本)别名默认扩展 - 仅当shopt -s expand_aliases 被显式调用时)。

              为了保证unalias——作为一个内置函数——有它的原意,你必须先在它上面使用\unset,这就要求unset有它的原意:

              unset 是一个shell builtin,所以要确保它被这样调用,你必须确保它本身没有被重新定义为一个函数。虽然您可以通过引用绕过别名表单,但不能绕过 shell 函数表单 - catch 22。

              因此,除非您可以依靠 unset 来获得其本来的含义,否则据我所知,没有保证可以防御所有恶意重新定义。

              【讨论】:

              • 根据我的经验,引用绕过别名,而不是 shell 函数,不像你第一次说的那样。
              • 我测试了我可以将[定义为dashbash中的别名以及bash中的shell函数。
              • 我的观点是单独的question
              • @jarno:好点;我已经更新了我的答案;如果您认为还有问题,请告诉我。
              • @jarno:您可以while定义为bashkshzsh中的一个函数(但不是dash),但只能使用function &lt;name&gt; 语法:function while { echo foo; } 有效(while() { echo foo; } 无效)。但是,这不会影响while 关键字,因为关键字的优先级高于函数(调用此函数的唯一方法是\while)。在bashzsh 中,别名的优先级高于关键字,因此alias 关键字的重新定义会影响它们(但在bash 中,默认情况下仅在interactive shell 中, 除非shopt -s expand_aliases 被显式调用)。
              【解决方案8】:

              将一些给定的解决方案放在一起,知道 readlink 在大多数系统上都可用,但需要不同的参数,这对我在 OSX 和 Debian 上很有效。我不确定 BSD 系统。也许条件需要是 [[ $OSTYPE != darwin* ]] 才能仅从 OSX 中排除 -f

              #!/bin/bash
              MY_DIR=$( cd $(dirname $(readlink `[[ $OSTYPE == linux* ]] && echo "-f"` $0)) ; pwd -P)
              echo "$MY_DIR"
              

              【讨论】:

                【解决方案9】:

                "pwd -P" 如果您只想要目录,似乎可以工作,但如果出于某种原因您想要实际可执行文件的名称,我认为这没有帮助。这是我的解决方案:

                #!/bin/bash
                
                # get the absolute path of the executable
                SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")
                
                # resolve symlinks
                while [[ -h $SELF_PATH ]]; do
                    # 1) cd to directory of the symlink
                    # 2) cd to the directory of where the symlink points
                    # 3) get the pwd
                    # 4) append the basename
                    DIR=$(dirname -- "$SELF_PATH")
                    SYM=$(readlink "$SELF_PATH")
                    SELF_PATH=$(cd "$DIR" && cd "$(dirname -- "$SYM")" && pwd)/$(basename -- "$SYM")
                done
                

                【讨论】:

                  【解决方案10】:
                  readlink -f "$path"
                  

                  编者注:以上内容适用于 GNU readlinkFreeBSD/PC-BSD/OpenBSD readlink,但 em> 从 10.11 开始在 OS X 上运行。
                  GNU readlink 提供额外的相关选项,例如 -m 用于解析符号链接,无论最终目标是否存在。

                  请注意,自 GNU coreutils 8.15 (2012-01-06) 以来,有一个可用的 realpath 程序,它比上述程序不那么钝且更灵活。它还与同名的 FreeBSD 实用程序兼容。它还包括在两个文件之间生成相对路径的功能。

                  realpath $path
                  

                  [halloleodanorton] danorton] 下面的评论添加管理员

                  对于 Mac OS X(至少 10.11.x),使用 readlink 而不使用 -f 选项:

                  readlink $path
                  

                  编者注:这不会递归地解析符号链接,因此不会报告ultimate目标;例如,给定符号链接a 指向b,而后者又指向c,这只会报告b(并且不会确保它作为绝对路径输出)。
                  在 OS X 上使用以下perl 命令来填补缺少的readlink -f 功能的空白:
                  perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"

                  【讨论】:

                  • 这在 Mac OS X 中不起作用 - 请参阅 stackoverflow.com/questions/1055671/…
                  • 对 OS X 不兼容感到羞耻,否则很好 +1
                  • 在 OS X 上,您可以使用 homebrew 安装 coreutils。它将其安装为“grealpath”。
                  • readlink 适用于 OSX,但需要另一种语法:readlink $path 没有 -f
                  • readlink 无法取消引用多层符号链接,它只是一次取消引用一层
                  【解决方案11】:

                  为了解决 Mac 不兼容的问题,我想出了

                  echo `php -r "echo realpath('foo');"`
                  

                  不太好,但跨操作系统

                  【讨论】:

                  • Python 2.6+ 可用于比 php 更多的最终用户系统,因此python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));" 可能更有意义。尽管如此,当您需要从 shell 脚本中使用 Python 时,您可能应该只使用 Python 并免去跨平台 shell 脚本的麻烦。
                  • 你只需要 sh 内置的 readlink、dirname 和 basename。
                  • @Dave: dirnamebasenamereadlink 是外部 实用程序,而不是 shell 内置; dirnamebasename 是 POSIX 的一部分,readlink 不是。
                  • @mklement0 - 你说的很对。它们由 CoreUtils 或同等产品提供。我不应该在凌晨 1 点之后访问 SO。我的评论的本质是除了安装在基本系统中的之外,不需要 PHP 或任何其他语言解释器。自 1997 年以来,我在 每个 linux 变体和自 2006 年以来的 MacOS X 上使用了我在此页面上提供的脚本,没有出现错误。 OP 没有要求 POSIX 解决方案。他们的特定环境是 Mac OS X。
                  • @Dave:是的,使用股票实用程序可能做到这一点,但这样做也困难(如你的脚本的缺点)。如果 OS X 真的是焦点,那么这个答案 非常好 - 而且更简单 - 考虑到 OS X 附带 php。然而,即使 body 的问题提到了 OS X,它没有这样标记,很明显,各种平台上的人都来这里寻求答案,所以值得指出什么是平台特定/非 POSIX。
                  【解决方案12】:

                  根据标准,pwd -P 应返回已解析符号链接的路径。

                  来自unistd.h 的C 函数char *getcwd(char *buf, size_t size) 应该具有相同的行为。

                  getcwd pwd

                  【讨论】:

                  • 这仅适用于(当前)目录?如果目标是一个文件,它不会给出任何东西......
                  • 如果链接断开则无法使用,因为您无法将其设为当前路径
                  • 事后总结:此答案仅在非常有限的情况下有效,即如果感兴趣的符号链接指向实际存在目录我>;另外,您必须先cd,然后再致电pwd -P。换句话说:它不允许您解析(查看目标)符号链接到文件损坏符号链接,并且为了解析现有目录符号链接,您必须做额外的工作(恢复以前的工作目录或在子shell中本地化cdpwd -P调用)。
                  • 糟糕,我正在寻找一种方法来解决文件而不是目录。
                  • 正如其他人所指出的,这并不能真正回答问题。 @pixelbeat 的回答如下。
                  【解决方案13】:
                  readlink -e [filepath]
                  

                  似乎正是您所要求的 - 它接受任意路径,解析所有符号链接,并返回“真实”路径 - 它是可能所有系统都已经拥有的“标准 *nix”

                  【讨论】:

                  • 刚被它咬了。它不适用于 Mac,我正在寻找替代品。
                  【解决方案14】:

                  由于这些年来我遇到过很多次,而这一次我需要一个可以在 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 
                  

                  【讨论】:

                  • 我之前的回答在大约 1/4 的空间中做了完全相同的事情 :) 这是非常基本的 shell 脚本,不值得一个 git repo。
                  【解决方案15】:

                  试试这个:

                  cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
                  

                  【讨论】:

                    【解决方案16】:
                    function realpath {
                        local r=$1; local t=$(readlink $r)
                        while [ $t ]; do
                            r=$(cd $(dirname $r) && cd $(dirname $t) && pwd -P)/$(basename $t)
                            t=$(readlink $r)
                        done
                        echo $r
                    }
                    
                    #example usage
                    SCRIPT_PARENT_DIR=$(dirname $(realpath "$0"))/..
                    

                    【讨论】:

                    • 这将中断 (a) 任何包含空格或 shell 元字符的路径和 (b) 损坏的符号链接(如果您只想要正在运行的脚本的父路径,这不是问题,假设 (a) 不申请)。
                    • 如果您担心空格,请使用引号。
                    • 请做 - 虽然这不会解决 (b)。
                    • 请给我看一个 b) 失败的例子。根据定义,损坏的符号链接指向不存在的目录条目。该脚本的重点是解析另一个方向的符号链接。如果符号链接被破坏,您将不会执行脚本。此示例旨在演示如何解析当前正在执行的脚本。
                    • “旨在演示解决当前正在执行的脚本” - 实际上,这是您选择关注的问题范围的缩小;这很好,只要你这么说。既然你没有,我在评论中说明了这一点。请修复引用问题,这是一个与答案范围无关的问题。
                    【解决方案17】:

                    另一种方式:

                    # Gets the real path of a link, following all links
                    myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
                    
                    # Returns the absolute path to a command, maybe in $PATH (which) or not. If not found, returns the same
                    whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 
                    
                    # Returns the realpath of a called command.
                    whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 
                    

                    【讨论】:

                    • 我需要在myreadlink()中cd,因为它是一个递归函数,去每个目录直到找到一个链接。如果找到链接,则返回真实路径,然后 sed 将替换路径。
                    【解决方案18】:

                    我最喜欢的一个是realpath foo

                    realpath - 返回规范化的绝对​​路径名 realpath 扩展所有符号链接并解析对 '/./'、'/../' 和额外的 '/' 字符的引用,这些字符在由 path 命名的空终止字符串中和 将规范化的绝对​​路径名存储在由resolved_pa​​th 命名的大小为PATH_MAX 的缓冲区中。生成的路径将没有符号链接,“/./”或 '/../' 成分。

                    【讨论】:

                    • 在 Debian(etch 和更高版本)上,此命令在 realpath 包中可用。
                    • realpath 现在(2012 年 1 月)是 coreutils 的一部分,并且向后兼容 debian 和 BSD 变体
                    • 我在带有 GNU coreutils 8.4.31 的 Centos 6 上没有 realpath。我在Unix & Linux 上遇到过其他几个有一个GNU coreutils 打包没有 realpath。所以它似乎不仅仅依赖于版本。
                    • 我更喜欢 realpath 而不是 readlink,因为它提供了选项标志,例如 --relative-to
                    【解决方案19】:

                    即使它们被作为符号链接调用,公共 shell 脚本通常也必须找到它们的“主”目录。因此,脚本必须从 $0 中找到它们的“真实”位置。

                    cat `mvn`
                    

                    在我的系统上打印一个包含以下内容的脚本,这应该可以很好地提示您需要什么。

                    if [ -z "$M2_HOME" ] ; then
                      ## resolve links - $0 may be a link to maven's home
                      PRG="$0"
                    
                      # need this for relative symlinks
                      while [ -h "$PRG" ] ; do
                        ls=`ls -ld "$PRG"`
                        link=`expr "$ls" : '.*-> \(.*\)$'`
                        if expr "$link" : '/.*' > /dev/null; then
                          PRG="$link"
                        else
                          PRG="`dirname "$PRG"`/$link"
                        fi
                      done
                    
                      saveddir=`pwd`
                    
                      M2_HOME=`dirname "$PRG"`/..
                    
                      # make it fully qualified
                      M2_HOME=`cd "$M2_HOME" && pwd`
                    

                    【讨论】:

                      猜你喜欢
                      • 2013-11-15
                      • 2016-09-23
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-01-09
                      • 1970-01-01
                      • 2013-01-08
                      相关资源
                      最近更新 更多