如何在Shell脚本中解析符号链接


220

给定绝对路径或相对路径(在类Unix系统中),我想在解决所有中间符号链接后确定目标的完整路径。同时解决〜username表示法的奖励点。

如果目标是目录,则可以将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”。

Answers:


92

根据标准 pwd -P应该用符号链接解析返回路径。

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

getcwd pwd


22
这仅适用于(当前)目录吗?如果目标是文件,则不会给出任何信息……
dala

4
如果链接断开,则无法工作,因为您无法将其设为当前路径
Tom Howard

6
总结一下后见之明:此答案仅在非常有限的情况下有效,即,如果感兴趣的符号链接是指向实际存在目录的;另外,您必须先致电,然后再致电。换句话说:它不允许您解析文件的符号链接(或目标符号链接)或损坏的符号链接,并且要解析现有目录的符号链接,您需要做其他工作(还原先前的工作目录或本地化and 调用在子外壳中)。cdpwd -Pcdpwd -P
mklement0

糟糕的是,我正在寻找一种解决文件而不是目录的方法。
马丁

正如其他人指出的那样,这并不能真正回答问题。@pixelbeat的答案如下。
erstaples

401
readlink -f "$path"

编者注:以上内容适用于GNU readlinkFreeBSD / PC-BSD / OpenBSD readlink,但不适用于OS X(自10.11起)。
GNU readlink提供了其他相关选项,例如-m用于解析符号链接(无论最终目标是否存在)。

请注意,自GNU coreutils 8.15(2012-01-06)起,提供了一个Realpath程序,该程序比上面的程序更钝和更灵活。它也与同名的FreeBSD util兼容。它还包括在两个文件之间生成相对路径的功能。

realpath $path

[下面的管理员补充来自halloleo的评论— danorton]

对于Mac OS X(至少为10.11.x),请使用readlink不带以下-f选项的产品:

readlink $path

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


5
这在Mac OS X中不起作用-参见stackoverflow.com/questions/1055671/…–
Bkkbrad,

1
关于OS X不兼容的耻辱,否则不错+1
jkp

11
在OS X上,您可以安装带有自制程序的coreutils。它将其安装为“ grealpath”。
基夫(Kief)2012年

10
readlink工作在OSX,但需要另一种语法:readlink $path 没有-f
halloleo

2
readlink虽然无法取消引用多层符号链接,但一次只能取消引用一层
Magnus

26

如果只需要目录,“ 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

17

我的最爱之一是 realpath foo

realpath-返回规范化的绝对​​路径名

realpath扩展所有符号链接,并解析由path和
       将规范化的绝对​​路径名存储在由resolve_path命名的PATH_MAX大小的缓冲区中。结果路径将没有符号链接“ /./”或
       '/../' 组件。

在Debian(蚀刻及更高版本)上,此命令在realpath软件包中可用。
Phil Ross

2
realpath现在(2012年1月)是coreutils的一部分,并与debian和BSD变体向后兼容
pixelbeat

1
我没有realpath在Centos 6上使用GNU coreutils 8.4.31。我在Unix和Linux上遇到了其他一些打包有GNU coreutils且没有的 软件包realpath。因此,它似乎不仅依赖于版本。
toxalot

我更喜欢realpathreadlink因为它提供了诸如--relative-to
-arr_sea

10
readlink -e [filepath]

似乎恰恰是您要的-它接受一个原始路径,解析所有符号链接,并返回“真实”路径-并且“标准* nix”可能已在所有系统中使用


只是被它咬了。它在Mac上不起作用,我正在寻找替换产品。
dhill

5

其他方式:

# 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将替换路径。
Keymon '17

5

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

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

3

这是使用嵌入式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'))")

3

您的路径是目录还是文件?如果是目录,则很简单:

(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,通过我首先描述的简单技巧将其解析为实际位置。


2
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)不适用))无关,则将中断。
mklement0 2015年

如果您担心空格,请使用引号。
戴夫

请这样做-尽管不会解决(b)。
mklement0

请给我举一个b)失败的例子。根据定义,断开的符号链接指向一个不存在的目录条目。该脚本的目的是解决另一个方向上的符号链接。如果符号链接中断,则不会执行脚本。本示例旨在演示如何解析当前正在执行的脚本。
戴夫

“旨在演示如何解决当前正在执行的脚本”-实际上,这是您选择要重点关注的问题的范围的缩小;只要您这么说就可以了。既然您没有,我在评论中表示了这一点。请解决报价问题,无论答案范围如何,这都是一个问题。
mklement0

2

注意:我认为这是一个可靠,可移植的现成解决方案,因此这总是很长

以下是完全兼容POSIX的脚本/函数,因此是跨平台的(也可在macOS上运行,从10.12(Sierra)开始readlink仍不支持-f)-它仅使用POSIX Shell语言功能,并且仅使用POSIX兼容的实用程序调用。

它是GNUreadlink -e(的更严格版本readlink -f)的可移植实现

您可以运行该脚本sh功能bashkshzsh

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

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

rreadlink 脚本/函数定义:

对此答案表示感谢。
我还创建了一个bash基于独立的实用程序的版本在这里,您可以与安装
npm install rreadlink -g,如果安装了Node.js的。

#!/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函数遮盖的函数,在注释中要求:

如果将unaliasor或unsetand [设置为别名或shell函数怎么办?

rreadlink确保command具有其原始含义的动机是使用它绕过(良性)便捷别名和函数,这些别名和函数通常用于遮盖交互式外壳程序中的标准命令,例如重新定义ls以包括喜欢的选项。

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

函数必须某种东西才能具有其原始含义和行为-无法解决。
类似于POSIX的shell允许重新定义内建函数,甚至语言关键字也固有地存在安全风险(并且编写偏执代码通常很困难)。

具体解决您的问题:

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

然而,引用是不是壳的选项关键字whileforifdo,...),并同时外壳的关键字做优先于外壳的功能,在bashzsh别名具有最高的优先级,所以对你必须运行壳关键字重定义后卫unalias与它们的名称(尽管在非交互式 bash外壳(例如脚本)中)是别名默认情况下扩展-仅在shopt -s expand_aliases首先显式调用时才使用)。

为了确保unalias-作为内置函数-具有其原始含义,您必须使用\unset首先在其上,这需要unset具有其原始含义:

unset内置的shell,因此要确保以这种方式调用它,必须确保它本身没有被重新定义为function。虽然可以用引号绕过别名表单,但不能绕过Shell函数形式-catch 22。

因此,据unset我所知,除非您可以依靠它具有其原始含义,否则无法保证防御所有恶意重新定义的方法。


以我的经验,引号会绕过别名,而不是shell函数,这与您先说的不同。
jarno

我测试了我可以定义[为在一个别名dashbash并作为一个壳的功能bash
jarno

1
我的观点是一个单独的问题
jarno

@jarno:好点;我已经更新了答案;如果您认为仍然存在问题,请告诉我。
mklement0 '16

1
@jarno:您可以whilebashksh和中定义一个函数zsh(但不能定义dash),但只能使用function <name>语法:function while { echo foo; }有效(while() { echo foo; }无效)。但是,这不会给while 关键字造成阴影,因为关键字的优先级高于函数(调用此函数的唯一方法是\while)。在bash和中zsh,别名比关键字具有更高的优先级,因此关键字的别名重新定义确实会掩盖它们(但bash默认情况下仅在交互式 shell中使用,除非shopt -s expand_aliases明确调用)。
mklement0 '16

1

即使将它们作为符号链接调用,常见的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`

1

试试这个:

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

1

多年来,由于我已经经历了很多次,并且这次我需要一个可以在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。
戴夫

1

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

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

不太好,但跨操作系统


3
与php相比,Python 2.6+在更多最终用户系统上可用,因此python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"可能更有意义。尽管如此,当您需要从Shell脚本中使用Python时,您可能应该只使用Python并避免跨平台Shell脚本的麻烦。
乔纳森·鲍德温

除了sh内置的readlink,dirname和basename,您只需要其他内容。
戴夫

@戴夫:dirnamebasename,和readlink是外部实用工具,而不是外壳内置插件; dirname并且basename是POSIX的一部分,readlink不是。
mklement0,2015年

@ mklement0-你说得对。它们由CoreUtils或同等功能提供。我不应该在凌晨1点以后去。我的评论的实质是,不需要PHP或基本系统中安装的其他语言解释器。自1997年以来,我就在每个 Linux变体上使用了此页面上提供的脚本,自2006年以来,对MacOS X 使用了此页面上的脚本,没有出现错误。OP并没有要求POSIX解决方案。他们的特定环境是Mac OSX。–
Dave

@戴夫:是的,这可能的股票工具来做到这一点,但它也很难这样做的(由你的脚本的缺点就是证明)。如果OS X是真正的焦点,那么给出的答案非常好的-而且要简单得多-鉴于phpOS X附带了该问题。但是,即使问题的正文提到了OS X,也未对其进行标记,并且很明显各种平台上的人都来这里寻求答案,因此值得指出什么是特定于平台的/非POSIX。
mklement0

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##*/}"
)}

请注意,所有cdand set东西都发生在子shell中。


实际上,()周围的{}是不必要的,因为()与{}一样被视为“复合命令”。但是您仍然需要在函数名称之后加上()。
克里斯·科格登

如果函数名称后面没有@@ ChrisCogdon {}()则不必要()。Bash接受函数声明的()原因是,因为Shell在声明中没有参数列表,并且不执行调用,()因此,函数声明()的意义不大。
solidsnack

0

在这里,我提出了我认为是跨平台(至少是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
}

0

我在这里的答案Bash:如何获得符号链接的真实路径?

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

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

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

为了测试所有内容,我们将symlink放在/ 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

0

如果无法使用pwd(例如,从其他位置调用脚本),请使用realpath(带或不带目录名):

$(dirname $(realpath $PATH_TO_BE_RESOLVED))

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

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.