Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Is there a way for a sourced shell script to find out the path to itself? I'm mainly concerned with bash, though I have some coworkers who use tcsh.

I'm guessing I may not have a ton of luck here, since sourcing causes commands to be executed in the current shell, so $0 is still the current shell's invocation, not the sourced script. My best thought currently is to do source $script $script, so that the first positional parameter contains the necessary information. Anyone have a better way?

To be clear, I am sourcing the script, not running it:

source foo.bash
share|improve this question

5 Answers 5

up vote 13 down vote accepted

In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.

#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
    echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
    echo "run $0"
endif

In Bash:

#!/bin/bash
called=$_
[[ $called != $0 ]] && echo "Script is being sourced" || echo "Script is being run"
echo "\$BASH_SOURCE ${BASH_SOURCE[@]}"
share|improve this answer
    
Wow, that's exactly what I was looking for, and I can barely find any reference to it in the documentation. Way to know your stuff. –  Jefromi Dec 8 '10 at 19:42
    
I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it... –  Jefromi Apr 20 '11 at 16:28
    
The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts? –  Jefromi May 9 '11 at 18:04
1  
@clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting. –  Dennis Williamson Mar 4 '13 at 11:51
1  
Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works. –  clacke Mar 5 '13 at 7:35

I think that you could use $BASH_SOURCE variable. It returns path that was executed:

pbm@tauri ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh

So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.

share|improve this answer
1  
And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details. –  Gilles Dec 8 '10 at 19:09
    
So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work? –  Kevin Cantu Dec 8 '10 at 19:36
    
Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it. –  Jefromi Dec 8 '10 at 19:46

For thoroughness and the sake of searchers, here is what these do... It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).

test.sh:

#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE

test2.sh:

#! /bin/sh
source ./test.sh

Bash:

$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh

Dash

$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$

Replacing $BASH_SOURCE with $DASH_SOURCE resulted in the same thing.

share|improve this answer

For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:

if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
    echo "I'm being sourced!"
    exit 1
fi
share|improve this answer

Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:

$ cat bash0
#!/bin/bash
echo \$0=$0
dirname $0
$ bash0    # "." appears in PATH right now.
$0=./bash0
.
$ ./bash0
$0=./bash0
.
$ $PWD/bash0
$0=/home/00/bediger/src/ksh/bash0
/home/00/bediger/src/ksh
$ $PWD/../ksh/bash0
$0=/home/00/bediger/src/ksh/../ksh/bash0
/home/00/bediger/src/ksh/../ksh
$ ../ksh/bash0
$0=../ksh/bash0
../ksh

You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.

share|improve this answer
4  
This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees. –  Jefromi Dec 8 '10 at 16:32

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.