Demi
Demi

Reputation: 3611

How can I tell what shell is running my init file?

I have an init file (/etc/profile.d/which2.sh) that aliases the which command whenever any shell starts. In bash or sh that is fine but in zsh I don't want that as which is a built-in that is already aware of aliases and functions. How can I have the script 'know' when it is under zsh and not execute the alias?

$0 does not help.

I have fixed the problem by simply unsetting the alias in zsh-specific ~/.zshrc, but I would like to know another way.

Upvotes: 2

Views: 630

Answers (4)

mklement0
mklement0

Reputation: 437503

@rici's approach is the most robust and generic solution.

A simpler, zsh-specific approach would be to use:

# Define a `which` alias only when NOT run by `zsh`.
[ -z $ZSH_VERSION ] && alias which ...

Some background information on how to determine the specific shell [binary] that is currently executing:

A robust option is to call the ps utility, relying on the fact POSIX-like shells as well as csh / tcsh report their own PID (process ID) via built-in variable $$:

ps -p $$ -o "comm="

Note: ps implementations differ with respect to what path form they report; e.g.:

  • macOS reports the binary path as invoked, which may be the mere file name, the full path, or even a relative path; additionally, if the shell was invoked as a login shell, such as via sudo -i, the mere file name can be prefixed with - (e.g., -bash).

  • by contrast, the procps-ng 3.3.12 ps implementation that comes with Ubuntu 18.04 only ever reports the file name, and never with the - prefix.


Alternatively, for scripts that are being sourced, as initialization files are, as well as interactive shells, you can examine the value of $0; note, however, that this is not foolproof, because the caller may have set $0 to an arbitrary value.

  • It points to the shell binary as invoked, i.e., the value may be either a mere filename or a path.
  • The value may be prefixed with -, namely if the shell is a login shell; e.g., -bash in a shell started with sudo -i.

Note: Do NOT use $SHELL, as it only ever reflects the current user's default shell. Its value doesn't change even when running other shells later.

Thus, a POSIX-compatible way of obtaining the current shell's executable filename is:

basename -- "${0#-}"  # -> (e.g., in bash) 'bash'; will NOT work in csh/tcsh

Examples:

currShell=$(basename -- "${0#-}")  # Store shell-binary filename in variable.

[ "$(basename -- "${0#-}")" = 'zsh' ] && echo "This is a ZSH shell."

If it is sufficient to test for a specific shell only, you may be able to simply test for the presence of specific environment variables such as $BASH_VERSION, $ZSH_VERSION, or $KSH_VERSION.

Note, however, that not all shells have such characteristic variables; dash, for instance, does not.

Example:

[ -n "$ZSH_VERSION" ] && echo "This is a ZSH shell."

Upvotes: 0

David W.
David W.

Reputation: 107040

What are you testing for? In the old days when we had to determine whether we were running under Kornshell or Bournshell, we could do the following test:

if [ "$RANDOM" = "$RANDOM" ]
then
    echo "This is the Bourne shell"
    /bin/ksh $*   # Script needs the Kornshell
else
    echo "This is the Kornshell"
fi

Of course, both Kornshell and Bash expand $RANDOM (and so does zsh)...

Okay... You can find the processes running on the current tty this way:

ps -ft $(tty)

A little formatting:

$ ps -ocommand="" -t$(tty)  
login -pf david
-ksh
bash

Pretty good. Shells end with sh, so I'll make that assumption. I just want the lines ending with sh:

$ ps -ocommand="" -t$(tty)  | grep "sh$"
-ksh
bash

Yes, I'm running two shells in this TTY. I login with the Kornshell, and shelled out to bash. Let's toss the PID in the mix:

$ ps -t$(tty) -opid="" -ocommand="" | grep "sh$"
62599 -ksh
62855 bash

We want the one with the highest PID

$ ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1
62983 bash

Yup, I'm running Bash. Let's get rid of the PID:

$  ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1 | sed 's/.* //'
bash

Let's see if it works with various shells:

$ ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1 | sed 's/.* //'
ksh

Kornshell is fine.

$ ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1 | sed 's/.* //'
zsh

Works with zsh

$ ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1 | sed 's/.* //'
sh

Works with Dash or Ash

% ps -t$(tty) -opid="" -ocommand="" | grep "sh$" | sort -k1,1nr | head -1 | sed 's/.* //'
Illegal variable name.

Doesn't work in tcsh. Oh well... You can't please everybody.

Upvotes: 1

rici
rici

Reputation: 241701

How about

[ "$(which which)" = /usr/bin/which ] && alias which "whichever"

This doesn't verify the name of the shell; rather it verifies the shell's behaviour. That's an instance of a generally-applicable programming paradigm: test behaviour directly whenever possible. (See, for example, browser detection.)

In this case, if you just checked the shell's name as a proxy for a behaviour check, you might luck out now, but things could break in the future. The name is actually arbitrary, and new names might easily be introduced. For example, in some distros ksh is a hard-link to zsh; zsh will adapt its behaviour in an attempt to emulate ksh. As another example, I have two different zsh versions, one of which is invoked as zsh5.

Ideally, the test wouldn't depend on the precise location of the which utility, either.

Upvotes: 3

Coroos
Coroos

Reputation: 390

The SHELL environment variable contains the full path to the binary of your shell. You could use that (or its basename):

s=$(basename $SHELL)
[ "$s" = 'zsh' ] || alias which="what you want it to be"

Upvotes: -1

Related Questions