关于bash:如何确定我正在处理的当前shell?

How to determine the current shell I'm working on?

如何确定当前正在处理的shell?

单是ps命令的输出就足够了吗?

如何在不同风格的Unix中实现这一点?


  • 查找当前shell可执行文件的名称有三种方法:

    请注意,如果shell的可执行文件是/bin/sh,所有3种方法都会被愚弄,但它实际上是一个重命名的bash,例如(这种情况经常发生)。

    因此,关于ps输出是否可行的第二个问题的答案是"不总是"。

  • echo $0将打印程序名…在壳的情况下,它是实际的壳

  • ps -ef | grep $$ | grep -v grep—这将在正在运行的进程列表中查找当前进程ID。由于当前进程是shell,因此将包括它。

    这不是100%可靠的,因为您可能有其他进程,它们的ps列表包含与shell进程ID相同的数字,特别是如果该ID是一个小的(例如,如果shell的PID是"5",您可以在同一grep输出中找到名为"java5"或"perl5"的进程!)这是"ps"方法的第二个问题,除了不能依赖shell名称。

  • echo $SHELL—当前shell的路径存储为任何shell的SHELL变量。对于这个问题,需要注意的是,如果将shell显式地作为子进程(例如,它不是您的登录shell)启动,那么您将获得登录shell的值。如果可能的话,使用ps$0方法。

  • 但是,如果可执行文件与实际shell不匹配(例如,/bin/sh实际上是bash或ksh),则需要启发式方法。以下是特定于各种外壳的一些环境变量:

    • TCSH上设置了$version

    • $BASH设置在bash上

    • 在csh或tcsh中将EDOCX1(小写)设置为实际shell名称

    • 在zsh上设置$ZSH_NAME

    • ksh有$PS3$PS4套,而普通bourne shell(sh套)只有$PS1$PS2套。这似乎是最难区分的——我们在Solaris Boxen上安装的shksh的整个环境变量集合的唯一区别是$ERRNO$FCEDIT$LINENO$PPID$PS3$PS4$RANDOM$SECONDS$TMOUT


ps -p $$

应适用于涉及ps -efgrep的解决方案的任何地方(在支持ps的posix选项的任何unix变体上),并且不会受到对可能出现在其他地方的数字序列进行grepping所引入的误报的影响。


尝试

1
ps -p $$ -oargs=

1
ps -p $$ -ocomm=


如果您只想确保用户使用bash调用脚本:

1
if [ ! -n"$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi


你可以试试:

1
ps | grep `echo $$` | awk '{ print $4 }'

或:

1
echo $SHELL


$shell不必总是显示当前外壳。它只反映要调用的默认shell。

要测试以上内容,假设bash是默认shell,尝试echo $SHELL,然后在同一终端,进入其他shell(例如ksh)并尝试$shell,在这两种情况下,结果都是bash。

要获取当前shell的名称,请使用cat /proc/$$/cmdlinereadlink /proc/$$/exe可执行的shell路径。


PS是最可靠的方法。不能保证设置shell envar,即使设置了shell envar,也很容易被欺骗。


我有一个简单的技巧来找到当前的外壳。只需键入一个随机字符串(它不是命令)。它将失败并返回一个"未找到"错误,但在行首它将指出它是哪个shell:

1
2
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found


这将始终给出实际使用的shell—获取实际可执行文件的名称,而不是用于/bin/sh的shell名称(即ksh93而不是ksh等),将显示实际使用的shell:即dash

1
ls -l /proc/$$/exe | sed 's%.*/%%'

我知道这里有很多人说ls输出应该被更新处理,但是您使用的shell用特殊字符命名或放在用特殊字符命名的目录中的可能性有多大?如果仍然是这样的话,这里还有很多其他不同的例子。


我的变体打印父进程。

1
ps -p $$ | awk '$1 == PP {print $4}' PP=$$

为什么运行不必要的应用程序,而"awk"可以为您做到这一点?


我尝试过许多不同的方法,对我来说最好的方法是:

ps -p $$

它也在Cygwin下工作,不能产生误报,因为PID变灰。通过一些清理,它只输出一个可执行文件名(在cygwin with path下):

1
ps -p $$ | tail -1 | awk '{print $NF}'

您可以创建一个函数,这样就不必记住它:

1
2
3
4
# print currently active shell
shell () {
  ps -p $$ | tail -1 | awk '{print $NF}'
}

…然后执行shell

在Debian和Cygwin下测试。


如果您的/bin/sh支持POSIX标准,并且您的系统安装了lsof命令,那么在这种情况下,可以使用pid2path作为lsof的一种可能替代方法,您也可以使用(或改编)以下打印完整路径的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/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
"
"lsof failed""try: sudo lsof -p \`ps -p \$\$ -o ppid=\`"; exit 1; }

printf"%s
"
"${lsofstr}" |
   LC_ALL=C awk -v var="${awkstr}" '$NF ~ var {print $NF}'


1
2
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' #use the PID to see what the process is.

来自http://www.unix.com/unix-dummies-questions-answers/10390-how-do-you-know-what-your-current-shell.html


在Mac OS X上(&;freebsd):

1
ps -p $$ -axco command | sed -n '$p'


如果您只想检查是否正在运行(特定版本的)bash,最好的方法是使用$BASH_VERSINFO数组变量。作为(只读)数组变量,无法在环境中设置它,所以你可以确定它是从当前外壳来的(如果有的话)。但是,由于bash作为sh调用时有不同的行为,您还需要检查$BASH环境变量以/bash结尾。

在我编写的一个脚本中,它使用了带有-的函数名(不是下划线)并且取决于关联数组(添加到bash 4中),我进行了以下健全性检查(带有有用的用户错误消息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

在第一种情况下,您可以省略对特性的一些偏执的功能检查,假设未来的bash版本是兼容的。


你要知道,无论你是使用破折号,壳/ bash。

1)LS–la /bin/sh,如果result is /bin/sh > /bin/bash==>然后你使用bash shell是。

如果result is /bin/sh>//==>则是仪表的壳体是用破折号。

如果你想更改到Dash Bash或反之亦然,使用下面的代码LN S /bin/bash /bin/sh(改变一个bash shell)

注:如果上面的命令结果的误差在A说,/bin/sh已经存在,删除"/bin/sh和再试一次。


没有一个答案适用于fishshell(它没有变量$$$0)。

这对我来说是可行的(在shbashfishkshcshtruetcshzsh上测试;opensuse 13.2):

1
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'

这个命令输出类似于bash的字符串。这里我只使用pstailsed(没有GNU扩展;尝试添加--posix来检查它)。它们都是标准的POSIX命令。我确信可以移除tail,但我的sedfu不够强大。

在我看来,这个解决方案不是非常可移植的,因为它在OS X上不起作用。(


不需要从"ps"的输出中提取pid,因为您可以从/proc目录结构中读取任何pid的相应命令行:

1
echo $(cat /proc/$$/cmdline)

然而,这可能并不比简单地:

1
echo $0

关于运行实际不同于名称所指示的shell,一个想法是使用以前获得的名称从shell请求版本:

1
<some_shell> --version

sh似乎失败了,退出代码为2,而其他人给出了一些有用的东西(但我无法验证所有的东西,因为我没有它们):

1
2
3
4
$ sh --version
sh: 0: Illegal option --
echo $?
2

这不是很干净的解决方案,但可以满足您的需要。

我知道答案在这个美好的2015年有点晚了,但是…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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

现在你可以使用$(getshell) --version.

不过,这种方法只适用于像ksh这样的外壳。


我的解决方案:

1
ps -o command | grep -v -e"\<ps\>" -e grep -e tail | tail -1

这应该是在不同的平台和便携式外壳。它使用ps样其他的解决方案,但它不依赖于sedawk出垃圾和从管道和过滤器外壳,这样ps本身应该永远是最后的入口。这种方式,我们不需要依靠非便携式PID变或"系右行和列。

我已经测试了在Debian和MacOS和bash和鱼(zsh),它不适合与大多数研究没有具体的解决方案,因为它表达的鱼,使用不同的控制变量)。


有许多的方式找出壳和其相应的版本。这里是一个为我工作的。

直向前

  • 回声$ > 0美元(这个程序的名字。在我的案例是bash)输出
  • 美元美元(这需要你到壳壳壳的提示您的名字和版本。在我的案例bash3.2美元)
  • 回声$美元(这将给你的壳可执行路径。在我的案例/bin/bash)
  • shell版美元美元(本想给一个完整的软件许可证信息壳型)
  • hackish方法

    美元* * * * * * *(A型随机字符集和输出你想要得到的壳的名字。在我的案例:一chapter2 bash命令示例应用程序不存在同构):


    请使用以下命令:

    1
     # ps -p $$ | tail -1 | awk '{print $4}'


    这一作品也在RHEL,MacOS,BSD和一些aixes

    1
    ps -T $$ | awk 'NR==2{print $NF}'

    也因此,如果pstree以下工作应该是可用的,

    1
    pstree | egrep $$ | awk 'NR==2{print $NF}'