How can a bash script know the directory it is installed in when it is sourced with . operator?
I believe $(dirname "$BASH_SOURCE")
will do what you want, as long as the file you are sourcing is not a symlink.
If the file you are sourcing may be a symlink, you can do something like the following to get the true directory:
PRG="$BASH_SOURCE"
progname=`basename "$BASH_SOURCE"`
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
dir=$(dirname "$PRG")
Here is what might be an elegant solution:
script_path="${BASH_SOURCE[0]}"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
This will not, however, work when sourcing links. In that case, one might do
script_path="$(readlink -f "$(readlink "${BASH_SOURCE[0]}")")"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
Things to note:
- arrays like
${array[x]}
are not POSIX compliant - but then, theBASH_SOURCE
array is only available in Bash, anyway - on macOS, the native BSD
readlink
does not support-f
, so you might have to install GNUreadlink
using e.g. brew bybrew install coreutils
and replacereadlink
bygreadlink
- depending on your use case, you might want to use the
-e
or-m
switches instead of-f
plus possibly-n
; see readlink man page for details
A different take on the problem - if you're using "." in order to set environment variables, another standard way to do this is to have your script echo variable setting commands, e.g.:
# settings.sh
echo export CLASSPATH=${CLASSPATH}:/foo/bar
then eval the output:
eval $(/path/to/settings.sh)
That's how packages like modules work. This way also makes it easy to support shells derived from sh (X=...; export X
) and csh (setenv X ...
)