Using anyenv in Fish Shell - shell

I tried to using anyenv in Fish Shell but the error occured.
This is my ~./config/fish/config.fish file.
set -x PATH $HOME/.anyenv/bin $PATH
eval (anyenv init -)
What's wrong with this?
Errors which I got:
$# is not supported. In fish, please use 'count $argv'.
- (line 1): begin; source "/Users/kasumi/.anyenv/libexec/../completions/anyenv.bash" anyenv() { typeset command command="$1" if [ "$#" -gt 0 ]; then shift fi command anyenv "$command" "$#" } export NDENV_ROOT="/Users/kasumi/.anyenv/envs/ndenv" export PATH="/Users/kasumi/.anyenv/envs/ndenv/bin:$PATH" export PATH="/Users/kasumi/.anyenv/envs/ndenv/shims:${PATH}" source "/Users/kasumi/.anyenv/envs/ndenv/libexec/../completions/ndenv.bash" ndenv rehash 2>/dev/null ndenv() { typeset command command="$1" if [ "$#" -gt 0 ]; then shift fi case "$command" in rehash|shell) eval "`ndenv "sh-$command" "$#"`";; *) command ndenv "$command" "$#";; esac }
^
from sourcing file -
called on line 60 of file /usr/local/Cellar/fish/2.4.0/share/fish/functions/eval.fish
in function 'eval'
called on line 6 of file ~/.config/fish/config.fish
from sourcing file ~/.config/fish/config.fish
called on standard input
source: Error while reading file '-'
Added:
I tried answer (https://stackoverflow.com/a/42119354/7524270) but I get some new errors.
New Errors:
Variables cannot be bracketed. In fish, please use "$PATH".
- (line 1): begin; source "/Users/kasumi/.anyenv/libexec/../completions/anyenv.fish" function anyenv set command $argv[1] set -e argv[1] command anyenv "$command" $argv end set -x NDENV_ROOT "/Users/kasumi/.anyenv/envs/ndenv" set -x PATH $PATH "/Users/kasumi/.anyenv/envs/ndenv/bin" export PATH="/Users/kasumi/.anyenv/envs/ndenv/shims:${PATH}" ndenv rehash 2>/dev/null ndenv() { typeset command command="$1" if [ "$#" -gt 0 ]; then shift fi case "$command" in rehash|shell) eval "`ndenv "sh-$command" "$#"`";; *) command ndenv "$command" "$#";; esac }
^
from sourcing file -
called on line 60 of file /usr/local/Cellar/fish/2.5.0/share/fish/functions/eval.fish
in function 'eval'
called on line 4 of file ~/.config/fish/config.fish
from sourcing file ~/.config/fish/config.fish
called during startup
source: Error while reading file '-'

Quick fix:
In ~/.config/fish/config.fish, change:
eval (anyenv init -)
to:
eval (anyenv init - fish)
EDIT: or a slightly more bullet-proof approach:
eval (command anyenv init - fish)
(command forces fish to ignore functions)
What's going on?
anyenv init uses the $SHELL environment variable to determine your active shell and returns some commands, to be executed by the shell, in order to finish the initialization of anyenv.
For some reason the $SHELL variable in your environment points to bash and not to fish (you can verify this by running echo $SHELL).
The most probable reason is that fish is simply not the default shell for your user. You may execute grep $USER /etc/passwd to find out (look at what it says after the last :). It's possible to change the default shell with chsh, but if you decide to do it, do it with great care. There might be some important settings in your .bashrc or some other programs may depend on a POSIX-compliant shell to work properly.
Another option is that somewhere you have an override of the $SHELL variable, such that it points to bash and not fish. I have this in my byobu/tmux configuration because it helped me avoid some strange behavior.
Luckily, anyenv init lets the user (you) specify the shell manually (ignore $SHELL), and that should fix your issue with anyenv.

Related

Can bash retrieve the entire command within the script? [duplicate]

I have a script where I do not want it to call exit if it's being sourced.
I thought of checking if $0 == bash but this has problems if the script is sourced from another script, or if the user sources it from a different shell like ksh.
Is there a reliable way of detecting if a script is being sourced?
Robust solutions for bash, ksh, zsh, including a cross-shell one, plus a reasonably robust POSIX-compliant solution:
Version numbers given are the ones on which functionality was verified - likely, these solutions work on much earlier versions, too - feedback welcome.
Using POSIX features only (such as in dash, which acts as /bin/sh on Ubuntu), there is no robust way to determine if a script is being sourced - see below for the best approximation.
Important:
The solutions determine whether the script is being sourced by its caller, which may be a shell itself or another script (which may or may not be sourced itself):
Also detecting the latter case adds complexity; if you do not need to detect the case when your script is being sourced by another script, you can use the following, relatively simple POSIX-compliant solution:
# Helper function
is_sourced() {
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file:*) return 0;; esac
else # Add additional POSIX-compatible shell names here, if needed.
case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) return 0;; esac
fi
return 1 # NOT sourced.
}
# Sample call.
is_sourced && sourced=1 || sourced=0
All solutions below must run in the top-level scope of your script, not inside functions.
One-liners follow - explanation below; the cross-shell version is complex, but it should work robustly:
bash (verified on 3.57, 4.4.19, and 5.1.16)
(return 0 2>/dev/null) && sourced=1 || sourced=0
ksh (verified on 93u+)
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
zsh (verified on 5.0.5)
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
cross-shell (bash, ksh, zsh)
(
[[ -n $ZSH_VERSION && $ZSH_EVAL_CONTEXT =~ :file$ ]] ||
[[ -n $KSH_VERSION && "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] ||
[[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)
) && sourced=1 || sourced=0
POSIX-compliant; not a one-liner (single pipeline) for technical reasons and not fully robust (see bottom):
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
Explanations
bash
(return 0 2>/dev/null) && sourced=1 || sourced=0
Note: The technique was adapted from user5754163's answer, as it turned out to be more robust than the original solution, [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]
Bash allows return statements only from functions and, in a script's top-level scope, only if the script is sourced.
If return is used in the top-level scope of a non-sourced script, an error message is emitted, and the exit code is set to 1.
(return 0 2>/dev/null) executes return in a subshell and suppresses the error message; afterwards the exit code indicates whether the script was sourced (0) or not (1), which is used with the && and || operators to set the sourced variable accordingly.
Use of a subshell is necessary, because executing return in the top-level scope of a sourced script would exit the script.
Tip of the hat to #Haozhun, who made the command more robust by explicitly using 0 as the return operand; he notes: per bash help of return [N]: "If N is omitted, the return status is that of the last command." As a result, the earlier version [which used just return, without an operand]
produces incorrect result if the last command on the user's shell has a non-zero return value.
ksh
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
Special variable ${.sh.file} is somewhat analogous to $BASH_SOURCE; note that ${.sh.file} causes a syntax error in bash, zsh, and dash, so be sure to execute it conditionally in multi-shell scripts.
Unlike in bash, $0 and ${.sh.file} are NOT guaranteed to be the same - at different times either may be a relative path or mere file name, while the other may be a full one; therefore, both $0 and ${.sh.file} must be resolved to full paths before comparing. If the full paths differ, sourcing is implied.
zsh
[[ $ZSH_EVAL_CONTEXT =~ :file$) ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT contains information about the evaluation context: substring file, separated with :, is only present if the script is being sourced.
In a sourced script's top-level scope, $ZSH_EVAL_CONTEXT ends with :file, and that's what this test is limited to. Inside a function, :shfunc is appended to :file; inside a command substitution, :cmdsubst, is appended.
Using POSIX features only
If you're willing to make certain assumptions, you can make a reasonable, but not fool-proof guess as to whether your script is being sourced, based on knowing the binary filenames of the shells that may be executing your script.
Notably, this means that this approach doesn't detect the case when your script is being sourced by another script.
The section "How to handle sourced invocations" in this answer discusses the edge cases that cannot be handled with POSIX features only in detail.
Examining the binary filename relies on the standard behavior of $0, which zsh, for instance, does not exhibit.
Thus, the safest approach is to combine the robust, shell-specific methods above - which do not rely on $0 - with a $0-based fallback solution for all remaining shells.
In short: The following solution:
in the shells that are covered with shell-specific tests: works robustly.
in all other shells: works only as expected when the script is being sourced directly from such a shell, as opposed to from another script.
Tip of the hat to Stéphane Desneux and his answer for the inspiration (transforming my cross-shell statement expression into a sh-compatible if statement and adding a handler for other shells).
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
Note that, for robustness, each shell binary filename (e.g. sh) is represented twice - once as-is and a second time, prefixed with -. This is to account for environments, such as macOS, where interactive shells are launched as login shells with a custom $0 value that is the (path-less) shell filename prefixed with -.Thanks, t7e.
(While sh and dash are perhaps unlikely to be used as interactive shells, others that you may need to add to the list may.)
[1] user1902689 discovered that [[ $0 != "$BASH_SOURCE" ]] yields a false positive when you execute a script located in the $PATH by passing its mere filename to the bash binary; e.g., bash my-script, because $0 is then just my-script, whereas $BASH_SOURCE is the full path. While you normally wouldn't use this technique to invoke scripts in the $PATH - you'd just invoke them directly (my-script) - it is helpful when combined with -x for debugging.
If your Bash version knows about the BASH_SOURCE array variable, try something like:
# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
This seems to be portable between Bash and Korn:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
A line similar to this or an assignment like pathname="$_" (with a later test and action) must be on the first line of the script or on the line after the shebang (which, if used, should be for ksh in order for it to work under the most circumstances).
After reading #DennisWilliamson's answer, there are some issues, see below:
As this question stand for ksh and bash, there is another part in this answer concerning ksh... see below.
Simple bash way
[ "$0" = "$BASH_SOURCE" ]
Let's try (on the fly because that bash could ;-):
source <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
bash <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)
I use source instead off . for readability (as . is an alias to source):
. <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
Note that process number don't change while process stay sourced:
echo $$
29301
Why not to use $_ == $0 comparison
For ensuring many case, I begin to write a true script:
#!/bin/bash
# As $_ could be used only once, uncomment one of two following lines
#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell
[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"
Copy this to a file called testscript:
cat >testscript
chmod +x testscript
Now we could test:
./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)
That's ok.
. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
That's ok.
But,for testing a script before adding -x flag:
bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)
Or to use pre-defined variables:
env PATH=/tmp/bintemp:$PATH ./testscript
proc: 25948[ppid:24890] is own (DW purpose: sourced)
env SOMETHING=PREDEFINED ./testscript
proc: 25972[ppid:24890] is own (DW purpose: sourced)
This won't work anymore.
Moving comment from 5th line to 6th would give more readable answer:
./testscript
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own
. testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
source testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
bash testscript
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own
env FILE=/dev/null ./testscript
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own
Harder: ksh now...
As I don't use ksh a lot, after some read on the man page, there is my tries:
#!/bin/ksh
set >/tmp/ksh-$$.log
Copy this in a testfile.ksh:
cat >testfile.ksh
chmod +x testfile.ksh
Than run it two time:
./testfile.ksh
. ./testfile.ksh
ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user 2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user 2140 avr 11 13:48 /tmp/ksh-9781.log
echo $$
9725
and see:
diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
> lineno=0
> SHLVL=3
diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
< level=1
< lineno=1
< SHLVL=2
There is some variable herited in a sourced run, but nothing really related...
You could even check that $SECONDS is close to 0.000, but that's ensure only manualy sourced cases...
You even could try to check for what's parent is:
Place this into your testfile.ksh:
ps $PPID
Than:
./testfile.ksh
PID TTY STAT TIME COMMAND
32320 pts/4 Ss 0:00 -ksh
. ./testfile.ksh
PID TTY STAT TIME COMMAND
32319 ? S 0:00 sshd: user#pts/4
or ps ho cmd $PPID, but this work only for one level of subsessions...
Sorry, I couldn't find a reliable way of doing that, under ksh.
The BASH_SOURCE[] answer (bash-3.0 and later) seems simplest, though BASH_SOURCE[] is not documented to work outside a function body (it currently happens to work, in disagreement with the man page).
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 values main and source distinguish the caller's context. Using FUNCNAME[] saves you capturing and parsing caller 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 never unset.)
function issourced() {
[[ ${FUNCNAME[#]: -1} == "source" ]]
}
(In bash-4.2 and later you can use the simpler form ${FUNCNAME[-1]} instead for the last item in the array. Improved and simplified thanks to Dennis Williamson's comment below.)
However, your problem as stated is "I have a script where I do not want it to call 'exit' if it's being sourced". The common bash idiom for this situation is:
return 2>/dev/null || exit
If the script is being sourced then return will terminate the sourced script and return to the caller.
If the script is being executed, then return will return an error (redirected), and exit will terminate the script as normal. Both return and exit can take an exit code, if required.
Sadly, this doesn't work in ksh (at least not in the AT&T derived version I have here), it treats return as equivalent to exit if invoked outside a function or dot-sourced script.
Updated: What you can do in contemporary versions of ksh is to check the special variable .sh.level which is set to the function call depth. For an invoked script this will initially be unset, for a dot-sourced script it will be set to 1.
function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
This is not quite as robust as the bash version, you must invoke issourced() in the file you are testing from at the top level or at a known function depth.
(You may also be interested in this code on github which uses a ksh discipline function and some debug trap trickery to emulate the bash FUNCNAME array.)
The canonical answer here: http://mywiki.wooledge.org/BashFAQ/109 also offers $- as another indicator (though imperfect) of the shell state.
Notes:
it is possible to create bash functions named "main" and "source" (overriding the builtin), these names may appear in FUNCNAME[] but as long as only the last item in that array is tested there is no ambiguity.
I don't have a good answer for pdksh. The closest thing I can find applies only to pdksh, where each sourcing of a script opens a new file descriptor (starting with 10 for the original script). Almost certainly not something you want to rely on...
Editor's note: This answer's solution works robustly, but is bash-only. It can be streamlined to
(return 2>/dev/null).
TL;DR
Try to execute a return statement. If the script isn't sourced, that will raise an error. You can catch that error and proceed as you need.
Put this in a file and call it, say, test.sh:
#!/usr/bin/env sh
# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)
# What exit code did that give?
if [ "$?" -eq "0" ]
then
echo "This script is sourced."
else
echo "This script is not sourced."
fi
Execute it directly:
shell-prompt> sh test.sh
output: This script is not sourced.
Source it:
shell-prompt> source test.sh
output: This script is sourced.
For me, this works in zsh and bash.
Explanation
The return statement will raise an error if you try to execute it outside of a function or if the script is not sourced. Try this from a shell prompt:
shell-prompt> return
output: ...can only `return` from a function or sourced script
You don't need to see that error message, so you can redirect the output to dev/null:
shell-prompt> return >/dev/null 2>&1
Now check the exit code. 0 means OK (no errors occurred), 1 means an error occurred:
shell-prompt> echo $?
output: 1
You also want to execute the return statement inside of a sub-shell. When the return statement runs it . . . well . . . returns. If you execute it in a sub-shell, it will return out of that sub-shell, rather than returning out of your script. To execute in the sub-shell, wrap it in $(...):
shell-prompt> $(return >/dev/null 2>$1)
Now, you can see the exit code of the sub-shell, which should be 1, because an error was raised inside the sub-shell:
shell-prompt> echo $?
output: 1
FWIW, after reading all of the other answers, I came up with following solution for me:
Update: Actually, somebody spotted a since-corrected error in another answer which affected mine, too. I think the update here also is an improvement (see edits if you are curious).
This works for all scripts, which start with #!/bin/bash but might be sourced by different shells as well to learn some information (like settings) which is are kept outside the main function.
According to the comments below, this answer here apparently does not work for all bash variants. Also not for systems, where /bin/sh is based on bash. I. E. it fails for bash v3.x on MacOS. (Currenty I do not know how to solve this.)
#!/bin/bash
# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)
# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}
BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$#"
Instead of the last 2 lines you can use following (in my opinion less readable) code to not set BASH_SOURCE in other shells and allow set -e to work in main:
if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$#"; fi
This script-recipe has following properties:
If executed by bash the normal way, main is called. Please note that this does not include a call like bash -x script (where script does not contain a path), see below.
If sourced by bash, main is only called, if the calling script happens to have the same name. (For example, if it sources itself or via bash -c 'someotherscript "$#"' main-script args.. where main-script must be, what test sees as $BASH_SOURCE).
If sourced/executed/read/evaled by a shell other than bash, main is not called (BASH_SOURCE is always different to $0).
main is not called if bash reads the script from stdin, unless you set $0 to be the empty string like so: ( exec -a '' /bin/bash ) <script
If evaluated by bash with eval (eval "`cat script`" all quotes are important!) from within some other script, this calls main. If eval is run from commandline directly, this is similar to previous case, where the script is read from stdin. (BASH_SOURCE is blank, while $0 usually is /bin/bash if not forced to something completely different.)
If main is not called, it does return true ($?=0).
This does not rely on unexpected behavior (previously I wrote undocumented, but I found no documentation that you cannot unset nor alter BASH_SOURCE either):
BASH_SOURCE is a bash reserved array. But allowing BASH_SOURCE=".$0" to change it would open a very dangerous can of worms, so my expectation is, that this must have no effect (except, perhaps, some ugly warning shows up in some future version of bash).
There is no documentation that BASH_SOURCE works outside functions. However the opposite (that it only works in functions) is neither documented. The observation is, that it works (tested with bash v4.3 and v4.4, unfortunately I have no bash v3.x anymore) and that quite too many scripts would break, if $BASH_SOURCE stops working as observed. Hence my expectation is, that BASH_SOURCE stays as is for future versions of bash, too.
In contrast (nice find, BTW!) consider ( return 0 ), which gives 0 if sourced and 1 if not sourced. This comes a bit unexpected not only for me , and (according to the readings there) POSIX says, that return from subshell is undefined behavior (and the return here is clearly from a subshell). Perhaps this feature eventually gets enough widespread use such that it can no more be changed, but AFAICS there is a much higher chance that some future bash version accidental changes the return behavior in that case.
Unfortunately bash -x script 1 2 3 does not run main. (Compare script 1 2 3 where script has no path). Following can be used as workaround:
bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
That bash script 1 2 3 does not run main can be considered a feature.
Note that ( exec -a none script ) calls main (bash does not pass it's $0 to the script, for this you need to use -c as shown in the last point).
Thus, except for some some corner cases, main is only called, when the script is executed the usual way. Normally this is, what you want, especially because it lacks complex hard to understand code.
Note that it is very similar to the Python code:
if __name__ == '__main__': main()
Which also prevents calling of main, except for some corner cases, as
you can import/load the script and enforce that __name__='__main__'
Why I think this is a good general way to solve the challenge
If you have something, which can be sourced by multiple shells, it must be compatible. However (read the other answers), as there is no (easy to implement) portable way to detect the sourceing, you should change the rules.
By enforcing that the script must be executed by /bin/bash, you exactly do this.
This solves all cases but following in which case the script cannot run directly:
/bin/bash is not installed or disfunctional (i. E. in a boot environment)
If you pipe it to a shell like in curl https://example.com/script | $SHELL
(Note: This is only true if your bash is recent enough. This recipe is reported to fail for certain variants. So be sure to check it works for your case.)
However I cannot think about any real reason where you need that and also the ability to source the exactly same script in parallel! Usually you can wrap it to execute the main by hand. Like that:
$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
Notes
This answer would not have been possible without the help of all the other answers! Even the wrong ones - which initially made me posting this.
Update: Edited due to the new discoveries found in https://stackoverflow.com/a/28776166/490291
This works later on in the script and does'nt depend on the _ variable:
## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
exit 1 # not sourced
fi
or
[ $(basename $0) = $Prog ] && exit
I will give a BASH-specific answer. Korn shell, sorry. Suppose your script name is include2.sh ; then make a function inside the include2.sh called am_I_sourced. Here's my demo version of include2.sh:
am_I_sourced()
{
if [ "${FUNCNAME[1]}" = source ]; then
if [ "$1" = -v ]; then
echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
fi
return 0
else
if [ "$1" = -v ]; then
echo "I am not being sourced, my script/shell name was $0"
fi
return 1
fi
}
if am_I_sourced -v; then
echo "Do something with sourced script"
else
echo "Do something with executed script"
fi
Now try to execute it in many ways:
~/toys/bash $ chmod a+x include2.sh
~/toys/bash $ ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ bash ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script
So this works without exception, and it is not using the brittle $_ stuff. This trick uses BASH's introspection facility, i.e. built-in variables FUNCNAME and BASH_SOURCE; see their documentation in bash manual page.
Only two caveat:
1) the call to am_I_called must take place in the sourced script, but not within any function, lest ${FUNCNAME[1]} returns something else. Yeah...you could have checked ${FUNCNAME[2]} -- but you just make your life harder.
2) function am_I_called must reside in the sourced script if you want to find out what the name of the file being included.
I would like to suggest a small correction to Dennis' very helpful answer, to make it slightly more portable, I hope:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
because [[ isn't recognized by the (somewhat anal retentive IMHO) Debian POSIX compatible shell, dash. Also, one may need the quotes to protect against filenames containing spaces, again in said shell.
The most beautiful way to detect if a Bash script is being executed or sourced (imported)
I really think this is the most beautiful way to do it:
From my if__name__==__main___check_if_sourced_or_executed_best.sh file in my eRCaGuy_hello_world repo:
#!/usr/bin/env bash
main() {
echo "Running main."
# Add your main function code here
}
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# This script is being run.
__name__="__main__"
else
# This script is being sourced.
__name__="__source__"
fi
# Only run `main` if this script is being **run**, NOT sourced (imported)
if [ "$__name__" = "__main__" ]; then
echo "This script is being run."
main
else
echo "This script is being sourced."
fi
References:
See also my other answer here for additional details on the above technique, including showing the run output: What is the bash equivalent to Python's if __name__ == '__main__'?
This answer where I first learned about "${BASH_SOURCE[0]}" = "$0"
You can also explore the following alternatives if you like, but I prefer to use the code chunk above.
Important: Using the "${FUNCNAME[-1]}" technique does not properly handle nested scripts, where one script calls or sources another, whereas the if [ "${BASH_SOURCE[0]}" = "$0" ] technique does. That's another huge reason to use if [ "${BASH_SOURCE[0]}" = "$0" ] instead.
4 ways to determine whether a bash script is being sourced or executed
I have read a bunch of answers all over the place on this and a few other questions, and have come up with 4 ways I'd like to summarize and put in one place.
if __name__ == "__main__":
See: What does if __name__ == "__main__": do? for what that does in Python.
You can see a full demonstration of all 4 techniques below in my check_if_sourced_or_executed.sh script in my eRCaGuy_hello_world repo.
You can see one of the techniques in-use in my advanced bash program with help menu, argument parsing, main function, automatic execute vs source detection (akin to if __name__ == "__main__": in Python), etc, see my demo/template program in this list here. It is currently called argument_parsing__3_advanced__gen_prog_template.sh, but if that name changes in the future I'll update it in the list at the link just above
Anyway, here are the 4 Bash techniques:
Technique 1 (can be placed anywhere; handles nested scripts):
See: https://unix.stackexchange.com/questions/424492/how-to-define-a-shell-script-to-be-sourced-not-run/424495#424495
if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 2 [My favorite technique] (can be placed anywhere; handles nestes scripts):
See this type of technique in-use in my most-advanced bash demo script yet, here: argument_parsing__3_advanced__gen_prog_template.sh, near the bottom.
Modified from: What is the bash equivalent to Python's `if __name__ == '__main__'`?
if [ "${BASH_SOURCE[0]}" == "$0" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 3 (requires another line which MUST be outside all functions):
Modified from: How to detect if a script is being sourced
# A. Place this line OUTSIDE all functions:
(return 0 2>/dev/null) && script_is_being_executed="false" || script_is_being_executed="true"
# B. Place these lines anywhere
if [ "$script_is_being_executed" == "true" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 4 [Limitation: does not handle nested scripts!] (MUST be inside a function):
Modified from: How to detect if a script is being sourced
and Unix & Linux: How to define a shell script to be sourced not run.
if [ "${FUNCNAME[-1]}" == "main" ]; then
echo " This script is being EXECUTED."
run="true"
elif [ "${FUNCNAME[-1]}" == "source" ]; then
echo " This script is being SOURCED."
else
echo " ERROR: THIS TECHNIQUE IS BROKEN"
fi
This is where I first learned about the ${FUNCNAME[-1]} trick: #mr.spuratic: How to detect if a script is being sourced - he learned it from Dennis Williamson apparently.
See also:
[my answer] What is the bash equivalent to Python's if __name__ == '__main__'?
[my answer] Unix & Linux: How to define a shell script to be sourced not run
$_ is quite brittle. You have to check it as the first thing you do in the script. And even then, it is not guaranteed to contain the name of your shell (if sourced) or the name of the script (if executed).
For example, if the user has set BASH_ENV, then at the top of a script, $_ contains the name of the last command executed in the BASH_ENV script.
The best way I have found is to use $0 like this:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$#"
;;
esac
Unfortunately, this way doesn't work out of the box in zsh due to the functionargzero option doing more than its name suggests, and being on by default.
To work around this, I put unsetopt functionargzero in my .zshenv.
Not exactly what the OP wanted, but I often find myself needing to source a script just to load its functions (i.e. as a library). For example, for benchmarking or testing purposes.
Here's a design that works in all shells (including POSIX):
Wrap all your top-level actions in a run_main() function.
Have your sourced script check for an initial --no-run argument which doesn't perform any actions; without --no-run, it can call run_main.
source the script using:
set -- --no-run "$#"
. script.sh
shift
The problem with . or source is that it's impossible to pass arguments to the script portably. POSIX shells ignore arguments to . and pass the caller's "$#" no matter what.
I don't think there is any portable way to do this in both ksh and bash. In bash you could detect it using caller output, but I don't think there exists equivalent in ksh.
I followed mklement0 compact expression.
That's neat, but I noticed that it can fail in the case of ksh when invoked as this:
/bin/ksh -c ./myscript.sh
(it thinks it's sourced and it's not because it executes a subshell)
But the expression will work to detect this:
/bin/ksh ./myscript.sh
Also, even if the expression is compact, the syntax is not compatible with all shells.
So I ended with the following code, which works for bash,zsh,dash and ksh
SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
[[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
[[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
case $0 in *dash*) SOURCED=1 ;; esac
fi
Feel free to add exotic shells support :)
The fix for this issue is not to write code that needs to know such a thing in order to behave correctly. And the way to do that is to put the code into a function, and not into the mainline of a script that needs to be sourced.
Code inside a function can just return 0 or return 1. This terminates just the function, so that control returns to whatever invoked the function.
This works whether the function is called from the mainline of a sourced script, from the mainline of a top-level script, or from another function.
Use sourcing to bring in "library" scripts that only define functions and perhaps variables, but don't actually execute any other top-level commands:
. path/to/lib.sh # defines libfunction
libfunction arg
or else:
path/to/script.sh arg # call script as a child process
and not:
. path/to/script.sh arg # shell programming anti-pattern
A small addition to the #mklement0 answer. This is the custom function I used in my script to determine whether it is sourced or not:
replace_shell(){
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
case $ZSH_EVAL_CONTEXT in *:file*) echo "Zsh is sourced";; esac
else
case ${0##*/} in sh|dash|bash) echo "Bash is sourced";; esac
fi
}
In a function, the output of "$ZSH_EVAL_CONTEXT" for zsh is toplevel:file:shfunc and not just toplevel:file during sorcing; thus, *:file* should fix this issue.
I needed a one-liner that works on [mac, linux] with bash.version >= 3 and none of these answers fit the bill.
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$#"
Straight to the point: you must evaluate if the variable "$0" is equal to the name of your Shell.
Like this:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
Via SHELL:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
Via SOURCE:
$ source check_source.sh
First Parameter: bash
The script was sourced.
It's pretty hard to have a 100% portable way of detecting if a script was sourced or not.
Regarding my experience (7 years with Shellscripting), the only safe way (not relying on environment variables with PIDs and so on, which is not safe due to the fact that it is something VARIABLE), you should:
extend the possibilities from your if
using switch/case, if you want to.
Both options cannot be auto scaled, but it is the safer way.
For example:
when you source a script via an SSH session, the value returned by the variable "$0" (when using source), is -bash.
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
OR
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
echo "The script was sourced via SSH session."
else
echo "The script WAS NOT sourced."
fi
I ended up with checking [[ $_ == "$(type -p "$0")" ]]
if [[ $_ == "$(type -p "$0")" ]]; then
echo I am invoked from a sub shell
else
echo I am invoked from a source command
fi
When use curl ... | bash -s -- ARGS to run remote script on-the-fly, the $0 will be just bash instead of normal /bin/bash when run actual script file, so I use type -p "$0" to show full path of bash.
test:
curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE
source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE
wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE
This is a spin off from some other answers, regarding "universal" cross shell support. This is admittedly very similar to https://stackoverflow.com/a/2942183/3220983 in particular, though slightly different. The weakness with this, is that a client script must respect how to use it (i.e. by exporting a variable first). The strength is that this is simple and should work "anywhere". Here's a template for your cut & paste pleasure:
# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"
main()
{
echo "Running in direct executable context!"
}
if [ -z "${ENTRY_POINT}" ]; then main "$#"; fi
Note: I use export just be sure this mechanism can be extended into sub processes.
Use a shebang line and check if it is being Executed instead.
Your script should have a shebang line #!/path/to/shell saying what shell it should run in. Otherwise, you will have other cross shell compatibility issues as well.
Therefore, you only need to check if its being executed by attempting a command that does not work when being sourced.
eg. For a Bash script:
#!/usr/bin/env bash
if (return 0 2>/dev/null); then
echo "Script was sourced."
fi
This method also works for zsh and sh just change the shebang line.

sudo, Bad substitution, indirect parameter expansion

Here a script that try to do a 'ifndef' (ifndef.sh):
#!/bin/bash
################# __ifndef__ #################
MAIN_SCRIPT=$(cd "$(dirname "$0")" && pwd -P)"/$(basename $0)"
script_path=$(echo $MAIN_SCRIPT | sed -e "s#/#_#g" -e "s#\.#_#g" -e "s#-#_#")
define_var="ALREADY_SOURCED_$script_path"
echo "Main_script: $MAIN_SCRIPT"
echo "script_path: $script_path"
echo "define_var: $define_var"
echo "??: ${!define_var}"
[[ ! -z ${!define_var} ]] && return
export ALREADY_SOURCED_$script_path="defined"
################# __ifndef__ #################
When I try to execute this code in an another file (main.sh):
. ./ifndef.sh
echo "TEST !"
With sudo, I always get this error:
./main.sh: 10: ./ifndef.sh: Bad substitution
The command I used to launch the script is: ./main.sh
I don't know why ${!define_var} is problematic.
You can't run a bash script with sh -- it can only be run with bash. In particular, you're using several features here (including both indirect expansion a la ${!foo} and the extended test operator [[ ]]) that aren't part of the POSIX sh standard, and so aren't guaranteed to be available when run with sh.
Instead of running sh yourscript, run bash yourscript or just ./yourscript (with a shebang set and execute permissions granted); and instead of starting it with #!/bin/sh, start it with #!/bin/bash. If this is being sourced, ensure that the script you're sourcing it from is itself started with bash.
The other thing here is that your escaping isn't complete enough to ensure that your $script_path really is a valid shell variable name. Instead of trying to fix that, just switch to using an associative array -- that ways keys can contain any character other than NUL, which is invalid in UNIX paths anyhow:
#!/usr/bin/env bash
# uses the first bash interpreter on the PATH, so on MacOS X, can use MacPorts bash
# if sourcing this, be sure to do the same in the script you're sourcing it from.
### start version check
[ -n "$BASH_VERSION" ] || {
echo "Current shell is not bash; must use bash 4.0 or later" >&2
return 1 || exit 1
}
[[ $BASH_VERSION =~ ^([4-9][.]|[0-9][0-9]+) ]] || {
echo "Bash version 4 or newer is required (using $BASH_VERSION)" >&2
return 1 || exit 1
}
### end version check
### start redefinition check
declare -A already_sourced
[[ ${already_sourced[$BASH_SOURCE]} ]] && return
already_sourced[$BASH_SOURCE]=1
### end redefinition check
The above requires Bash 4.x, which isn't shipped with MacOS, but is available through MacPorts.
Using BASH you can skip all sed do all the replacements in BASH itself:
#!/bin/bash
################# __ifndef__ #################
MAIN_SCRIPT=$(cd "$(dirname "$0")" && pwd -P)"/$(basename $0)"
script_path="${MAIN_SCRIPT//[\/.-]/_}"
echo "Main_script: $MAIN_SCRIPT"
echo "script_path: $script_path"
define_var="ALREADY_SOURCED_$script_path"
echo "define_var: $define_var"
echo "??: ${!define_var}"
[[ ! -z ${!define_var} ]] && return
declare -x $define_var="defined"
echo "##: ${!define_var}"
################# __ifndef__ #################
My bad !
I just had to add '#!/bin/bash' in the inner script (main.sh here)

bashrc not exporting variable in function when changing directories

I have several projects I work on. Instead of setting aliases for each project's location, I would rather set it when I navigate to that specific directory. Each project is a git repo and I already have a mechanism that appends the current branch name to $PS1 when I navigate to it.
In my .bashrc, I call a function: parse_git_branch to append to command prompt:
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u#\h:\w\a\]$PS1\$(parse_git_branch) "
;;
*)
;;
esac
parse_git_branch is defined at the end of my .basrhc:
function parse_git_branch {
GIT_BRANCH=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ \[\1\]/')
if [[ -n $GIT_BRANCH ]];then
source $HOME/bin/cur
fi
echo $GIT_BRANCH
}
if GIT_BRANCH isn't empty, I source a simple script ~/bin/cur which sets an alias for the pwd:
#!/bin/bash
echo "got here!"
shopt -s expand_aliases
alias current="cd $(pwd)"
When cur is sourced, the alias is not set. The debug message does display correctly however.
I believe this is because shell scripts run "outside" of my current environment. The alias is set when call source cur from the command line.
So, why is the alias not being set when called from "parse_git_branch" inside my .bashrc?
Thanks!
Problem here is usage of $() that runs parse_git_branch in a subshell. As a result, alias current is not found in opened terminal because aliases are not visible by parent processes.
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u#\h:\w\a\]$PS1\$(parse_git_branch) "
source $HOME/bin/cur
;;
*)
;;
should work.
Ok, I figured it out. The issue here isn't why I'm trying to do this. The issue lays in the fact that I'm trying to affect a parent shell environment from a subshell environment spawned from it (The $() command syntax bart spoke of.)
So the workaround was to allow communication from both environments. For that, I simply used the filesystem. Here's what I did:
.bashrc didn't have to change much, but it didn't have to source anything either:
parse_git_branch {
GIT_BRANCH=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ \[\1\]/')
if [[ -n $GIT_BRANCH ]];then
$HOME/bin/cur -p
fi
echo $GIT_BRANCH
}
I modified ~/bin/cur to simply write or read the fs, not attempt to modify any environments:
#!/bin/bash
put() {
echo $(pwd) > $HOME/.tmpalias
}
get() {
cat $HOME/.tmpalias
}
case "$1" in
*p)
put
;;
*g)
get
;;
esac
The cur script will either write or read from the file .tmpalias when called (I didn't bother with arguments and files etc since this is only called in two places.
Then I added the following to my .bash_aliases file:
alias current='cd $(cur -g)'
Voila! So I can expand this into an event trapping mechanism that can be invoked for any command I run.

How to get shell to self-detect using zsh or bash

I've a question on how to tell which shell the user is using. Suppose a script that if the user is using zsh, then put PATH to his .zshrc and if using bash should put in .bashrc. And set rvmrc accordingly.
#!/usr/bin/env bash
export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
I've tried the following but it does not work : (
if [[ $0 == "bash" ]]; then
export PATH='/usr/local/bin:$PATH' >> ~/.bashrc
elif [[ $0 == "zsh" ]]; then
export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
fi
# ... more commands ...
if [[ $0 == "bash" ]]; then
[[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.bashrc
source ~/.bashrc
elif [[ $0 == "zsh" ]]; then
[[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.zshrc
source ~/.zshrc
fi
If the shell is Zsh, the variable $ZSH_VERSION is defined. Likewise for Bash and $BASH_VERSION.
if [ -n "$ZSH_VERSION" ]; then
# assume Zsh
elif [ -n "$BASH_VERSION" ]; then
# assume Bash
else
# assume something else
fi
However, these variables only tell you which shell is being used to run the above code. So you would have to source this fragment in the user's shell.
As an alternative, you could use the $SHELL environment variable (which should contain absolute path to the user's preferred shell) and guess the shell from the value of that variable:
case $SHELL in
*/zsh)
# assume Zsh
;;
*/bash)
# assume Bash
;;
*)
# assume something else
esac
Of course the above will fail when /bin/sh is a symlink to /bin/bash.
If you want to rely on $SHELL, it is safer to actually execute some code:
if [ -n "$($SHELL -c 'echo $ZSH_VERSION')" ]; then
# assume Zsh
elif [ -n "$($SHELL -c 'echo $BASH_VERSION')" ]; then
# assume Bash
else
# assume something else
fi
This last suggestion can be run from a script regardless of which shell is used to run the script.
Just do echo $0
it says -zsh if it's zsh and -bash if it's bash
EDIT: Sometimes it returns -zsh and sometimes zsh and the same with bash, idk why.
A word of warning: the question you seem to have asked, the question you meant to ask, and the question you should have asked are three different things.
“Which shell the user is using” is ambiguous. Your attempt looks like you're trying to determine which shell is executing your script. That's always going to be whatever you put in the #! line of the script, unless you meant your users to edit that script, so this isn't useful to you.
What you meant to ask, I think, is what the user's favorite shell is. This can't be determined fully reliably, but you can cover most cases. Check the SHELL environment variable. If it contains fish, zsh, bash, ksh or tcsh, the user's favorite shell is probably that shell. However, this is the wrong question for your problem.
Files like .bashrc, .zshrc, .cshrc and so on are shell initialization files. They are not the right place to define environment variables. An environment variable defined there would only be available in a terminal where the user launched that shell and not in programs started from a GUI. The definition would also override any customization the user may have done in a subsession.
The right place to define an environment variable is in a session startup file. This is mostly unrelated to the user's choice of shell. Unfortunately, there's no single place to define environment variables. On a lot of systems, ~/.profile will work, but this is not universal. See https://unix.stackexchange.com/questions/4621/correctly-setting-environment and the other posts I link to there for a longer discussion.
You can simply try
echo $SHELL
the other answers fail with set -u
if [ ! -z ${ZSH_VERSION+x} ]; then
echo "this is zsh"
echo ${(%):-%x}
elif [ ! -z ${BASH_VERSION+x} ]; then
echo "this is bash"
echo $BASH_SOURCE
else
echo "not recognized"
fi
An alternative, might not work for all shells.
for x in $(ps -p $$)
do
ans=$x
done
echo $ans
Myself having a similar problem, settled for:
_shell="$(ps -p $$ --no-headers -o comm=)"
if [[ $_shell == "zsh" ]]; then
read -q -s "?Do it?: "
fi
elif [[ $_shell == "bash" || $_shell == "sh" ]]; then
read -n 1 -s -p "Do it [y/n] "
fi
Here is how I am doing it based on a previous answer from Gilles :
if [ -n "$ZSH_VERSION" ]; then
SHELL_PROFILE="$HOME/.zprofile"
else
SHELL_PROFILE="$HOME/.bash_profile"
fi
echo "export VAR1=whatever" >> $SHELL_PROFILE
echo "INFO: Refreshing your shell profile: $SHELL_PROFILE"
if [ -n "$ZSH_VERSION" ]; then
exec zsh --login
else
source $SHELL_PROFILE
fi

How to detect if a script is being sourced

I have a script where I do not want it to call exit if it's being sourced.
I thought of checking if $0 == bash but this has problems if the script is sourced from another script, or if the user sources it from a different shell like ksh.
Is there a reliable way of detecting if a script is being sourced?
Robust solutions for bash, ksh, zsh, including a cross-shell one, plus a reasonably robust POSIX-compliant solution:
Version numbers given are the ones on which functionality was verified - likely, these solutions work on much earlier versions, too - feedback welcome.
Using POSIX features only (such as in dash, which acts as /bin/sh on Ubuntu), there is no robust way to determine if a script is being sourced - see below for the best approximation.
Important:
The solutions determine whether the script is being sourced by its caller, which may be a shell itself or another script (which may or may not be sourced itself):
Also detecting the latter case adds complexity; if you do not need to detect the case when your script is being sourced by another script, you can use the following, relatively simple POSIX-compliant solution:
# Helper function
is_sourced() {
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file:*) return 0;; esac
else # Add additional POSIX-compatible shell names here, if needed.
case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) return 0;; esac
fi
return 1 # NOT sourced.
}
# Sample call.
is_sourced && sourced=1 || sourced=0
All solutions below must run in the top-level scope of your script, not inside functions.
One-liners follow - explanation below; the cross-shell version is complex, but it should work robustly:
bash (verified on 3.57, 4.4.19, and 5.1.16)
(return 0 2>/dev/null) && sourced=1 || sourced=0
ksh (verified on 93u+)
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
zsh (verified on 5.0.5)
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
cross-shell (bash, ksh, zsh)
(
[[ -n $ZSH_VERSION && $ZSH_EVAL_CONTEXT =~ :file$ ]] ||
[[ -n $KSH_VERSION && "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] ||
[[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)
) && sourced=1 || sourced=0
POSIX-compliant; not a one-liner (single pipeline) for technical reasons and not fully robust (see bottom):
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
Explanations
bash
(return 0 2>/dev/null) && sourced=1 || sourced=0
Note: The technique was adapted from user5754163's answer, as it turned out to be more robust than the original solution, [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]
Bash allows return statements only from functions and, in a script's top-level scope, only if the script is sourced.
If return is used in the top-level scope of a non-sourced script, an error message is emitted, and the exit code is set to 1.
(return 0 2>/dev/null) executes return in a subshell and suppresses the error message; afterwards the exit code indicates whether the script was sourced (0) or not (1), which is used with the && and || operators to set the sourced variable accordingly.
Use of a subshell is necessary, because executing return in the top-level scope of a sourced script would exit the script.
Tip of the hat to #Haozhun, who made the command more robust by explicitly using 0 as the return operand; he notes: per bash help of return [N]: "If N is omitted, the return status is that of the last command." As a result, the earlier version [which used just return, without an operand]
produces incorrect result if the last command on the user's shell has a non-zero return value.
ksh
[[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]] && sourced=1 || sourced=0
Special variable ${.sh.file} is somewhat analogous to $BASH_SOURCE; note that ${.sh.file} causes a syntax error in bash, zsh, and dash, so be sure to execute it conditionally in multi-shell scripts.
Unlike in bash, $0 and ${.sh.file} are NOT guaranteed to be the same - at different times either may be a relative path or mere file name, while the other may be a full one; therefore, both $0 and ${.sh.file} must be resolved to full paths before comparing. If the full paths differ, sourcing is implied.
zsh
[[ $ZSH_EVAL_CONTEXT =~ :file$) ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT contains information about the evaluation context: substring file, separated with :, is only present if the script is being sourced.
In a sourced script's top-level scope, $ZSH_EVAL_CONTEXT ends with :file, and that's what this test is limited to. Inside a function, :shfunc is appended to :file; inside a command substitution, :cmdsubst, is appended.
Using POSIX features only
If you're willing to make certain assumptions, you can make a reasonable, but not fool-proof guess as to whether your script is being sourced, based on knowing the binary filenames of the shells that may be executing your script.
Notably, this means that this approach doesn't detect the case when your script is being sourced by another script.
The section "How to handle sourced invocations" in this answer discusses the edge cases that cannot be handled with POSIX features only in detail.
Examining the binary filename relies on the standard behavior of $0, which zsh, for instance, does not exhibit.
Thus, the safest approach is to combine the robust, shell-specific methods above - which do not rely on $0 - with a $0-based fallback solution for all remaining shells.
In short: The following solution:
in the shells that are covered with shell-specific tests: works robustly.
in all other shells: works only as expected when the script is being sourced directly from such a shell, as opposed to from another script.
Tip of the hat to Stéphane Desneux and his answer for the inspiration (transforming my cross-shell statement expression into a sh-compatible if statement and adding a handler for other shells).
sourced=0
if [ -n "$ZSH_VERSION" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
[ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
(return 0 2>/dev/null) && sourced=1
else # All other shells: examine $0 for known shell binary filenames.
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac
fi
Note that, for robustness, each shell binary filename (e.g. sh) is represented twice - once as-is and a second time, prefixed with -. This is to account for environments, such as macOS, where interactive shells are launched as login shells with a custom $0 value that is the (path-less) shell filename prefixed with -.Thanks, t7e.
(While sh and dash are perhaps unlikely to be used as interactive shells, others that you may need to add to the list may.)
[1] user1902689 discovered that [[ $0 != "$BASH_SOURCE" ]] yields a false positive when you execute a script located in the $PATH by passing its mere filename to the bash binary; e.g., bash my-script, because $0 is then just my-script, whereas $BASH_SOURCE is the full path. While you normally wouldn't use this technique to invoke scripts in the $PATH - you'd just invoke them directly (my-script) - it is helpful when combined with -x for debugging.
If your Bash version knows about the BASH_SOURCE array variable, try something like:
# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
This seems to be portable between Bash and Korn:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
A line similar to this or an assignment like pathname="$_" (with a later test and action) must be on the first line of the script or on the line after the shebang (which, if used, should be for ksh in order for it to work under the most circumstances).
After reading #DennisWilliamson's answer, there are some issues, see below:
As this question stand for ksh and bash, there is another part in this answer concerning ksh... see below.
Simple bash way
[ "$0" = "$BASH_SOURCE" ]
Let's try (on the fly because that bash could ;-):
source <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
bash <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)
I use source instead off . for readability (as . is an alias to source):
. <(echo $'#!/bin/bash
[ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)
Note that process number don't change while process stay sourced:
echo $$
29301
Why not to use $_ == $0 comparison
For ensuring many case, I begin to write a true script:
#!/bin/bash
# As $_ could be used only once, uncomment one of two following lines
#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell
[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"
Copy this to a file called testscript:
cat >testscript
chmod +x testscript
Now we could test:
./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)
That's ok.
. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
That's ok.
But,for testing a script before adding -x flag:
bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)
Or to use pre-defined variables:
env PATH=/tmp/bintemp:$PATH ./testscript
proc: 25948[ppid:24890] is own (DW purpose: sourced)
env SOMETHING=PREDEFINED ./testscript
proc: 25972[ppid:24890] is own (DW purpose: sourced)
This won't work anymore.
Moving comment from 5th line to 6th would give more readable answer:
./testscript
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own
. testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
source testscript
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced
bash testscript
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own
env FILE=/dev/null ./testscript
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own
Harder: ksh now...
As I don't use ksh a lot, after some read on the man page, there is my tries:
#!/bin/ksh
set >/tmp/ksh-$$.log
Copy this in a testfile.ksh:
cat >testfile.ksh
chmod +x testfile.ksh
Than run it two time:
./testfile.ksh
. ./testfile.ksh
ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user 2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user 2140 avr 11 13:48 /tmp/ksh-9781.log
echo $$
9725
and see:
diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
> lineno=0
> SHLVL=3
diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
< level=1
< lineno=1
< SHLVL=2
There is some variable herited in a sourced run, but nothing really related...
You could even check that $SECONDS is close to 0.000, but that's ensure only manualy sourced cases...
You even could try to check for what's parent is:
Place this into your testfile.ksh:
ps $PPID
Than:
./testfile.ksh
PID TTY STAT TIME COMMAND
32320 pts/4 Ss 0:00 -ksh
. ./testfile.ksh
PID TTY STAT TIME COMMAND
32319 ? S 0:00 sshd: user#pts/4
or ps ho cmd $PPID, but this work only for one level of subsessions...
Sorry, I couldn't find a reliable way of doing that, under ksh.
The BASH_SOURCE[] answer (bash-3.0 and later) seems simplest, though BASH_SOURCE[] is not documented to work outside a function body (it currently happens to work, in disagreement with the man page).
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 values main and source distinguish the caller's context. Using FUNCNAME[] saves you capturing and parsing caller 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 never unset.)
function issourced() {
[[ ${FUNCNAME[#]: -1} == "source" ]]
}
(In bash-4.2 and later you can use the simpler form ${FUNCNAME[-1]} instead for the last item in the array. Improved and simplified thanks to Dennis Williamson's comment below.)
However, your problem as stated is "I have a script where I do not want it to call 'exit' if it's being sourced". The common bash idiom for this situation is:
return 2>/dev/null || exit
If the script is being sourced then return will terminate the sourced script and return to the caller.
If the script is being executed, then return will return an error (redirected), and exit will terminate the script as normal. Both return and exit can take an exit code, if required.
Sadly, this doesn't work in ksh (at least not in the AT&T derived version I have here), it treats return as equivalent to exit if invoked outside a function or dot-sourced script.
Updated: What you can do in contemporary versions of ksh is to check the special variable .sh.level which is set to the function call depth. For an invoked script this will initially be unset, for a dot-sourced script it will be set to 1.
function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
This is not quite as robust as the bash version, you must invoke issourced() in the file you are testing from at the top level or at a known function depth.
(You may also be interested in this code on github which uses a ksh discipline function and some debug trap trickery to emulate the bash FUNCNAME array.)
The canonical answer here: http://mywiki.wooledge.org/BashFAQ/109 also offers $- as another indicator (though imperfect) of the shell state.
Notes:
it is possible to create bash functions named "main" and "source" (overriding the builtin), these names may appear in FUNCNAME[] but as long as only the last item in that array is tested there is no ambiguity.
I don't have a good answer for pdksh. The closest thing I can find applies only to pdksh, where each sourcing of a script opens a new file descriptor (starting with 10 for the original script). Almost certainly not something you want to rely on...
Editor's note: This answer's solution works robustly, but is bash-only. It can be streamlined to
(return 2>/dev/null).
TL;DR
Try to execute a return statement. If the script isn't sourced, that will raise an error. You can catch that error and proceed as you need.
Put this in a file and call it, say, test.sh:
#!/usr/bin/env sh
# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)
# What exit code did that give?
if [ "$?" -eq "0" ]
then
echo "This script is sourced."
else
echo "This script is not sourced."
fi
Execute it directly:
shell-prompt> sh test.sh
output: This script is not sourced.
Source it:
shell-prompt> source test.sh
output: This script is sourced.
For me, this works in zsh and bash.
Explanation
The return statement will raise an error if you try to execute it outside of a function or if the script is not sourced. Try this from a shell prompt:
shell-prompt> return
output: ...can only `return` from a function or sourced script
You don't need to see that error message, so you can redirect the output to dev/null:
shell-prompt> return >/dev/null 2>&1
Now check the exit code. 0 means OK (no errors occurred), 1 means an error occurred:
shell-prompt> echo $?
output: 1
You also want to execute the return statement inside of a sub-shell. When the return statement runs it . . . well . . . returns. If you execute it in a sub-shell, it will return out of that sub-shell, rather than returning out of your script. To execute in the sub-shell, wrap it in $(...):
shell-prompt> $(return >/dev/null 2>$1)
Now, you can see the exit code of the sub-shell, which should be 1, because an error was raised inside the sub-shell:
shell-prompt> echo $?
output: 1
FWIW, after reading all of the other answers, I came up with following solution for me:
Update: Actually, somebody spotted a since-corrected error in another answer which affected mine, too. I think the update here also is an improvement (see edits if you are curious).
This works for all scripts, which start with #!/bin/bash but might be sourced by different shells as well to learn some information (like settings) which is are kept outside the main function.
According to the comments below, this answer here apparently does not work for all bash variants. Also not for systems, where /bin/sh is based on bash. I. E. it fails for bash v3.x on MacOS. (Currenty I do not know how to solve this.)
#!/bin/bash
# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)
# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}
BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$#"
Instead of the last 2 lines you can use following (in my opinion less readable) code to not set BASH_SOURCE in other shells and allow set -e to work in main:
if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$#"; fi
This script-recipe has following properties:
If executed by bash the normal way, main is called. Please note that this does not include a call like bash -x script (where script does not contain a path), see below.
If sourced by bash, main is only called, if the calling script happens to have the same name. (For example, if it sources itself or via bash -c 'someotherscript "$#"' main-script args.. where main-script must be, what test sees as $BASH_SOURCE).
If sourced/executed/read/evaled by a shell other than bash, main is not called (BASH_SOURCE is always different to $0).
main is not called if bash reads the script from stdin, unless you set $0 to be the empty string like so: ( exec -a '' /bin/bash ) <script
If evaluated by bash with eval (eval "`cat script`" all quotes are important!) from within some other script, this calls main. If eval is run from commandline directly, this is similar to previous case, where the script is read from stdin. (BASH_SOURCE is blank, while $0 usually is /bin/bash if not forced to something completely different.)
If main is not called, it does return true ($?=0).
This does not rely on unexpected behavior (previously I wrote undocumented, but I found no documentation that you cannot unset nor alter BASH_SOURCE either):
BASH_SOURCE is a bash reserved array. But allowing BASH_SOURCE=".$0" to change it would open a very dangerous can of worms, so my expectation is, that this must have no effect (except, perhaps, some ugly warning shows up in some future version of bash).
There is no documentation that BASH_SOURCE works outside functions. However the opposite (that it only works in functions) is neither documented. The observation is, that it works (tested with bash v4.3 and v4.4, unfortunately I have no bash v3.x anymore) and that quite too many scripts would break, if $BASH_SOURCE stops working as observed. Hence my expectation is, that BASH_SOURCE stays as is for future versions of bash, too.
In contrast (nice find, BTW!) consider ( return 0 ), which gives 0 if sourced and 1 if not sourced. This comes a bit unexpected not only for me , and (according to the readings there) POSIX says, that return from subshell is undefined behavior (and the return here is clearly from a subshell). Perhaps this feature eventually gets enough widespread use such that it can no more be changed, but AFAICS there is a much higher chance that some future bash version accidental changes the return behavior in that case.
Unfortunately bash -x script 1 2 3 does not run main. (Compare script 1 2 3 where script has no path). Following can be used as workaround:
bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
That bash script 1 2 3 does not run main can be considered a feature.
Note that ( exec -a none script ) calls main (bash does not pass it's $0 to the script, for this you need to use -c as shown in the last point).
Thus, except for some some corner cases, main is only called, when the script is executed the usual way. Normally this is, what you want, especially because it lacks complex hard to understand code.
Note that it is very similar to the Python code:
if __name__ == '__main__': main()
Which also prevents calling of main, except for some corner cases, as
you can import/load the script and enforce that __name__='__main__'
Why I think this is a good general way to solve the challenge
If you have something, which can be sourced by multiple shells, it must be compatible. However (read the other answers), as there is no (easy to implement) portable way to detect the sourceing, you should change the rules.
By enforcing that the script must be executed by /bin/bash, you exactly do this.
This solves all cases but following in which case the script cannot run directly:
/bin/bash is not installed or disfunctional (i. E. in a boot environment)
If you pipe it to a shell like in curl https://example.com/script | $SHELL
(Note: This is only true if your bash is recent enough. This recipe is reported to fail for certain variants. So be sure to check it works for your case.)
However I cannot think about any real reason where you need that and also the ability to source the exactly same script in parallel! Usually you can wrap it to execute the main by hand. Like that:
$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
Notes
This answer would not have been possible without the help of all the other answers! Even the wrong ones - which initially made me posting this.
Update: Edited due to the new discoveries found in https://stackoverflow.com/a/28776166/490291
This works later on in the script and does'nt depend on the _ variable:
## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
exit 1 # not sourced
fi
or
[ $(basename $0) = $Prog ] && exit
I will give a BASH-specific answer. Korn shell, sorry. Suppose your script name is include2.sh ; then make a function inside the include2.sh called am_I_sourced. Here's my demo version of include2.sh:
am_I_sourced()
{
if [ "${FUNCNAME[1]}" = source ]; then
if [ "$1" = -v ]; then
echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
fi
return 0
else
if [ "$1" = -v ]; then
echo "I am not being sourced, my script/shell name was $0"
fi
return 1
fi
}
if am_I_sourced -v; then
echo "Do something with sourced script"
else
echo "Do something with executed script"
fi
Now try to execute it in many ways:
~/toys/bash $ chmod a+x include2.sh
~/toys/bash $ ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ bash ./include2.sh
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script
~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script
So this works without exception, and it is not using the brittle $_ stuff. This trick uses BASH's introspection facility, i.e. built-in variables FUNCNAME and BASH_SOURCE; see their documentation in bash manual page.
Only two caveat:
1) the call to am_I_called must take place in the sourced script, but not within any function, lest ${FUNCNAME[1]} returns something else. Yeah...you could have checked ${FUNCNAME[2]} -- but you just make your life harder.
2) function am_I_called must reside in the sourced script if you want to find out what the name of the file being included.
I would like to suggest a small correction to Dennis' very helpful answer, to make it slightly more portable, I hope:
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
because [[ isn't recognized by the (somewhat anal retentive IMHO) Debian POSIX compatible shell, dash. Also, one may need the quotes to protect against filenames containing spaces, again in said shell.
The most beautiful way to detect if a Bash script is being executed or sourced (imported)
I really think this is the most beautiful way to do it:
From my if__name__==__main___check_if_sourced_or_executed_best.sh file in my eRCaGuy_hello_world repo:
#!/usr/bin/env bash
main() {
echo "Running main."
# Add your main function code here
}
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
# This script is being run.
__name__="__main__"
else
# This script is being sourced.
__name__="__source__"
fi
# Only run `main` if this script is being **run**, NOT sourced (imported)
if [ "$__name__" = "__main__" ]; then
echo "This script is being run."
main
else
echo "This script is being sourced."
fi
References:
See also my other answer here for additional details on the above technique, including showing the run output: What is the bash equivalent to Python's if __name__ == '__main__'?
This answer where I first learned about "${BASH_SOURCE[0]}" = "$0"
You can also explore the following alternatives if you like, but I prefer to use the code chunk above.
Important: Using the "${FUNCNAME[-1]}" technique does not properly handle nested scripts, where one script calls or sources another, whereas the if [ "${BASH_SOURCE[0]}" = "$0" ] technique does. That's another huge reason to use if [ "${BASH_SOURCE[0]}" = "$0" ] instead.
4 ways to determine whether a bash script is being sourced or executed
I have read a bunch of answers all over the place on this and a few other questions, and have come up with 4 ways I'd like to summarize and put in one place.
if __name__ == "__main__":
See: What does if __name__ == "__main__": do? for what that does in Python.
You can see a full demonstration of all 4 techniques below in my check_if_sourced_or_executed.sh script in my eRCaGuy_hello_world repo.
You can see one of the techniques in-use in my advanced bash program with help menu, argument parsing, main function, automatic execute vs source detection (akin to if __name__ == "__main__": in Python), etc, see my demo/template program in this list here. It is currently called argument_parsing__3_advanced__gen_prog_template.sh, but if that name changes in the future I'll update it in the list at the link just above
Anyway, here are the 4 Bash techniques:
Technique 1 (can be placed anywhere; handles nested scripts):
See: https://unix.stackexchange.com/questions/424492/how-to-define-a-shell-script-to-be-sourced-not-run/424495#424495
if [ "${BASH_SOURCE[0]}" -ef "$0" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 2 [My favorite technique] (can be placed anywhere; handles nestes scripts):
See this type of technique in-use in my most-advanced bash demo script yet, here: argument_parsing__3_advanced__gen_prog_template.sh, near the bottom.
Modified from: What is the bash equivalent to Python's `if __name__ == '__main__'`?
if [ "${BASH_SOURCE[0]}" == "$0" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 3 (requires another line which MUST be outside all functions):
Modified from: How to detect if a script is being sourced
# A. Place this line OUTSIDE all functions:
(return 0 2>/dev/null) && script_is_being_executed="false" || script_is_being_executed="true"
# B. Place these lines anywhere
if [ "$script_is_being_executed" == "true" ]; then
echo " This script is being EXECUTED."
run="true"
else
echo " This script is being SOURCED."
fi
Technique 4 [Limitation: does not handle nested scripts!] (MUST be inside a function):
Modified from: How to detect if a script is being sourced
and Unix & Linux: How to define a shell script to be sourced not run.
if [ "${FUNCNAME[-1]}" == "main" ]; then
echo " This script is being EXECUTED."
run="true"
elif [ "${FUNCNAME[-1]}" == "source" ]; then
echo " This script is being SOURCED."
else
echo " ERROR: THIS TECHNIQUE IS BROKEN"
fi
This is where I first learned about the ${FUNCNAME[-1]} trick: #mr.spuratic: How to detect if a script is being sourced - he learned it from Dennis Williamson apparently.
See also:
[my answer] What is the bash equivalent to Python's if __name__ == '__main__'?
[my answer] Unix & Linux: How to define a shell script to be sourced not run
$_ is quite brittle. You have to check it as the first thing you do in the script. And even then, it is not guaranteed to contain the name of your shell (if sourced) or the name of the script (if executed).
For example, if the user has set BASH_ENV, then at the top of a script, $_ contains the name of the last command executed in the BASH_ENV script.
The best way I have found is to use $0 like this:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$#"
;;
esac
Unfortunately, this way doesn't work out of the box in zsh due to the functionargzero option doing more than its name suggests, and being on by default.
To work around this, I put unsetopt functionargzero in my .zshenv.
Not exactly what the OP wanted, but I often find myself needing to source a script just to load its functions (i.e. as a library). For example, for benchmarking or testing purposes.
Here's a design that works in all shells (including POSIX):
Wrap all your top-level actions in a run_main() function.
Have your sourced script check for an initial --no-run argument which doesn't perform any actions; without --no-run, it can call run_main.
source the script using:
set -- --no-run "$#"
. script.sh
shift
The problem with . or source is that it's impossible to pass arguments to the script portably. POSIX shells ignore arguments to . and pass the caller's "$#" no matter what.
I don't think there is any portable way to do this in both ksh and bash. In bash you could detect it using caller output, but I don't think there exists equivalent in ksh.
I followed mklement0 compact expression.
That's neat, but I noticed that it can fail in the case of ksh when invoked as this:
/bin/ksh -c ./myscript.sh
(it thinks it's sourced and it's not because it executes a subshell)
But the expression will work to detect this:
/bin/ksh ./myscript.sh
Also, even if the expression is compact, the syntax is not compatible with all shells.
So I ended with the following code, which works for bash,zsh,dash and ksh
SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
[[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
[[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
case $0 in *dash*) SOURCED=1 ;; esac
fi
Feel free to add exotic shells support :)
The fix for this issue is not to write code that needs to know such a thing in order to behave correctly. And the way to do that is to put the code into a function, and not into the mainline of a script that needs to be sourced.
Code inside a function can just return 0 or return 1. This terminates just the function, so that control returns to whatever invoked the function.
This works whether the function is called from the mainline of a sourced script, from the mainline of a top-level script, or from another function.
Use sourcing to bring in "library" scripts that only define functions and perhaps variables, but don't actually execute any other top-level commands:
. path/to/lib.sh # defines libfunction
libfunction arg
or else:
path/to/script.sh arg # call script as a child process
and not:
. path/to/script.sh arg # shell programming anti-pattern
A small addition to the #mklement0 answer. This is the custom function I used in my script to determine whether it is sourced or not:
replace_shell(){
if [ -n "$ZSH_EVAL_CONTEXT" ]; then
case $ZSH_EVAL_CONTEXT in *:file*) echo "Zsh is sourced";; esac
else
case ${0##*/} in sh|dash|bash) echo "Bash is sourced";; esac
fi
}
In a function, the output of "$ZSH_EVAL_CONTEXT" for zsh is toplevel:file:shfunc and not just toplevel:file during sorcing; thus, *:file* should fix this issue.
I needed a one-liner that works on [mac, linux] with bash.version >= 3 and none of these answers fit the bill.
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$#"
Straight to the point: you must evaluate if the variable "$0" is equal to the name of your Shell.
Like this:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
Via SHELL:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
Via SOURCE:
$ source check_source.sh
First Parameter: bash
The script was sourced.
It's pretty hard to have a 100% portable way of detecting if a script was sourced or not.
Regarding my experience (7 years with Shellscripting), the only safe way (not relying on environment variables with PIDs and so on, which is not safe due to the fact that it is something VARIABLE), you should:
extend the possibilities from your if
using switch/case, if you want to.
Both options cannot be auto scaled, but it is the safer way.
For example:
when you source a script via an SSH session, the value returned by the variable "$0" (when using source), is -bash.
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
OR
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
echo "The script was sourced via SSH session."
else
echo "The script WAS NOT sourced."
fi
I ended up with checking [[ $_ == "$(type -p "$0")" ]]
if [[ $_ == "$(type -p "$0")" ]]; then
echo I am invoked from a sub shell
else
echo I am invoked from a source command
fi
When use curl ... | bash -s -- ARGS to run remote script on-the-fly, the $0 will be just bash instead of normal /bin/bash when run actual script file, so I use type -p "$0" to show full path of bash.
test:
curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE
source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE
wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE
This is a spin off from some other answers, regarding "universal" cross shell support. This is admittedly very similar to https://stackoverflow.com/a/2942183/3220983 in particular, though slightly different. The weakness with this, is that a client script must respect how to use it (i.e. by exporting a variable first). The strength is that this is simple and should work "anywhere". Here's a template for your cut & paste pleasure:
# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"
main()
{
echo "Running in direct executable context!"
}
if [ -z "${ENTRY_POINT}" ]; then main "$#"; fi
Note: I use export just be sure this mechanism can be extended into sub processes.
Use a shebang line and check if it is being Executed instead.
Your script should have a shebang line #!/path/to/shell saying what shell it should run in. Otherwise, you will have other cross shell compatibility issues as well.
Therefore, you only need to check if its being executed by attempting a command that does not work when being sourced.
eg. For a Bash script:
#!/usr/bin/env bash
if (return 0 2>/dev/null); then
echo "Script was sourced."
fi
This method also works for zsh and sh just change the shebang line.

Resources