一个bash脚本可靠的方式来获得自己的完整path?

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

我到目前为止发现的:

  1. “ 从内部获取Bash脚本的源目录 ”地址的公认答案通过dirname $0获取脚本的path,这很好,但是可能会返回一个相对path(如. ),这是一个问题,如果你想更改脚本中的目录并使path仍指向脚本的目录。 不过, dirname将成为这个难题的一部分。

  2. 被接受的答案是“ OS X的 绝对pathBash脚本 ” (OS X的具体,但答案无论如何)给出了一个函数,将testing,看看$0看起来相对,如果是的话,将预先$PWD 。 但是结果仍然可能有相对的位(尽pipe总体上它是绝对的) – 例如,如果脚本在目录/usr/bin并且在/usr并且键入bin/../bin/t运行它(是的,这是错综复杂的),你最终以/usr/bin/../bin作为脚本的目录path。 哪个有效 ,但…

  3. 这个页面上的readlink解决scheme,看起来像这样:

     # 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` 

    但是readlink不是POSIX,显然这个解决scheme依赖于GNU的readlink ,因为BSD不能用于某些原因(我没有访问类似BSD的系统来检查)。

所以,各种各样的做法,但他们都有他们的警告。

什么是更好的方法? “更好”是指:

  • 给我绝对path。
  • 即使以错综复杂的方式调用,也会带出时髦的位(参见上面的#2的评论)。 (例如,至less适度规范化path。)
  • 仅仅依赖于bash-isms或几乎可以肯定是在* nix系统(GNU / Linux,BSD和类似BSD的系统,如OS X等)的最stream行的版本的东西。
  • 如果可能的话避免调用外部程序(例如,喜欢bash内置插件)。
  • 更新 ,谢谢你的抬头, 至极 )不必解决符号链接(事实上,我宁愿让它们独自一人,但这不是要求)。

下面是我想到的(编辑:加上sfstewman , levigroker , Kyle Strand和Rob Kennedy提供的一些调整),似乎大多数符合我的“更好”的标准:

 SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 

SCRIPTPATH行似乎特别迂回,但我们需要它而不是SCRIPTPATH=`pwd`为了正确处理空格和符号链接。

还要注意的是,深奥的情况下,如执行非可访问的文件系统中的文件(这是完全可能的)执行一个脚本,是不是迎合那里(或在我见过的任何其他答案)。

我很惊讶realpath命令在这里没有提到。 我的理解是,它是广泛的便携/移植。

你的初始解决scheme变成

 SCRIPT=`realpath $0` SCRIPTPATH=`dirname $SCRIPT` 

为了让您的喜好保持符号链接没有解决:

 SCRIPT=`realpath -s $0` SCRIPTPATH=`dirname $SCRIPT` 

我发现在bash中获得完整规范path的最简单方法是使用cdpwd

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

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

我只是今天重新访问这个问题,发现https://stackoverflow.com/a/246128/1034080 。 它详细阐述了我过去使用的解决scheme。

 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 

在链接的答案中有更多的变体,例如脚本本身是符号链接的情况。

获取shell脚本的绝对path

在readlink中不使用-f选项,因此应该在bsd / mac-osx中工作

支持

  • 源./script(由.点运算符调用时)
  • 绝对path/path/到/脚本
  • 相对path如./script
  • /path/dir1/../dir2/dir3/../script
  • 从符号链接调用时
  • 当符号链接嵌套时,例如foo->dir1/dir2/bar bar->./../doe doe->script
  • 调用者更改脚本名称时

我正在寻找这个代码不起作用的angular落案件 。 请告诉我。

 pushd . > /dev/null SCRIPT_PATH="${BASH_SOURCE[0]}"; while([ -h "${SCRIPT_PATH}" ]); do cd "`dirname "${SCRIPT_PATH}"`" SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")"; done cd "`dirname "${SCRIPT_PATH}"`" > /dev/null SCRIPT_PATH="`pwd`"; popd > /dev/null echo "srcipt=[${SCRIPT_PATH}]" echo "pwd =[`pwd`]" 

已知的问题

脚本必须放在磁盘上 ,让它通过networking。 如果您尝试从PIPE运行此脚本,则不起作用

 wget -o /dev/null -O - http://host.domain/dir/script.sh |bash 

从技术上讲,这是不明确的。
实际上,没有任何理智的方法来检测这一点。 (协同进程无法访问父级的环境)

怎么样使用:

 SCRIPT_PATH=$(dirname `which $0`) 

打印到标准输出可执行文件的完整path,当在shell提示符下input参数时(这是$ 0包含的)

dirname从文件名dirname非目录后缀

因此,无论path是否被指定,最终的path都是脚本的完整path。

因为我的Linux系统上没有安装默认实用path,所以下面的代码适用于我:

 SCRIPT="$(readlink --canonicalize-existing "$0")" SCRIPTPATH="$(dirname "$SCRIPT")" 

$SCRIPT将包含$SCRIPT的真实文件path, $SCRIPTPATH包含脚本的目录的真实path。

在使用之前阅读这个答案的评论。

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

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

我们把自己的产品realpath-lib放在GitHub上,免费和不受限制地使用社区。

无耻的插件,但与这个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_dirnameget_filenameget_stemname validate_path validate_realpath 。 它是免费的,干净的,简单的和有据可查的,所以它也可以用于学习的目的,毫无疑问可以改进。 尝试跨平台。

更新:经过一些审查和testing,我们已经取代了上述function取得了相同的结果(不使用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 ,它提供了parsing符号链接到物理系统的能力。 默认情况下,它保持符号链接不动。

符合sh的方式:

 SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done` 

您可以尝试定义以下variables:

 CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" 

或者你可以在bash中尝试下面的函数:

 realpath () { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" } 

这个函数需要1个参数。 如果参数已经是绝对path,按原样打印,否则打印$PWDvariables+文件名参数(不带./前缀)。

有关:

  • Bash脚本绝对path与OSX

  • 从内部获取Bash脚本的源代码目录

再考虑一下这个问题:在这个线程中引用了一个非常受欢迎的解决scheme,它的起源在这里 :

 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 

由于使用了dirname,我已经远离了这个解决scheme – 它可能会出现跨平台的困难,特别是在出于安全原因需要locking脚本的情况下。 但作为一个纯粹的Bash替代品,如何使用:

 DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )" 

这是一个select吗?

也许对以下问题的接受答案可能有帮助。

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

鉴于你只是想要从连接$ PWD和$ 0(假设$ 0不是绝对的开始)得到的名称进行规范化,只需沿着abs_dir=${abs_dir//\/.\//\/}等等。

是的,我知道这看起来很可怕,但它会工作,是纯粹的打击。

被接受的解决scheme对我来说是不方便的,不是“源代码”:
如果你从“ source ../../yourScript ”中调用它, $0就是“ bash ”!

下面的函数(对于bash> = 3.0)给了我正确的path,但是可以调用脚本(直接或通过source ,使用绝对path或相对path):
(通过“正确的path”,我指的是被调用的脚本完全绝对path ,即使从另一path直接或者以“ source ”调用)

 #!/bin/bash echo $0 executed function bashscriptpath() { local _sp=$1 local ascript="$0" local asp="$(dirname $0)" #echo "b1 asp '$asp', b1 ascript '$ascript'" if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}" elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd) else if [[ "$ascript" == "bash" ]] ; then ascript=${BASH_SOURCE[0]} asp="$(dirname $ascript)" fi #echo "b2 asp '$asp', b2 ascript '$ascript'" if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ; elif [[ "${ascript#../}" != "$ascript" ]]; then asp=$(pwd) while [[ "${ascript#../}" != "$ascript" ]]; do asp=${asp%/*} ascript=${ascript#../} done elif [[ "${ascript#*/}" != "$ascript" ]]; then if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi fi fi eval $_sp="'$asp'" } bashscriptpath H export H=${H} 

关键是要检测“ source ”情况,并使用${BASH_SOURCE[0]}来取回实际的脚本。

简单地说: BASEDIR=$(readlink -f $0 | xargs dirname)

没有花哨的运营商

HIH。

如果我们使用Bash,我相信这是最方便的方式,因为它不需要调用任何外部命令:

 THIS_PATH="${BASH_SOURCE[0]}"; THIS_DIR=$(dirname $THIS_PATH) 

尝试这个:

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

容易阅读? 替代。 忽略符号链接

 #!/bin/bash currentDir=$( cd $(dirname "$0") pwd ) echo -n "current " pwd echo script $currentDir 

只是对它而言,我已经做了一些黑客攻击脚本,纯粹是在纯粹的文本,纯粹在bash中。 我希望我抓住了所有的边缘情况。 请注意,我在另一个答案中提到的${var//pat/repl}是行不通的,因为你不能只取代最短的匹配,这是将/foo/../replace为例如/*/../将采取一切之前,而不是一个单一的条目。 而且由于这些模式不是真正的正则expression式,所以我不明白怎样才能工作。 所以这里是我提出的很好的复杂的解决scheme,享受。 ;)

顺便说一句,让我知道如果你发现任何未处理的边缘情况。

 #!/bin/bash canonicalize_path() { local path="$1" OIFS="$IFS" IFS=$'/' read -a parts < <(echo "$path") IFS="$OIFS" local i=${#parts[@]} local j=0 local back=0 local -a rev_canon while (($i > 0)); do ((i--)) case "${parts[$i]}" in ""|.) ;; ..) ((back++));; *) if (($back > 0)); then ((back--)) else rev_canon[j]="${parts[$i]}" ((j++)) fi;; esac done while (($j > 0)); do ((j--)) echo -n "/${rev_canon[$j]}" done echo } canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/" 

我已经成功地使用了下面的方法(不是在OSX上),它只使用内置的shell,并处理'source foobar.sh'的情况,据我所见。

下面这个例子代码的一个问题是函数使用$ PWD,这个函数调用时可能是正确的,也可能不正确。 所以需要处理。

 #!/bin/bash function canonical_path() { # Handle realtive vs absolute path [ ${1:0:1} == '/' ] && x=$1 || x=$PWD/$1 # Change to dirname of x cd ${x%/*} # Combine new pwd with basename of x echo $(pwd -P)/${x##*/} cd $OLDPWD } echo $(canonical_path "${BASH_SOURCE[0]}") type [ type cd type echo type pwd 

还有另一种方法来做到这一点:

 shopt -s extglob selfpath=$0 selfdir=${selfpath%%+([!/])} while [[ -L "$selfpath" ]];do selfpath=$(readlink "$selfpath") if [[ ! "$selfpath" =~ ^/ ]];then selfpath=${selfdir}${selfpath} fi selfdir=${selfpath%%+([!/])} done echo $selfpath $selfdir 

一个class轮

 `dirname $(realpath $0)` 

更简单地说,这是对我有用的东西:

 MY_DIR=`dirname $0` source $MY_DIR/_inc_db.sh