Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

The accepted answer for a similar question for bash does not seem to work for zsh. In fact, if I copy basically the same code given in that answer, to produce the script

#!/usr/bin/zsh -
# test.sh

[[ $_ != $0 ]] && echo "sourced\n" || echo "subshell\n"

the output hardly ever corresponds to the actual situation:

zsh% chmod +x ./test.sh
zsh% env -i /usr/bin/zsh -f
zsh% ./test.sh

zsh% /usr/bin/zsh ./test.sh

zsh% /bin/bash ./test.sh

zsh% source ./test.sh

zsh% . ./test.sh

zsh% env -i /bin/bash --norc --noprofile
bash-3.2$ ./test.sh

bash-3.2$ /usr/bin/zsh ./test.sh

bash-3.2$ /bin/bash ./test.sh

bash-3.2$ source ./test.sh

bash-3.2$ . ./test.sh

When the current interactive shell is zsh, the script gets it exactly wrong every time. It fares a bit better under bash (though in a way reminiscent of the stopped watch that gets the time exactly right twice a day).

These truly abysmal results give me little confidence in this approach.

Is there something better?

share|improve this question
If I were desperate I'd use $SECONDS - if it's zero, then it's a subshell; >0 and the file's been sourced. That idea is so stinky, I'm only posting it as a novelty item. – Mel Boyce Apr 19 '13 at 14:56
if [[ $ZSH_EVAL_CONTEXT == 'toplevel' ]]; then
    # We're not being sourced so run the colors command which in turn sources
    # this script and uses its content to produce representative output.

Via Kurtis Rader on the zsh-users mailing list.

share|improve this answer

You could get the Shell Level:

[ $SHLVL -gt 1 ] && echo "subshell"

There's also (ZSH-only) $ZSH_SUBSHELL.

Obviously these break if you're nesting.

share|improve this answer

If you only intend it to work with zsh, it looks like you could just swap the two cases:

#!/usr/bin/env zsh -
# test.sh

[[ $_ != $0 ]] && echo "subshell\n" || echo "sourced\n"

This might not be good enough for you, if you need it to support bash as well.

(I also changed the shebang because my zsh is not in /usr/bin, but this shouldn't affect you).

share|improve this answer
The line "These truly abysmal results give me little confidence in this approach" was an attempt to discourage precisely this answer. I guess I should have been blunter about it... – kjo Apr 19 '13 at 14:32
More specifically, this answer sounds to me like guesswork, which is not what I'm after (I can supply the guesswork myself). – kjo Apr 19 '13 at 14:35
@kjo Yes, perhaps it would help to be more clear about your situation, what you have tried, and why this solution is inadequate. It appears to me that this solution returns 'subshell' in every case where zsh (or bash) is executing the script as a subshell, and 'sourced' when you source it from a zsh prompt; is this not what you want? Sometimes guesswork is the only viable solution in the absence of a supported test. That, or considering a different approach than detecting your script's environment. – mrb Apr 19 '13 at 14:52
This code is used by smartcd, but does not work for me: when I source the script $_ is empty, and $0 contains the filename. – blueyed Sep 5 '14 at 7:50

Isn't the answer you are looking for the difference between login and interactive shell?

localhost% cat foo
#!/usr/bin/env zsh

[[ $- == *i* ]] && print ' interactive=sourced' || print ' login=called'

localhost% source foo
localhost% zsh foo   
share|improve this answer

Your Answer


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.