views:

89

answers:

4

The scenario is that users are asked to source a script file:

$ source envsetup.sh

This script file may use bash only feature so we have detect the running shell is bash or not.

For other shells that share common syntax with bash, for example, sh, zsh, ksh, I'd like to report a warning.

What is the most reliable way to detect the current shell across Linux, Cygwin, OS X?

What I know is $BASH, but I am wondering the chances it could fail.

+1  A: 

The SHELL environment variable will tell you what login shell is running.

Also, you can use ps $$ to find the current shell, which can be used if you want to know what shell the script is running under (not necessarily the login shell). To whittle down the ps output to just the shell name: ps o command= $$ (not sure how cross-platform safe this is, but it works on Mac OS X).

Jeff
Try this from within bash before you rely on `$SHELL`: `csh -c 'echo "$SHELL"'`
D.Shawley
My impression of the question is that the script needs to know the login shell. Using bash as the login shell, running that command should return bash, which is expected.
Jeff
Not the login shell. If you login use bash then change to csh, $SHELL is remain bash, but `source` would fail.
Kan-Ru Chen
`ps o command= $$` also works on Linux, now I have to check cygwin..But you know, the ps command has so many variants..
Kan-Ru Chen
`ps -o command -p $$` is probably the most portable form but it still can fail. This is actually a tricky one to get to work reliably.
D.Shawley
+4  A: 

There are a bunch of environment variables that you can look at but many of them will not detect if a different shell is spawned from bash. Consider the following:

bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: -bash, PS1: bash$ , prompt: 

bash$ csh
[lorien:~] daveshawley% echo "SHELL: $SHELL, shell: $shell, \$0: $0, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: /bin/tcsh, ARGV[0]: csh, PS1: bash$ , prompt: [%m:%c3] %n%#

[lorien:~] daveshawley% bash -r
bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: sh, PS1: bash$ , prompt:

bash$ zsh
% echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: zsh, PS1: % , prompt: % 

% ksh
$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: ksh, PS1: bash$ , prompt: 

There are a number of variables specific to the various shells except that they have a habit of being inherited by sub-shells which is where the environment thing really breaks. The only thing that almost works is ps -o command -p $$. This technically gives you the command name that the shell is running as. In most cases this will work... since applications are started with some variant of the exec system call and it allows for the name of the command and the executable to differ, it is possible for this to fail as well. Consider:

bash$ exec -a "-csh" bash
bash$ echo "$0, $SHELL, $BASH"
-csh, /bin/bash, /bin/bash
bash$ ps -o command -p $$
COMMAND
-csh
bash$

Another trick is to use lsof -p $$ | awk '(NR==2) {print $1}'. This is probably as close as you can get if you are lucky enough to have lsof handy.

D.Shawley
+1  A: 

This works also

[ -z "$BASH_VERSION" ] && return
fgm
A: 

What prevents you from writing portable (ie. shell-independent) script?

Roman Cheplyaka
Because upstream relies very strongly on it. It's not what I can control ;-)
Kan-Ru Chen
So you're kinda patching existing scripts for some distribution?
Roman Cheplyaka
Out of curiosity, what bash-specific features may be used by `envsetup.sh`? I would imagine that it contains just some `export A=B` statements...
Roman Cheplyaka
I'm wondering that, too. The whole build system is strongly tied on bash, so they don't want envsetup.sh to be portable. *sigh*It's the *cough* Android *cough* project.
Kan-Ru Chen