我有一个Bash脚本,需要知道它的完整路径。我试图找到一种广泛兼容的方式来做到这一点,而不会以相对或时髦的路径结束。我只需要支持Bash,不支持sh, csh等。

到目前为止,我发现:

The accepted answer to Getting the source directory of a Bash script from within addresses getting the path of the script via dirname $0, which is fine, but that may return a relative path (like .), which is a problem if you want to change directories in the script and have the path still point to the script's directory. Still, dirname will be part of the puzzle. The accepted answer to Bash script absolute path with OS X (OS X specific, but the answer works regardless) gives a function that will test to see if $0 looks relative and if so will pre-pend $PWD to it. But the result can still have relative bits in it (although overall it's absolute) — for instance, if the script is t in the directory /usr/bin and you're in /usr and you type bin/../bin/t to run it (yes, that's convoluted), you end up with /usr/bin/../bin as the script's directory path. Which works, but... The readlink solution on this page, which looks like this: # Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f $0) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT` But readlink isn't POSIX and apparently the solution relies on GNU's readlink where BSD's won't work for some reason (I don't have access to a BSD-like system to check).

有很多种方法,但都有注意事项。

还有什么更好的办法呢?“更好”的意思是:

Gives me the absolute path. Takes out funky bits even when invoked in a convoluted way (see comment on #2 above). (E.g., at least moderately canonicalizes the path.) Relies only on Bash-isms or things that are almost certain to be on most popular flavors of *nix systems (GNU/Linux, BSD and BSD-like systems like OS X, etc.). Avoids calling external programs if possible (e.g., prefers Bash built-ins). (Updated, thanks for the heads up, wich) It doesn't have to resolve symlinks (in fact, I'd kind of prefer it left them alone, but that's not a requirement).


当前回答

我发现在Bash中获得完整规范路径的最简单方法是使用cd和pwd:

ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"

使用${BASH_SOURCE[0]}而不是$0,无论脚本是作为<name>还是source <name>调用,都会产生相同的行为。

其他回答

我们在GitHub上放置了自己的产品realpath-lib,供社区免费使用。

无耻的插头,但有了这个Bash库,你可以:

get_realpath <absolute|relative|symlink|local file>

这个函数是库的核心:

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

它不需要任何外部依赖,只需要Bash 4+。还包含函数get_dirname, get_filename, get_stemname和validate_path validate_realpath。它是免费的,干净的,简单的,有良好的文档,所以它也可以用于学习目的,毫无疑问,它是可以改进的。尝试跨平台。

更新:经过一些审查和测试,我们已经将上面的函数替换为可以达到相同结果的函数(没有使用dirname,只使用纯Bash),但效率更高:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

这还包括一个环境设置no_symlinks,它提供了将符号链接解析到物理系统的能力。默认情况下,它保持符号链接不变。

易于阅读?下面是一个替代方案。它忽略了符号链接

#!/bin/bash
currentDir=$(
  cd $(dirname "$0")
  pwd
)

echo -n "current "
pwd
echo script $currentDir

自从几年前我发布了上面的答案,我已经发展了我的实践,使用这个linux特定的范例,它正确地处理符号链接:

ORIGIN=$(dirname $(readlink -f $0))

也许下面这个问题的公认答案会有所帮助。

如何在Mac上获得GNU的readlink -f的行为?

假设您只想规范化从连接$PWD和$0得到的名称(假设$0一开始就不是绝对的),那么只需沿abs_dir=${abs_dir//\/一行使用一系列正则表达式替换即可。\//\/}等。

是的,我知道这看起来很糟糕,但它会起作用,而且是纯粹的Bash。

回答这个问题很晚,但我用:

SCRIPT=$( readlink -m $( type -p ${0} ))      # Full path to script handling Symlinks
BASE_DIR=`dirname "${SCRIPT}"`                # Directory script is run in
NAME=`basename "${SCRIPT}"`                   # Actual name of script even if linked

试试这个:

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