How to define a shell script to be sourced not run
Assuming that you are running bash, put the following code near the start of the script that you want to be sourced but not executed:
if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
echo "Hey, you should source this script, not execute it!"
exit 1
fi
Under bash, ${BASH_SOURCE[0]}
will contain the name of the current file that the shell is reading regardless of whether it is being sourced or executed.
By contrast, $0
is the name of the current file being executed.
-ef
tests if these two files are the same file. If they are, we alert the user and exit.
Neither -ef
nor BASH_SOURCE
are POSIX. While -ef
is supported by ksh, yash, zsh and Dash, BASH_SOURCE
requires bash. In zsh
, however, ${BASH_SOURCE[0]}
could be replaced by ${(%):-%N}
.
A non-executable file can be sourced but not executed, so, as a first line of defense, not setting the executable flag should be a good hint...
Edit: trick I just stumbled on: make the shebang be any executable that isn't a shell interpreter, /bin/false
makes the script return an error (rc!=0)
#!/bin/false "This script should be sourced in a shell, not executed directly"
There are several methods suggested in this Stack Overflow post, of which, I liked the function-based one suggested by Wirawan Purwanto and mr.spuratic best:
The most robust way, as suggested by Wirawan Purwanto, is to check
FUNCNAME[1]
within a function:function mycheck() { declare -p FUNCNAME; } mycheck
Then:
$ bash sourcetest.sh declare -a FUNCNAME='([0]="mycheck" [1]="main")' $ . sourcetest.sh declare -a FUNCNAME='([0]="mycheck" [1]="source")'
This is the equivalent to checking the output of
caller
, the valuesmain
andsource
distinguish the caller's context. UsingFUNCNAME[]
saves you capturing and parsingcaller
output. You need to know or calculate your local call depth to be correct though. Cases like a script being sourced from within another function or script will cause the array (stack) to be deeper. (FUNCNAME
is a special bash array variable, it should have contiguous indexes corresponding to the call stack, as long as it is neverunset
.)
So you can add to the start of the script:
function check()
{
if [[ ${FUNCNAME[-1]} != "source" ]] # bash 4.2+, use ${FUNCNAME[@]: -1} for older
then
printf "Usage: source %s\n" "$0"
exit 1
fi
}
check