How can I determine the current shell I am working on?
Would the output of the ps
command alone be sufficient?
How can this be done in different flavors of UNIX?
There are 3 approaches to finding the name of the current shell's executable:
Please note that all 3 approaches can be fooled if the executable of the shell is
/bin/sh
but it's really a renamedbash
, for example (which frequently happens).Thus your second question of whether
ps
output will do is answered with "not always".echo $0
- will print the program name... which in the case of shell is the actual shellps -ef | grep $$ | grep -v grep
- This will look for the current process ID in the list of running processes. Since the current process is shell, it will be included.This is not 100% reliable, as you might have OTHER processes whose
ps
listing includes the same number as shell's process ID, especially if that ID is a small # (e.g. if shell's PID is "5", you may find processes called "java5" or "perl5" in the samegrep
output!). This is the second problem with the "ps" approach, on top of not being able to rely on the shell name.echo $SHELL
- The path to the current shell is stored as theSHELL
variable for any shell. The caveat for this one is that if you launch a shell explicitly as a subprocess (e.g. it's not your login shell), you will get your login shell's value instead. If that's a possibility, use theps
or$0
approach.
If, however, the executable doesn't match your actual shell (e.g.
/bin/sh
is actually bash or ksh), you need heuristics. Here are some environmental variables specific to various shells:$version
is set on tcsh$BASH
is set on bash$shell
(lowercase) is set to actual shell name in csh or tcsh$ZSH_NAME
is set on zshksh has
$PS3
and$PS4
set, whereas normal Bourne shell (sh
) only has$PS1
and$PS2
set. This generally seems like the hardest to distinguish - the ONLY difference in entire set of envionmental variables betweensh
andksh
we have installed on Solaris boxen is$ERRNO
,$FCEDIT
,$LINENO
,$PPID
,$PS3
,$PS4
,$RANDOM
,$SECONDS
,$TMOUT
.
ps -p $$
should work anywhere that the solutions involving ps -ef
and grep
do (on any Unix variant which supports POSIX options for ps
) and will not suffer from the false positives introduced by grepping for a sequence of digits which may appear elsewhere.
Try
ps -p $$ -oargs=
or
ps -p $$ -ocomm=
If you just want to ensure user is invoking script with bash:
if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
You can try:
ps | grep `echo $$` | awk '{ print $4 }'
Or:
echo $SHELL
$SHELL
need not always show the current shell. It only reflects the default shell to be invoked.
To test the above, Say bash
is the default shell, try echo $SHELL
, then in the same terminal ,get into some other shell(ksh for example) and try $SHELL
, you will see the result as bash in both cases.
To get the name of the current shell, Use cat /proc/$$/cmdline
And the path to the shell executable by readlink /proc/$$/exe
ps is the most reliable method. The SHELL envar is not guaranteed to be set and even if it is, it can be easily spoofed
I have a simple trick to find the current shell. Just type a random string (which is not a command). It will fail and return a "not found" error, but at start of line it will say which shell it is:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
This will give always the actual shell used - gets the name of the actual executable and not shell name (i.e. ksh93
instead of ksh
etc.) For /bin/sh
will show the actual shell used: i.e. dash
ls -l /proc/$$/exe | sed 's%.*/%%'
I know that here are many who say ls
output should be newer processed but what is the probability you'll have a shell you are using named with special characters or placed in a directory named with special characters? If still this is the case, here are plenty other examples doing it differently.
I have tried many different approaches and the best one for me is:
ps -p $$
It also works under Cygwin and cannot produce false positives as PID grepping. With some cleaning, it outputs just an executable name (under Cygwin with path):
ps -p $$ | tail -1 | awk '{print $NF}'
You can create a function so you don't have to memorize it:
# print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
...and then just execute shell
.
Tested under Debian and Cygwin.
My variant on printing the parent process.
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Why run unnecessary applications, when 'awk' can do it for you?
Provided that your /bin/sh
supports the POSIX standard and your system has the lsof
command installed - a possible alternative to lsof
could in this case be pid2path
- you can also use (or adapt) the following script that prints full paths:
#!/bin/sh
# cat /usr/local/bin/cursh
set -eu
pid="$$"
set -- sh bash zsh ksh ash dash csh tcsh pdksh mksh fish psh rc scsh bournesh wish Wish login
unset echo env sed ps lsof awk getconf
# getconf _POSIX_VERSION # reliable test for availability of POSIX system?
PATH="`PATH=/usr/bin:/bin:/usr/sbin:/sbin getconf PATH`"
[ $? -ne 0 ] && { echo "'getconf PATH' failed"; exit 1; }
export PATH
cmd="lsof"
env -i PATH="${PATH}" type "$cmd" 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
awkstr="`echo "$@" | sed 's/\([^ ]\{1,\}\)/|\/\1/g; s/ /$/g' | sed 's/^|//; s/$/$/'`"
ppid="`env -i PATH="${PATH}" ps -p $pid -o ppid=`"
[ "${ppid}"X = ""X ] && { echo "no ppid found"; exit 1; }
lsofstr="`lsof -p $ppid`" ||
{ printf "%s\n" "lsof failed" "try: sudo lsof -p \`ps -p \$\$ -o ppid=\`"; exit 1; }
printf "%s\n" "${lsofstr}" |
LC_ALL=C awk -v var="${awkstr}" '$NF ~ var {print $NF}'
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' #use the PID to see what the process is.
None of answers worked with fish
shell (it doesn't have variables $$
or $0
).
This works for me (tested on sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, and zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
This command outputs string like bash
. I'm using here only ps
, tail
, and sed
(without GNU extesions; try to add --posix
to check it). They all are standard POSIX commands. I'm sure tail
can be removed, but my sed
fu is not strong enough to do this.
It seems to me, that this solution is not very portable as it doesn't work on OS X. :(
On Mac OS X (& FreeBSD):
ps -p $$ -axco command | sed -n '$p'
If you just want to check that you are running (a particular version of) Bash,
the best way to do so is to use the $BASH_VERSINFO
array variable.
As a (readonly) array variable it cannot be set in the environment,
so you can be sure it is coming (if at all) from the current shell.
However, since Bash has different behavior when invoked as sh
,
you do also need to check the $BASH
environment variable ends with /bash
.
In a script I wrote that uses function names with -
(not underscore)
and depends on associative arrays (added in Bash 4),
I have the following sanity check (with helpful user error message):
case `eval 'echo $BASH@${BASH_VERSINFO[0]}' 2>/dev/null` in
*/bash@[456789])
# Claims bash version 4+, check for func-names and associative arrays
if ! eval "declare -A _ARRAY && func-name() { :; }" 2>/dev/null; then
echo >&2 "bash $BASH_VERSION is not supported (not really bash?)"
exit 1
fi
;;
*/bash@[123])
echo >&2 "bash $BASH_VERSION is not supported (version 4+ required)"
exit 1
;;
*)
echo >&2 "This script requires BASH (version 4+) - not regular sh"
echo >&2 "Re-run as \"bash $CMD\" for proper operation"
exit 1
;;
esac
You could omit the somewhat paranoid functional check for features in the first case, and just assume that future bash versions would be compatible.
Grepping PID from output of "ps" is not needed because you can read respective command line for any PID from /proc directory structure:
echo $(cat /proc/$$/cmdline)
However, that might not be any better than just simply:
echo $0
About running actually different shell than the name indicates, one idea is to request version from shell using the name you got previously:
<some_shell> --version
sh seems to fail with exit code 2 while others give something useful (but I am not able to verify all since I don't have them):
$ sh --version
sh: 0: Illegal option --
echo $?
2
This is not very clean solution, but does what you want.
I realise that the answer is a bit late in this good old 2015, but...
#MUST BE SOURCED..
getshell() {
local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"
shells_array=(
# It is important that the shells are listed by the decrease of their length name.
pdksh
bash dash mksh
zsh ksh
sh
)
local suited=false
for i in ${shells_array[*]}; do
if ! [ -z `printf $shell | grep $i` ] && ! $suited; then
shell=$i
suited=true
fi
done
echo $shell
}
getshell
Now you can use
$(getshell) --version.
This works, though, only on ksh-like shells.
My solution:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
This should be portable across different platforms and shells. It uses ps
like other solutions, but it doesn't rely on sed
or awk
and filters out junk from piping and ps
itself so that the shell should always be the last entry. This way we don't need to rely on non-portable PID variables or picking out the right lines and columns.
I've tested on Debian and MacOS with bash, zsh, and fish (which doesn't work with most of these solutions without changing the expression specifically for fish, because it uses a different PID variable).
There are many ways to find out the shell and its corresponding version. Here are few which worked for me.
Straight forward
- $> echo $0 (Gives you the program name. In my case the output was -bash)
- $> $SHELL (This takes you into the shell and in the prompt you get the shell name and version. In my case bash3.2$ )
- $> echo $SHELL (This will give you executable path. In my case /bin/bash)
- $> $SHELL --version (This will give complete info about the shell software with licence type)
Hackish approach
$> ******* (Type a set of random characters and in the output you will get the shell name. In my case -bash: chapter2-a-sample-isomorphic-app: command not found)
Do the following to know, whether your Shell is using DASH/BASH.
1) ls –la /bin/sh, if the result is /bin/sh ->/bin/bash ==> Then your shell is using BASH.
if the result is /bin/sh ->/bin/dash ==> Then your shell is using DASH.
If you want to change from BASH to DASH or vice-versa, use the below code ln -s /bin/bash /bin/sh (change shell to BASH)
NOTE: If the above command results in a error saying, /bin/sh already exists, remove the /bin/sh and try again.
Kindly use below command:
# ps -p $$ | tail -1 | awk '{print $4}'
This one works well on RHEL, MacOS, BSD and some AIXes
ps -T $$ | awk 'NR==2{print $NF}'
alternatively, following one should also work if pstree is available,
pstree | egrep $$ | awk 'NR==2{print $NF}'
来源:https://stackoverflow.com/questions/3327013/how-to-determine-the-current-shell-im-working-on