736

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

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

如何在不同风格的 Unix 中做到这一点?

4

28 回答 28

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

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

    因此,您的第二个关于输出是否可行的问题是用“不总是ps”来回答的。

    1. echo $0- 将打印程序名称......在外壳的情况下是实际的外壳。

    2. ps -ef | grep $$ | grep -v grep- 这将在正在运行的进程列表中查找当前进程 ID。由于当前进程是shell,所以会包含在内。

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

    3. echo $SHELL- 当前 shell 的路径存储SHELL为任何 shell 的变量。对此的警告是,如果您将 shell 显式启动为子进程(例如,它不是您的登录 shell),您将获得登录 shell 的值。如果有可能,请使用psor$0方法。


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

    • $version在 tcsh 上设置

    • $BASH设置在 bash

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

    • $ZSH_NAME在 zsh 上设置

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

于 2010-07-24T21:37:34.633 回答
117

ps -p $$

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

于 2010-07-24T22:09:37.047 回答
51

尝试

ps -p $$ -oargs=

或者

ps -p $$ -ocomm=
于 2012-06-19T08:59:32.270 回答
40

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

if [ -z "$BASH" ] ;then echo "Please run this script $0 with bash"; exit 1; fi
于 2013-03-31T16:54:15.197 回答
24

你可以试试:

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

或者:

echo $SHELL
于 2010-07-24T21:38:44.137 回答
21

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

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

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

于 2012-10-15T11:45:43.417 回答
13

有很多方法可以找出 shell 及其对应的版本。这里有一些对我有用的。

直截了当

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

骇人听闻的方法

$> ******* (输入一组随机字符,在输出中您将获得 shell 名称。在我的情况下-bash: chapter2-a-sample-isomorphic-app: command not found

于 2017-09-19T07:30:27.827 回答
11

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

于 2010-07-24T21:39:39.810 回答
11

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

ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
于 2015-12-09T15:34:54.403 回答
7

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

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 下进行了测试。

于 2014-10-14T11:57:50.520 回答
6

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

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

我知道有很多人说ls永远不应该处理输出,但是您使用的外壳以特殊字符命名或放置在以特殊字符命名的目录中的概率是多少?如果情况仍然如此,那么还有很多其他不同做法的例子。

正如Toby Speight所指出的,这将是实现相同目标的更合适和更清洁的方法:

basename $(readlink /proc/$$/exe)
于 2014-06-16T22:44:25.323 回答
5

我打印父进程的变体:

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

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

于 2015-08-25T13:37:28.490 回答
4

假设您/bin/sh支持 POSIX 标准并且您的系统安装了命令 -在这种情况下lsof可能是替代方法- 您还可以使用(或调整)以下打印完整路径的脚本:lsofpid2path

#!/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}'
于 2013-03-20T17:30:04.863 回答
3

我的解决方案:

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

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

我已经在 Debian 和 macOS 上使用 Bash、Z shell ( zsh) 和fish进行了测试(如果不更改专门针对 fish 的表达式,它不适用于大多数这些解决方案,因为它使用不同的 PID 变量)。

于 2017-05-17T23:13:09.367 回答
2

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

但是,由于 Bash 在调用为 时具有不同的行为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 版本将是兼容的。

于 2014-08-11T17:38:09.670 回答
1

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

这对我有用(在sh, bash, fish, ksh, csh, true, tcsh, 和zsh; openSUSE 13.2 上测试):

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

此命令输出一个字符串,如bash. 这里我只使用ps, tail, and sed(没有 GNU 扩展;尝试添加--posix以检查它)。它们都是标准的 POSIX 命令。我确定tail可以移除,但我的sedfu不够强大,无法做到这一点。

在我看来,这个解决方案不是很便携,因为它在 OS X 上不起作用。:(

于 2014-09-13T09:07:50.537 回答
1
echo $$ # Gives the Parent Process ID 
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.

你怎么知道你当前的外壳是什么?.

于 2010-07-24T21:41:25.477 回答
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)。

于 2015-07-09T18:42:56.277 回答
1

执行以下操作以了解您的 shell 是否使用 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(将外壳更改为 Bash)

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

于 2017-12-14T08:12:15.190 回答
1

我特别喜欢 Nahuel Fouilleul 的解决方案,但我必须在带有内置 Bash shell 的 Ubuntu 18.04 上运行它的以下变体:

bash -c 'shellPID=$$; ps -ocomm= -q $shellPID'

没有临时变量shellPID,例如:

bash -c 'ps -ocomm= -q $$'

只会ps为我输出。也许您并不都使用非交互模式,这会有所不同吗?

于 2021-03-24T17:50:52.463 回答
0

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

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

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

echo $0

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

<some_shell> --version

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

$ sh --version
sh: 0: Illegal option --
echo $?
2
于 2014-10-30T18:16:40.280 回答
0

在 Mac OS X(和 FreeBSD)上:

ps -p $$ -axco command | sed -n '$p' 
于 2010-07-25T18:02:39.813 回答
0

一种方法是:

ps -p $$ -o exe=

这比使用-o args-o comm在另一个答案中建议的 IMO 更好(这些可能使用例如一些符号链接,例如当/bin/sh指向某些特定外壳时作为破折号或 bash)。

以上返回可执行文件的路径,但请注意,由于 /usr-merge,可能需要检查多个路径(例如/bin/bash /usr/bin/bash

另请注意,上述内容不完全兼容 POSIX(POSIX ps 没有exe)。

于 2021-06-09T01:13:32.657 回答
0

为什么不使用$SHELLvar 获取它,一个简单的 sed 可以删除路径:

$ echo $SHELL | sed -E 's/^.*\/([aA-zZ]+$)/\1/g'
bash

在 MacOS、Ubuntu、CentOS 上测试

于 2021-06-03T09:52:36.947 回答
-1

请使用以下命令:

ps -p $$ | tail -1 | awk '{print $4}'
于 2013-10-22T10:26:08.373 回答
-1

您可以使用echo $SHELL|sed "s/\/bin\///g"

于 2021-10-10T18:39:38.423 回答
-1

我想出了这个

sed 's/.*SHELL=//; s/[[:upper:]].*//' /proc/$$/environ
于 2020-02-05T09:52:19.800 回答
-2

这个在Red Hat Linux (RHEL)、macOS、BSD 和一些AIX上运行良好:

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

或者,如果pstree可用,以下一个也应该工作,

pstree | egrep $$ | awk 'NR==2{print $NF}'
于 2017-12-11T16:38:00.717 回答