一个bash脚本可靠的方式来获得自己的完整path?
我有一个bash脚本需要知道它的完整path。 我试图find一个广泛兼容的方式来做到这一点,而不是结束相对或时髦的path。 我只需要支持bash,而不是sh,csh等。
我到目前为止发现的:
-
“ 从内部获取Bash脚本的源目录 ”地址的公认答案通过
dirname $0
获取脚本的path,这很好,但是可能会返回一个相对path(如.
),这是一个问题,如果你想更改脚本中的目录并使path仍指向脚本的目录。 不过,dirname
将成为这个难题的一部分。 -
被接受的答案是“ OS X的 绝对pathBash脚本 ” (OS X的具体,但答案无论如何)给出了一个函数,将testing,看看
$0
看起来相对,如果是的话,将预先$PWD
。 但是结果仍然可能有相对的位(尽pipe总体上它是绝对的) – 例如,如果脚本在目录/usr/bin
并且在/usr
并且键入bin/../bin/t
运行它(是的,这是错综复杂的),你最终以/usr/bin/../bin
作为脚本的目录path。 哪个有效 ,但… -
这个页面上的
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的最简单方法是使用cd
和pwd
:
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_dirname
, get_filename
, get_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,按原样打印,否则打印$PWD
variables+文件名参数(不带./
前缀)。
有关:
-
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