我有一个Bash脚本需要知道它的完整路径。我试图找到一种广泛兼容的方式来做到这一点,而不会结束相对或时髦的路径。我只需要支持Bash,而不是sh,csh等。
到目前为止我发现了什么:
Getting the source directory of a Bash script from within 的接受答案通过dirname $0
解决了脚本的路径,这很好,但可能会返回 relative < / em> path(如.
),如果要更改脚本中的目录并使路径仍指向脚本的目录,则会出现问题。尽管如此,dirname
将成为这个难题的一部分。
Bash script absolute path with OS X 的已接受答案(特定于OS X,但答案无论如何)提供了一个函数,该函数将测试是否{ {1}}看起来是相对的,如果是的话会预先$0
。但是结果仍然可以包含相对位(尽管总体上它是绝对的) - 例如,如果脚本在$PWD
目录中t
并且您在/usr/bin
并且键入了/usr
运行它(是的,那是错综复杂的),最终以bin/../bin/t
作为脚本的目录路径。哪个有效,但是......
/usr/bin/../bin
解决方案on this page,如下所示:
readlink
但是# 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`
不是POSIX,显然解决方案依赖于GNU的readlink
,其中BSD由于某种原因无法工作(我无法访问类似BSD的系统进行检查)
所以,各种方法,但他们都有他们的警告。
什么是更好的方法? “更好”的意思是:
答案 0 :(得分:472)
以下是我提出的内容(编辑:加上sfstewman,levigroker,Kyle Strand和Rob Kennedy提供的一些调整),这些调整似乎最适合我的“更好”标准:
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
SCRIPTPATH
行似乎特别迂回,但为了正确处理空格和符号链接,我们需要它而不是SCRIPTPATH=`pwd`
。
另请注意,深奥的情况,例如执行一个根本不是来自可访问文件系统中的文件的脚本(这是完全可能的),也不能满足(或者在任何其他答案中)已经看过了。
答案 1 :(得分:185)
我很惊讶这里没有提到realpath
命令。我的理解是它可以广泛移植/移植。
您的初始解决方案变为:
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
根据您的偏好保留未解析的符号链接:
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
答案 2 :(得分:148)
我发现在Bash中获得完整规范路径的最简单方法是使用cd
和pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
使用${BASH_SOURCE[0]}
代替$0
会产生相同的行为,无论脚本是以<name>
还是source <name>
调用。
答案 3 :(得分:40)
我今天不得不重新审视此问题并找到 Get the source directory of a Bash script from within the script itself :
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
链接答案中有更多变体,例如:对于脚本本身是符号链接的情况。
答案 4 :(得分:36)
它不使用readlink中的-f
选项,因此它应该适用于BSD / Mac OS X.
.
点运算符调用时)foo->dir1/dir2/bar bar->./../doe doe->script
我正在寻找此代码不起作用的极端情况。请告诉我。
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`]"
脚本必须位于磁盘上。让它通过网络。 如果您尝试从PIPE运行此脚本,它将无法正常工作
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
从技术上讲,它是未定义的。实际上,没有理智的方法来检测这一点。 (协同进程无法访问父进程的环境。)
答案 5 :(得分:31)
使用:
SCRIPT_PATH=$(dirname `which $0`)
which
向标准输出打印在shell提示符下输入传递的参数时已经执行的可执行文件的完整路径(这是$ 0包含的内容)
dirname
从文件名中删除非目录后缀。
因此,无论路径是否已指定,您最终都会获得脚本的完整路径。
答案 6 :(得分:25)
由于我的Linux系统上没有默认安装realpath,以下内容适用于我:
SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
将包含脚本的真实文件路径,$SCRIPTPATH
包含脚本的目录的实际路径。
在使用本文之前,请阅读this answer的评论。
答案 7 :(得分:13)
易于阅读?以下是另一种选择。它忽略了符号链接
override func loadView() {
}
func zoomToUser(sender: UIButton!) {
mapView.showsUserLocation = true
}
自从几年前我发布了上述答案以来,我已经将我的实践演变为使用这种适当处理符号链接的特定于Linux的范例:
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
答案 8 :(得分:11)
很晚才回答这个问题,但我用的是:
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
答案 9 :(得分:8)
我们已将自己的产品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_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
,它提供了解析物理系统符号链接的功能。默认情况下,它会保持符号链接不变。
答案 10 :(得分:7)
简单地:
BASEDIR=$(readlink -f $0 | xargs dirname)
不需要花哨的操作员。
答案 11 :(得分:5)
Bourne shell(sh
)符合方式:
SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`
答案 12 :(得分:5)
您可以尝试定义以下变量:
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
或者您可以在Bash中尝试以下功能:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
此函数接受一个参数。如果参数已经有绝对路径,请按原样打印,否则打印$PWD
变量+文件名参数(不带./
前缀)。
相关:
答案 13 :(得分:3)
或许接受以下问题的答案可能会有所帮助。
How can I get the behavior of GNU's readlink -f on a Mac?
鉴于你只想规范化连接$ PWD和$ 0的名称(假设$ 0不是绝对的开头),只需在abs_dir=${abs_dir//\/.\//\/}
行使用一系列正则表达式替换即可
是的,我知道它看起来很糟糕,但它会起作用并且是纯粹的Bash。
答案 14 :(得分:3)
再次考虑这个问题:在这个线程中引用了一个非常流行的解决方案,其原点为here:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
由于使用了dirname,我远离了这个解决方案 - 它可能会出现跨平台的困难,特别是出于安全原因需要锁定脚本时。但作为一个纯粹的Bash替代品,如何使用:
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
这会是一个选择吗?
答案 15 :(得分:3)
接受的解决方案不方便(对我来说)不是“可源”的:
如果您从“source ../../yourScript
”调用它,$0
将为“bash
”!
以下函数(对于bash&gt; = 3.0)为我提供了正确的路径,但是可以直接或通过source
调用脚本,使用绝对或相对路径):
(通过“正确的路径”,我的意思是被称为的完整绝对路径,即使是从另一条路径调用,直接或使用“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]}
取回实际脚本。
答案 16 :(得分:2)
如果我们使用Bash,我相信这是最方便的方式,因为它不需要调用任何外部命令:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
答案 17 :(得分:1)
试试这个:
cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
答案 18 :(得分:0)
我已成功使用以下方法一段时间(虽然不是在OS X上),它只使用内置的shell并处理'source foobar.sh'的情况,就我所见。
以下示例代码的一个问题是(函数放在一起)示例代码是函数使用$ PWD,它在函数调用时可能正确也可能不正确。所以这需要处理。
#!/bin/bash
function canonical_path() {
# Handle relative 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
答案 19 :(得分:0)
一个班轮
`dirname $(realpath $0)`
答案 20 :(得分:0)
只是为了它的地狱我已经做了一些黑客攻击一个纯文本的东西,纯粹在Bash。我希望我抓住了所有边缘案例。
请注意,我在其他答案中提到的${var//pat/repl}
不起作用,因为您无法仅替换最短的匹配,这是将/foo/../
替换为/*/../
的问题。 #!/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/"
将收集所有内容,而不仅仅是一个条目。由于这些模式不是真正的正则表达式,我不知道如何才能使它工作。所以这是我提出的非常复杂的解决方案,享受。 ;)
顺便提一下,如果您发现任何未处理的边缘情况,请告诉我。
{{1}}
答案 21 :(得分:-1)
另一种方法:
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
答案 22 :(得分:-4)
更简单地说,这对我有用:
MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh