How to get shell to self-detect using zsh or bash
Asked Answered
D

8

87

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
Dentil answered 28/3, 2012 at 15:37 Comment(4)
Wouldn't it make more sense to put this in .profile, which is portable across shells, including sh?Carbonaceous
@Carbonaceous Hi, because it's a specific thing for bash or zsh I think...Dentil
I suggest you read this section of the advanced bash scripting guide.Lei
$0 == "-zsh" instead of $0 == "zsh"Luvenialuwana
L
42

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.

Langham answered 28/3, 2012 at 20:40 Comment(4)
"That's always going to be whatever you put in the #! line of the script" - That's not true since a user could easily just pass the script filename as a parameter to any shell.Volsung
@MichaelMior: And the user probably will pass the script filename as a parameter to some random shell, if the script is not marked executable. Depending on your installation process, this may or may not be the case.Supporter
As expected, the accepted solution only offers rhetorical admonishment and chastisement rather than meaningful solutions. Everyone wants adl's authoritative answer instead, which provides several general-purpose snippets addressing the actual question.Oleson
@CecilCurry I think my answer was accepted because it answers what the asker meant to ask. adl's answer is a more detailed treatment of the title, but that's often the wrong question to ask.Rizal
T
195

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.

Tile answered 28/3, 2012 at 15:43 Comment(10)
Hi Thank you for your answer. I've tried your last method. While I resides in zsh, I ran your script, but it still says I am using bash. I took a photo: screenshotDentil
@juanitofatas You ran the script under bash, so it correctly reported it was running under bash.Rizal
@juanitofatas You ran zsh in a terminal. From that interactive zsh, you ran bash to interpret a script. In that script, you tested which shell was executed the script, and the answer was bash.Rizal
@adl, the last code chunk is missing a "$" in front of SHELL. I tried editing it, but StackOverflow edits require at least 6 characters to be changed.Tate
Fails on set -u: unbound variableAncona
While possibly useful, this will fail if export BASH_VERSION is executed prior to transitioning from bash to zsh. Likewise export ZSH_VERSION when transitioning from zsh to bash. Even without an export scenario, depending on an environmental variable is risky, since it could be easily modified or unset prior to the shell check. Conceivably you could use typeset -r ZSH_VERSIONand typeset -r BASH_VERSION ... tho it's kludgy at best, difficult to implement correctly, and probably poor practice.Charades
zsh doesn't set $ZSH_VERSION anymore. It does set a var called $ZSH though.Beaird
@Beaird latest development version of Zsh still sets $ZSH_VERSION and it is unlikely they will remove that variable that is used in many places by Zsh itself.Tile
@Tile if you grep through the zsh repo you can see that it's not set: github.com/ohmyzsh/ohmyzsh/search?q=ZSH_VERSION. This checks out empirically too as neither myself or any of my team members have it set in their shell. Why do you think it's still set?Beaird
@Beaird It's normal for ohmyzsh (a set of configuration files, not zsh itself) to not set that variable. That variable is set in the Zsh source code github.com/zsh-users/zsh/blob/master/Src/params.c#L938 and then used all over the place.Tile
Q
55

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.

Quadruplet answered 11/10, 2014 at 12:42 Comment(5)
This doesn't work for all shells, but is still my go-to method any time I sit down at a CLI -- it works more times than not. Shells such as Fish will not return anything for echo $0Mascia
The -zsh or -bash is used to indicate a login shell. If it doesn't have the - infront of the name, then the user is using a non-login shellStudent
This doesn't work if you're in a function, where $0 will return the function name.Ivo
This doesn’t work when called by a script either, it gives the name of the scriptLazy
detected_shell=$(echo "${0//-/}") ... You can test it like this: if [[ -f "${HOME}/.${detected_shell}rc" ]]; then echo "Found ${HOME}/.${detected_shell}rc"; fiBoyish
L
42

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.

Langham answered 28/3, 2012 at 20:40 Comment(4)
"That's always going to be whatever you put in the #! line of the script" - That's not true since a user could easily just pass the script filename as a parameter to any shell.Volsung
@MichaelMior: And the user probably will pass the script filename as a parameter to some random shell, if the script is not marked executable. Depending on your installation process, this may or may not be the case.Supporter
As expected, the accepted solution only offers rhetorical admonishment and chastisement rather than meaningful solutions. Everyone wants adl's authoritative answer instead, which provides several general-purpose snippets addressing the actual question.Oleson
@CecilCurry I think my answer was accepted because it answers what the asker meant to ask. adl's answer is a more detailed treatment of the title, but that's often the wrong question to ask.Rizal
J
6

You can simply try

 echo $SHELL
Janel answered 21/4, 2020 at 16:11 Comment(1)
This fails. In Zsh, if I open a bash subshell and do this, it still says /bin/zsh, which is obviously not the shell.Brushoff
A
5

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
Ancona answered 23/9, 2020 at 20:49 Comment(1)
This succeeds if I open a bash subshell from zsh, unlike echo $SHELLBrushoff
I
3

An alternative, might not work for all shells.

for x in $(ps -p $$)
do
  ans=$x
done
echo $ans
Indoaryan answered 29/3, 2012 at 1:17 Comment(3)
ps -p $$ --no-headers -o cmd is a bit better.Cosmorama
Except for when it doesn't work: ps: unknown option -- no-headersIvo
ps -f -o cmd= -p "$$" | sed '1{/^$/N;s/^\n//;t r;:r;/^\[/{:l;$s/^\[\(.*\)\]$/\1/;t;N;b l}}' should be robust and complete. (The sed deals with stripping -f fallback annotations & non–POSIX-compliant ps implementations that still write header lines when all header text fields are null.) It looks like the cmd format name isn't standard, though, so you could also try ps -o comm= -p "$$" | sed '1{/^$/N;s/^\n//}' if that fails.Mantinea
G
1

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                                                                                                                                               
Glenine answered 24/1, 2014 at 8:38 Comment(0)
H
1

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
Hughhughes answered 1/8, 2019 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.