如何确定我正在使用的当前shell


632

如何确定正在使用的当前Shell?

将输出 ps仅命令就足够了吗?

如何在Unix的不同版本中完成此操作?


9
测试特定功能(例如,是否进行!替代?)可能比查找外壳名称更容易携带。本地自定义设置可能会让您运行某些名称/bin/sh,该名称实际上可能是灰,破折号,bash等
。– msw

1
@ msw:似乎像一个很好的注释,除了它让我想知道“如何?”。
nobar 2012年

看来,这个问题没有简单的答案。如果我们无法查询外壳,也许更好的方法是始终指定外壳。我不确定这是否总是可能的,但是也许比人们通常认为的要容易得多。
nobar 2012年


@Aniket,没有您想像的那么多帮助-只对交互式 shell进程感兴趣。
Toby Speight

Answers:


793
  • 有三种方法可以找到当前shell可执行文件的名称

    请注意,如果外壳程序的可执行文件是/bin/sh,则可以欺骗所有这三种方法,但是实际上,它实际上是重命名的bash(经常发生)。

    因此,您的第二个问题,即ps输出是否会用“ 并非总是 ” 回答。

    1. echo $0 -将打印程序名称...如果是外壳,则为实际外壳。

    2. ps -ef | grep $$ | grep -v grep-这将在正在运行的进程列表中查找当前进程ID。由于当前进程是外壳程序,因此将包含它。

      这不是100%可靠的,因为您可能还有其他进程的ps列表包含与Shell的进程ID相同的数字,尤其是如果该ID是一个小数字(例如,如果Shell的PID为“ 5”,则您可能会发现称为“ java5”或“ perl5”在同一grep输出中!)。这是“ ps”方法的第二个问题,除了无法依赖shell名称之外。

    3. echo $SHELL-当前外壳程序的路径存储SHELL为任何外壳程序的变量。需要注意的是,如果您明确启动外壳程序作为子进程(例如,它不是您的登录外壳程序),那么您将获得登录外壳程序的值。如果有可能,请使用ps$0方法。


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

    • $version 在tcsh上设置

    • $BASH 受到重击

    • $shell (小写)设置为csh或tcsh中的实际shell名称

    • $ZSH_NAME 在zsh上设置

    • ksh具有$PS3and $PS4set,而普通的Bourne shell(sh)仅具有$PS1and $PS2set。这通常似乎是最难区分-在唯一之间整套环境变量的差异shksh我们已经安装在Solaris上Boxen有是$ERRNO$FCEDIT$LINENO$PPID$PS3$PS4$RANDOM$SECONDS,和$TMOUT


$ {。sh.version}设置在ksh93
fpmurphy

14
ps -p $$正如Matthew Slattery指出的那样。对于kshecho $KSH_VERSIONecho ${.sh.version}
暂停,直到另行通知。

@Dennish-我的ksh现在没有设置KSH_VERSION。并echo ${.sh.version}返回“错误替换”。请参阅上方的解决方案
DVK 2010年

3
ps -ef | grep ……这不是100%可靠的……” 使用一个简单的正则表达式可以通过egrepgrep -e轻松地将可靠性提高到100%的意图和目的ps -ef | egrep "^\s*\d+\s+$$\s+"。在^确保我们从行的开头开始,\d+吃了UID,则$$该PID匹配,并且\s*\s+账户及保证其他部分之间的空白。
Slipp D. Thompson

2
@ SlippD.Thompson在GNU / Linux中不起作用。但这似乎可行:ps -ef | awk '$2==pid' pid=$$
jarno

98

ps -p $$

应该可以在解决方案涉及ps -efgrep执行的任何地方工作(在任何支持POSIX选项的ps Unix变体上),并且不会遭受grepping可能出现在其他地方的数字序列引起的误报。


12
有些shell有其自己的内置版本ps,可能无法理解,-p因此您可能需要使用/bin/ps -p $$
暂停,直到另行通知。

12
我熟悉的所有shell都可以理解,$$除了fish必须使用的外壳ps -p %self
暂停,直到另行通知。

3
实际上,您不应该依赖诸如的艰难路径/bin/psps可以很容易地安装(实际上,如今已经很正常了)/usr/bin$(which ps) -p $$是更好的方法。当然,这不适用于鱼,甚至可能还有其他贝壳。我认为是(which ps) -p %self鱼里的。
Aron Cederholm 2014年

1
在像debian-slim docker容器这样的最小系统中,ps可能不存在。在那种情况下,这种方法仍然有效:readlink /proc/$$/exe
mxmlnkn19年

如果您sh的模仿对象是bash,ps -p /usr/bin/bash甚至可以将您的运行方式显示为sh
Ding-Yi Chen

45

尝试

ps -p $$ -oargs=

要么

ps -p $$ -ocomm=

4
这是一个不错的短篇。我在用自己ps -o fname --no-headers $$
Anne van Rossum 2014年

谢谢。我发现这是在脚本中保护bash特定命令的最佳选择test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash。(脚本的下一行与csh等效。)
craq 2014年

1
我发现,如果从子shell内执行此操作,则通过匹配父级的PID和实际的shell进程,可能导致多余的额外行。为此,我使用的-q不是-pSHELL=$(ps -ocomm= -q $$)
Steve

35

如果只想确保用户正在使用Bash调用脚本,请执行以下操作:

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

1
这应该是离顶部更近的那个。非常感谢。
Alex Skrypnyk

4
它不应该接近顶部,因为它根本无法回答问题。如果问题是“如何检查脚本是否在bash下运行”,我投赞成票。
David FerenczyRogožan2014年

2
@DawidFerenczy-但是,当您搜索该短语时,此问题是最高的结果。从长远来看,我认为答案能回答人们正在寻找的东西比回答最初的问题要重要得多。
ArtOfWarfare

3
此外,变量$ BASH 如果tcsh的是从bash的调用tcsh下定义
18446744073709551615

这非常有用,谢谢。稍微修改一下,将其放在#!/bin/bash:之后的第二行if [ ! -n "$BASH" ] ;then exec bash $0; fi。在此行中,即使使用ksh或sh开始,该脚本也使用bash运行。我的用例不需要命令行参数,但$0如有必要,可以在后面添加它们。
joanis

20

你可以试试:

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

要么:

echo $SHELL

6
grep跟awk的/pattern/ { action }关系是什么,什么时候可以呢?
Jens

#in zshell alias shell ='echo $ {SHELL:t}'
SergioAraujo 2014年

4
$SHELL环境变量包含一个外壳程序,该外壳程序被配置为当前用户的默认设置。它不反映当前正在运行的外壳。而且,ps -p $$由于误报,使用它比删除$$ 更好。
David FerenczyRogožan2014年

$ SHELL环境。变量指向POSIX规范中指定的“父”外壳: SHELL此变量应表示用户首选的命令语言解释器的路径名。因此,$ SHELL的值可能不是当前的shell。
Theo

所有内awkps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
go2null

16

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

要测试以上内容,请说bash是默认的shell,请尝试echo $SHELL,然后在同一终端中,进入其他外壳程序(例如KornShell(ksh))并尝试$SHELL。在两种情况下,您都将看到结果为bash。

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


8
...只要你有/proc
Tripleee 2013年

10

ps是最可靠的方法。不能保证设置SHELL环境变量,即使已设置,也很容易被欺骗。


12
+1 $ SHELL是需要生成一个的程序的默认外壳程序。它不一定反映当前正在运行的shell。
吉姆·刘易斯

8

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

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

3
脚本不好。echo 'aaaa' > script; chmod +x script; ./script./script.sh: 1: aaaa: not found
苗条的

6

以下内容将始终给出使用的实际外壳程序-它获取实际可执行文件的名称,而不是外壳程序名称(即ksh93代替ksh,等等)。对于/bin/sh,它将显示实际使用的外壳,即dash

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

我知道有很多人说 ls应对输出进行处理,但是您使用的外壳用特殊字符命名或放在以特殊字符命名的目录中的可能性是多少?如果仍然是这种情况,那么还有许多其他的方法可以做到这一点。

正如Toby Speight所指出的那样,这将是实现这一目标的一种更合适,更清洁的方法:

basename $(readlink /proc/$$/exe)

3
这只是对所有不提供的Unices的错误/proc。并非全世界都是Linux机器。
延斯2014年

5
如果你在Linux上,我们宁愿basename $(readlink /proc/$$/exe)ls+ sed+ echo
Toby Speight

1
这将给出实际的可执行文件的名称,而不是实际的shell。当实际的外壳程序链接为busybox小程序时,例如ash -> /bin/busybox,这将给出/ bin / busybox。
stepe

6

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

ps -p $$

它也可以在Cygwin运行,并且不能作为PID grepping产生误报。经过一些清理,它仅输出一个可执行文件名称(在Cygwin下带有路径):

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

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

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

...然后执行shell

它已在Debian和Cygwin下进行了测试。


在我的设置(Cygwin | Windows 7)中,您的答案是最好的答案,而 ps -p $$ | 尾巴-1 | gawk'{print $ NF}' 即使在没有$ bash的cmd中也可以工作。请注意gawk而不是awk,因为awk仅可用于bash。
WebComer

@WebComer对不起,我不确定您的意思是“ 即使在没有$ bash的cmd上也可以工作 ”。即使您有Windows端口pstailgawk,cmd也未定义$$为PID,因此它绝对不能在普通cmd下运行。
David FerenczyRogožan,19年

为什么不简单ps -p$$ -o comm=?POSIX表示将所有标头指定为空会完全抑制该标头。ps当我们从直接执行的脚本(例如#!/bin/sh)获得资源时,我们仍然会失败(就像所有答案一样)。
Toby Speight

5

我在打印父进程时的变体:

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

当AWK可以帮您完成时,请不要运行不必要的应用程序。


2
为什么运行不必要的awk,何时ps -p "$$" -o 'comm='可以为您完成?
Toby Speight

5

有很多方法可以找到外壳及其对应的版本。这里有一些对我有用的东西。

直截了当

  1. $> echo $ 0(为您提供程序名称。在我的情况下,输出为 -bash。)
  2. $> $ SHELL(这将带您进入shell,并在提示符下获得shell名称和版本。 bash3.2 $。)
  3. $> echo $ SHELL(这将为您提供可执行路径。 / bin / bash。)
  4. $> $ SHELL --version(这将提供有关具有许可证类型的Shell软件的完整信息)

骇人的方法

$> *******(输入一组随机字符,然后在输出中得到外壳名称。在我的情况下,-bash:Chapter2-a-sample-isomorphic-app:命令未找到


3

如果您/bin/sh支持POSIX标准,并且您的系统已lsof安装命令(lsof在这种情况下可以替代该命令pid2path),则还可以使用(或改编)以下打印完整路径的脚本:

#!/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}'

1
这对鱼有用!但是对我来说,由于-i(-> ignore environment)选项env在您检查lsof是否可用的行中而无法执行bash 。它失败:env -i PATH="${PATH}" type lsof->env: ‘type’: No such file or directory
hoijui

2

如果您只是想检查自己是否正在运行Bash(特定版本),最好的方法是使用 $BASH_VERSINFO数组变量。作为(只读)数组变量,不能在环境中设置它,因此可以确保它(如果有的话)来自当前shell。

但是,由于Bash在按as调用时具有不同的行为sh,因此您还需要检查$BASH环境变量以结尾/bash

在我写的脚本中,函数名称与-(而不是下划线)一起使用,并且依赖于关联数组(在Bash 4中添加),我具有以下的健全性检查(带有有用的用户错误消息):

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版本将兼容。


2

没有任何答案适用于fishShell(它没有变量$$$0)。

这对我的作品(测试上shbashfishkshcshtruetcsh,和zsh; openSUSE的13.2):

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

此命令输出类似的字符串bash。在这里,我仅使用pstailsed(没有GNU扩展;尝试添加--posix以对其进行检查)。它们都是标准的POSIX命令。我敢肯定tail可以将其删除,但是我的sed功夫不足以做到这一点。

在我看来,该解决方案不是很可移植,因为它不能在OS X上使用。


sed: invalid option -- 'E'上bash 3.2.51和tcsh 6.15.00
craq 2014年

2

我的解决方案:

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

这应该可以跨不同平台和外壳移植。它ps与其他解决方案一样使用,但是它不依赖sedawk从管道及其ps本身中过滤掉垃圾,因此外壳应始终是最后一个条目。这样,我们就不必依赖不可移植的PID变量或选择正确的行和列。

我已经在Debian和macOS上使用Bash,Z shellzsh)和fish进行了测试(在不更改鱼的专用表达式的情况下,这些解决方案不适用于大多数解决方案,因为它使用了不同的PID变量)。



1

在Mac OS X(和FreeBSD)上:

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

2
在使用时尝试zsh了一下,它给了我-bash
user137369

在我的系统(现在)上,经过bash和dash测试,返回以下内容mutt::
F. Hauri

1

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

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

但是,这可能不仅仅是简单地更好:

echo $0

关于运行与名称实际不同的外壳程序,一种想法是使用先前获得的名称向外壳程序请求版本:

<some_shell> --version

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

$ sh --version
sh: 0: Illegal option --
echo $?
2

1

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

# MUST BE SOURCED..
getshell() {
    local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"

    shells_array=(
    # It is important that the shells are listed in descending order of their name length.
        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

但是,这仅在类似KornShell的shell(ksh)上有效。


1
“不过,这仅在类似ksh的外壳上有效。” 您是说我必须运行此程序之前验证外壳程序吗?嗯...
jpaugh

@jpaugh,名单必须由壳这个项目的代码,这是不是对的情况下得到支持dashyash等等。通常情况下,如果你正在使用bashzshksh,不管是谁-你不应该在乎这样的事情。
theoden8

因此,您将bash视为“类似ksh”吗?得到它了。这更有意义
jpaugh

@jpaugh,好吧,这就是我的意思,因为ksh的功能集主要是的功能的子集bash(尽管尚未对此进行彻底检查)。
theoden8

1

执行以下操作,了解您的外壳是否正在使用Dash / Bash。

ls –la /bin/sh

  • 如果结果为/bin/sh -> /bin/bash==>,则说明您的shell正在使用Bash。

  • 如果结果为/bin/sh ->/bin/dash==>,则说明您的Shell使用的是Dash。

如果要从Bash更改为Dash,反之亦然,请使用以下代码:

ln -s /bin/bash /bin/sh (将shell更改为Bash)

注意:如果以上命令导致错误提示/ bin / sh已经存在,请删除/ bin / sh并重试。


0

我想出了这个

sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ

这仅适用于Linux!不是MacOS,也不是BSD!
F. Hauri

-1

请使用以下命令:

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

第一个是胡说八道,echo $SHELL做您想做的并且做得很好。第二个也不是很好,因为$SHELL环境变量包含当前用户的默认外壳程序,而不是当前正在运行的外壳程序。例如,如果我将其bash设置为默认外壳,请执行zshecho $SHELL,它将显示bash
David FerenczyRogožan2014年

您是正确的Dawid Ferenczy,我们可以使用ps命令来确定当前的shell。[#ps -p $$ | 尾-1 | awk'{print $ 4}']。
Ranjithkumar T 2014年

echo $SHELL可能是垃圾: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
SaltwaterC

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.