views:

512

answers:

5

What I'd like to do is to include settings from a file into my current interactive bash shell like this:

$ . /path/to/some/dir/.settings

The problem is that the .settings script also needs to use the "." operator to include other files like this:

. .extra_settings

How do I reference the relative path for .extra_settings in the .settings file? These two files are always stored in the same directory, but the path to this directory will be different depending on where these files were installed.

The operator always knows the /path/to/some/dir/ as shown above. How can the .settings file know the directory where it is installed? I would rather not have an install process that records the name of the installed directory.

+1  A: 

You can usually get that from $0. But it's possible that $0 can be wrong, like when it's invoked through a symlink, so you can't count on it 100% of the time.

Paul Tomblin
A: 

I tried messing with variants of $(dirname $0) but it fails when the .settings file is included with ".". If I were executing the .settings file instead of including it, this solution would work. Instead, the $(dirname $0) always returns ".", meaning current directory. This fails when doing something like this:

$ cd / $ . /some/path/.settings

Gary
hmm, too bad. i've no idea then
Johannes Schaub - litb
+1  A: 

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 ...)

orip
Right, that's how INN does it. "innconfvar" (I think that's what it's called) emits the configuration variables as sh, csh or perl commands.
Paul Tomblin
I like this idea. However, I'm not using it because the side effects would make the .settings file even more unreadable since it already has plenty of escapes to begin with. See below.
Gary
+3  A: 

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")
Jason Day
Perfect. $(dirname "$BASH_SOURCE") is what I was exactly what I was looking for. For my purpose, I don't care so much about symlinks. Thanks to all that responded. Really good answers here.
Gary
To resolve symlinks you could just do: `dir=$(dirname $(readlink -f "$BASH_SOURCE"))`
Mark Longair
A: 

This sort of works. It works in the sense that you can use the $(dirname $0) syntax within the .settings file to determine its home since you are executing this script in a new shell. However, it adds an extra layer of convolution where you need to change lines such as:

export MYDATE=$(date)

to

echo "export MYDATE=\$(date)"

Maybe this is the only way?

Gary
Seems like it would also add complexity if you were sourcing in shell functions. (I was looking for a way to do this that works with both ksh and bash...)
Mike