我如何检查我正在使用哪个shell?

我读到终端只是一个壳,Unix提供了不同风格的壳:
- Bourne shell (sh) - C shell (csh) - TC shell (tcsh) - Korn shell (ksh) - Bourne Again shell (bash)
问题:
- 当我打开一个终端窗口时,默认打开哪个壳? - 如何检查已安装了多少个壳? - 如何更改我账户使用的壳?

7如何确定我正在使用的当前shell?在stackoverflow.com上的这个问题上有相关讨论。 - αғsнιη
@KasiyA 这也与http://askubuntu.com/questions/87853/what-is-default-shell-for-terminal有关。 - d a i s y
请注意,终端是与shell交互的界面(在某个时候实际上曾经是物理界面),而shell并不是终端 - 它是一个命令解释器。详见https://askubuntu.com/a/640105/295286。 - Sergiy Kolodyazhnyy
9个回答

你可以在终端中输入以下命令来查看你正在使用的shell:
echo $0

如果您使用的是bash(Bourne Again Shell)终端,结果将类似于下面的样子:
-bash

130是一个环境变量吗? - Mi_Onim
70@Mi_Onim $0 是正在运行的进程的名称。如果你在shell中使用它,它将返回shell的名称。如果你在脚本中使用它,它将是脚本的名称。 - kingmilo
2注意:这对于交互式的 csh 是不起作用的。% echo $0 No file for $0.,但对于 tcsh 是起作用的。 - Sergiy Kolodyazhnyy
8如果一个符号链接,就不能工作。比如将bash作为sh使用。 - Anwar
@Serg 有没有办法找到 csh?还是这是设计上的限制? - asgs
14@Anwar说得很对,从echo $0中获取shell名称后,执行ls -l `which <name>` 来查看它是否链接到另一个shell二进制文件。 - JivanAmara
2无法在 su - user 上工作。 - Luis Muñiz
2这是一个不好的解决方案。一个程序的名称可以有很多种方式来改变。 - Daniel Santos
5不适用于 Fish Shell。 - masukomi
在一个名为myfunc的shell函数中访问时,$0对于bash来说是bash,而对于zsh来说是myfunc - frozen-flame
这是一个脆弱的解决方案,会在引用的脚本中出现问题。 - Alexander Revo
@AlexanderRevo 嗯,我猜这样说也好,问题并不是围绕“来源”的脚本。;) - kingmilo
1@kingmilo 虽然从技术上讲你没有错,但仅适用于特定情况的解决方案应该附有免责声明。 - Alexander Revo
@AlexanderRevo 我理解你的观点,但提问问题的用户和接受我的答案的用户是同一个人,我对此没有控制权。如果用户对答案满意,那么这个论坛的整个目的就已经实现了。如果你决定给答案打分低一些,那没关系,如果你不同意的话,鼓励这样做。但是我对你的评论表示异议。你要求我在我的答案中加上免责声明,然而你提交的评论与问题/答案的背景完全不符。希望你能看到其中的讽刺之处 =] - kingmilo
1@马苏科米 对于 Fish Shell,请查看下面的下一个答案,推荐使用 echo $SHELL 或检查进程ID。 - Mike B
这在执行source命令时不适用于zsh。为了确认这一点,请尝试创建名为foo.sh的文件,其中包含以下内容:echo $0,然后创建名为main.sh的文件,其中包含以下内容:echo $0 && source ./foo.sh。执行zsh main.sh的输出应为zsh.sh\n./foo.sh,这就是我说$0zsh中无效的原因。 - doltes
@mblakesley 这对于非 POSIX shell(如 fish)是不起作用的 - 而且 echo $SHELL 与您实际运行的 shell 没有任何关联。 - mmmmmm
值得注意的是,如果你安装了多个Bash(例如在默认情况下附带过时Bash的macOS上很常见),那么这种方法无法显示你正在运行的是哪个版本 - 如果你需要知道这一点,我刚刚添加了一个可以做到的答案。 - MikeBeaton
这是在ArchLinux上显示给我的:/usr/bin/zsh - Ahmad Moghazi

要找到默认环境中使用的shell,您可以检查SHELL环境变量的值。
echo $SHELL

要找到当前的shell实例,可以查找具有当前shell实例PID的进程(shell)。
要找到当前shell实例的PID:
echo "$$"

现在要找到具有PID的进程:
ps -p <PID>

组合起来:
ps -p "$$"

52$SHELL 是系统(或用户)的默认 shell,通常(但不一定)是实际上在任何给定时刻使用的 shell。 - kingmilo
1@kingmilo:没错,答案已修改。 - heemayl
当通过'su - <username>'切换到用户时,我发现这非常有用,例如用于设置postgres、jenkins等。 - bully
3echo $SHELL з»ҷеҮәдәҶ /bin/cshпјҢиҖҢ ps -p $$ з»ҷеҮәдәҶ 22673 pts/1 00:00:00 bashгҖӮKingmiloеңЁдёҠйқўи§ЈйҮҠдәҶе®ғ们дёҚеҗҢзҡ„еҺҹеӣ гҖӮ - 18446744073709551615
@18446744073709551615 我并没有说它们是一样的。请再仔细阅读回答,注意“默认”和“当前”的措辞。 - heemayl
@heemayl 我已经做了。当然,当代码产生意外结果时,人们会阅读其附近的文本。这是合乎逻辑的。 - 18446744073709551615
用于启动新会话的Shell脚本 - Marcello de Sales
4为了在gnu和bsd ps版本之间实现可移植性,仅获取当前shell的shell名称,可以使用以下方法:ps -cp "$$" -o command="" - James Tomasino
1谢谢 @James Tomasino,那个方法最有效! - Robert Houghton
1“$$”不是fish shell中的进程ID,而是“$fish_pid”。Fish会告诉你这一点,但如果你不知道你正在使用fish,这将成为一个令人讨厌的错误,使得这种特定方法不可靠来确定你的shell。 - masukomi
有趣的是,我执行了 kill $(echo "$$"),但我的shell仍在运行。 - theX
在命令行上执行"./test.sh"将会显示"test.sh"作为被调用的命令。然而,在这种情况下,解决方案并没有真正起作用。 - Alexander Stohr
@masukomi 提到鱼会让你知道“$$”不是pid; 但在我的fish版本中,它说fish: $$ is not the pid. In fish, please use %self. 不过至少那时,我知道我正在使用fish shell,而不是bash,因为 echo $SHELL 会让我相信这一点。 - hlongmore
不适用于Fish Shell。 - Sergei G
当我在GHCI REPL中运行ps -cp "$$" -o command=""作为一个shell命令时,我得到的输出是ps而不是它所使用的shell。 - Miguel Guthridge

$SHELL 给出默认的 shell。 $0 给出当前的 shell。

例如:我默认使用 bash 作为我的 shell,用于终端应用程序。但对于 iTerm2 应用程序,我在打开窗口时使用命令:/bin/ksh

所以我的 $0 在 iTerm2 上给我 /bin/ksh$SHELL 在 iTerm2 上给我 /bin/bash$0$SHELL 在 Terminal 上给我 /bin/bash


2这是对其他得票最多的答案进行了很好的澄清。谢谢你。 - Michael Hoffmann
1对我来说,我已经安装了zsh,并且它是当前的默认终端。要切换到bash,我只需要在终端中输入bash。要切换回zsh,只需输入zsh即可。 - Yousuf Azad
2你不想一直来回这样做,因为你会在每个壳内部堆叠壳,并且在每个壳内都有一个新的上下文。总的来说,最好是键入ctrl-d或exit返回到前一个壳。 - Phill Apley
最佳答案应该被选择为答案。同意Yousuf Azad关于更改shell的建议,但也同意Phill Apley提到的堆叠问题。我的默认shell也是zsh,但要切换到bash,我只需输入bash,要返回到原来的shell,只需输入exit或按下ctrl + D即可。 - Sanjay

其他答案往往使用特定于shell的功能,但我们正在尝试确定我们正在使用的shell,因此它们假设问题的答案。例如,没有一个答案适用于fish。
sh -c 'ps -p $$ -o ppid=' | xargs ps -o comm= -p

不要在sh的调用中使用$$语法,而是要寻找PPID而不是PID。使用PPID来找到cmd。
sh -c 'ps -p $$ -o ppid=' | xargs -I'{}' readlink -f '/proc/{}/exe'

感谢@muru的改进。

2您可以使用ppid=/cmd=来省略标题(以及tail -1),并考虑查看/proc/.../exe,以查看正在运行的文件(因为cmd输出可能会被运行shell的程序操纵)。 - muru
3显然,在busybox中这个方法不起作用,因为它不符合posix标准。参考链接:https://github.com/broadinstitute/cromwell/pull/3561#pullrequestreview-116186537 - Evan Benn
你的第一个代码块是什么示例? - Andreas
1这个解决方案解决了假设shell的问题,但引入了假设操作系统的问题。macOS(以及可能其他BSD发行版)附带的BSD版本readlink使用-f来指定“格式”,它是一个printf风格的字符串,必须以%开头和结尾,并具有各种模板选项。 - masukomi
很棒的回答,@EvanBenn,谢谢!这里有一个小修改,可以让它在像BusyBox这样不支持ps命令的shell中运行,并且也没有像cut这样的其他实用工具来帮助。代码:set -- $(ps -o pid,ppid | grep -E "^ *$$"); readlink -f "/proc/${2}/exe"。我正在使用你的方法来确定一堆“免费在线Linux shell”网站上使用的shell,效果非常好! - Sean
@Sean 如果你喜欢的话,可以在编辑中添加它! - Evan Benn
我尝试运行@Sean的命令,结果fish对我抱怨了好几个原因。虽然我没有运行BusyBox,但至少它以一种让我知道我正在运行fish的方式进行了抱怨。原始答案中的第二个命令在我基于Ubuntu的系统上有效。 - hlongmore
@EvanBenn, @hlongmore:如果你想支持像fishcsh这样的奇怪的shell,并且仍然想获取父shell的可执行路径(而不是PID),以下方法可以实现:test ! -z "$shell" && set empty && set pid="$$$empty" || test ! -z "$fish_pid" && set pid "$fish_pid" || export pid="$$$empty"; ps -o pid,ppid | grep -E "^ *$pid" | grep -o '[^ ]*$' | xargs -I ppid readlink -f "/proc/ppid/exe" | cat - Sean
如果你想获取当前shell的可执行路径,只需将其更改为:test ! -z "$shell" && set empty && set pid="$$$empty" || test ! -z "$fish_pid" && set pid "$fish_pid" || export pid="$$$empty"; readlink -f "/proc/$pid/exe" | cat - Sean

原帖提出了三个问题。给出的答案确实涵盖了第一个问题:“当我打开终端窗口时,默认打开哪个shell?”它们还回答了一个未被问到的问题,即“如何查找当前在终端中运行的shell?”然而,据我所见,没有人回答最初提出的第二个和第三个问题,即“如何检查安装了多少个shell?”和“如何更改我账户使用的shell?”
要回答“如何检查安装了多少个shell?”这里的命令将列出所有可用的shell:
cat /etc/shells
例如,在Ubuntu 18.10的默认安装中,会得到以下结果:
# /etc/shells: valid login shells /bin/sh /bin/dash /bin/bash /bin/rbash
然而,默认情况下,sh是一个指向dash的符号链接,而rbash是通过选项“-r”(“restricted bash”)与bash关联的,所以实际上只有两个shell,而不像上面的列表中显示的那样有四个。以下命令将显示列出的任何shell是否实际上是符号链接,并且如果是的话它们链接到哪里: ls -l /bin
现在来回答问题:“如何更改我帐户使用的shell?”假设这意味着“如何永久更改终端使用的默认shell”,可以在这里找到答案。

要知道您的用户的默认shell是哪个,您可以运行以下命令:
echo "$SHELL"

例如,如果您正在使用Bash,则应获取以下输出:
/bin/bash

如果您没有更改任何配置,那么默认的shell应该是Bash,因为Bash是Ubuntu上的默认shell。

/bin/sh 是 Shell 命令语言,而不是 Bourne Shell,请修改您的回答。 - kingmilo
@kingmilo 我的回答中没有提到 /bin/sh 的参考。 - kos
在编辑之前,@kol有一个问题。请注意$SHELL是系统(或用户)的默认shell,通常(但不一定)是实际使用的shell。 - kingmilo
@kingmilo 请注意问题是“当我打开终端时,默认打开哪个shell?”而答案是“如果您没有更改任何配置,它应该是Bash,因为这是Ubuntu上的默认shell”,所以答案符合问题。 - kos
1@kingmilo 重新阅读了你的问题,我想我明白你的担忧了,可能我表达得不够清楚。我在我的回答中澄清了“当前”一词的含义。 - kos
使用ps -p "$$"的答案在bash和可能其他shell中有效,但在fish(Friendly Interactive SHell)中无效。在fish中,您应该运行ps -p %self。所以我不确定是否有任何一个命令可以告诉您正在使用的是哪个shell。您可能需要尝试这两个命令,或者可能还需要尝试其他命令来确定。 - frederickjh
2@frederickjh fish在这里有点不同寻常。$$变量实际上是由POSIX定义的,并且在绝大多数的shell中都可以使用。Fish决定在这里不遵循标准,所以我认为可以忽略它。我可以确认,在sh、dash、bash、zsh、ksh、ash、tcsh和csh中,$$按预期工作。事实上,就我所知,除了fish之外,我想不出还有哪个shell不能使用它。 - terdon

你可能不想知道当前shell的名称(例如-bash,bash,zsh等,通过echo $0),也不想知道默认shell的可执行路径(通过echo $SHELL),而是想知道当前shell的可执行路径(特别有用,例如如果你安装了多个版本的Bash)。
要做到这一点,你可以使用lsof -p "$$"或者通过一些额外的编码来提取所需的信息。
lsof -p "$$" | grep -m 1 txt | xargs -n 1 | tail -n 1

通过Homebrew安装的Bash示例输出:
/usr/local/Cellar/bash/5.1.8/bin/bash

或者对于Zsh:
/bin/zsh

上述与echo $SHELL不同,因为上述是针对当前正在运行的shell而不是用户的默认shell,并且上述在扩展任何符号链接后给出可执行文件。例如,对于与上述相同的Bash安装,echo $SHELL给出/usr/local/bin/bash。
编辑:如果需要考虑shell路径中可能存在空格字符,请改用lsof -p "$$" | grep -m 1 txt | xargs -n 1 | tail -n +9 | xargs。

回答你的第三个问题,“如何从我的账户中更改使用的shell?”答案是使用chsh命令。
有两种模式:
1. 交互模式; 2. 非交互模式。
参考Changing Shells - Changing your login shell which is permanent,稍作改述。

You will use a program called chsh. There is a interactive method and non-interactive method. Type the following into your terminal:

INTERACTIVE METHOD

$ chsh

This results in a brief dialog in which the user is prompted first for their password and then for the full path of the desired new shell.

Caution should be exercised when changing one's default shell because it is possible to make an error that only the root user (i.e., system administrator) can repair (although it should be easy for a skilled user to repair it on a home system). In particular, it is important to first test the shell temporarily in the current session and then to make certain that a valid shell name is being entered when making the permanent change.

NON-INTERACTIVE METHOD

I will use csh as again an example.

$ chsh -s /bin/csh

The -s sets it for you without having to go into the editor to do it.

Once this is executed successfully, then echo $SHELL will still say that you are in the same shell as before. However, you need to log out and back in for the change to take effect. Now do echo $SHELL. You should see it shows the new shell.


在我连接的一个服务器上,登录 shell 是 /bin/sh,它是一个指向 /bin/bash 的符号链接。
这里大多数答案会给出 sh,这会让提问者认为它是 Bourne shell 而不是 GNU bash,除了 this one 给出的 /bin/bash
对于这种情况,另一个可行的选项是:
$ echo $SHELL
/bin/sh

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 May 31 16:15 /bin/sh -> bash

$ /bin/sh --help
GNU bash, version 4.2.10(1)
Usage:  /bin/sh [GNU long option] [option] ...
        /bin/sh [GNU long option] [option] script-file ...

可能我刚刚添加的答案也解决了这个问题吗?我不太确定它在跨系统上是否有效,所以很想知道它在你那台服务器上是如何运作的。 :) - MikeBeaton