Find location of sourced shell script

The location of the sourced script is not available unless you are using a shell that offers extensions to the POSIX specification. You can test this with the following snippet:

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included

where included.sh contains

echo "$0"
set

In bash, the name of the sourced script is in $BASH_SOURCE. In zsh (in zsh compatibility mode, not in sh or ksh compatibility mode), it's in $0 (note that in a function, $0 is the function name instead). In pdksh and dash, it isn't available. In ksh93, this method doesn't reveal the solution, but the full path to the included script is available as ${.sh.file}.

If requiring bash or ksh93 or zsh is good enough, you can use this snippet:

if [ -n "$BASH_SOURCE" ]; then
  this_script=$BASH_SOURCE
elif [ -n "$ZSH_VERSION" ]; then
  setopt function_argzero
  this_script=$0
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
  eval 'this_script=${.sh.file}'
else
  echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
  exit 2
fi

You can try to guess the location of the script by looking at what files the shell has open. Experimentally this seems to work with dash and pdksh but not with bash or ksh93 which at least for a short script have closed the script file by the time they get around to executing it.

  open_file=$(lsof -F n -p $$ | sed -n '$s/^n//p')
  if [ -n "$open_file" ]; then
    # best guess: $open_file is this script
  fi

The script may not be the file with the highest-numbered descriptor if the script is sourced inside a complex script that has been playing with redirections. You may want to loop through the open files. This is not guaranteed to work anyway. The only reliable way to locate a sourced script is to use bash, ksh93 or zsh.


If you can change the interface, then instead of sourcing your script, have your script print out a shell snippet to be passed to eval in the caller. This is what scripts to set environment variables typically do. It allows your script to be written independently of the vagaries of the caller's shell and shell configuration.

#!/bin/sh
FOO_DIR=$(dirname -- "$0")
cat <<EOF
FOO_DIR='$(printf %s "$FOO_DIR" | sed "s/'/'\\''/g")'
PATH="\$PATH:$FOO_DIR/bin";
export FOO_DIR PATH
EOF

In the caller: eval "`/path/to/setenv`"

Tags:

Shell

Path