我如何*可靠地*和*简单地*获取当前的 shell 解释器名称?

我如何*可靠地*和*简单地*获取当前的 shell 解释器名称?

我正在寻找一种简单可靠的方法来从脚本或源文件中获取当前 shell 的名称(不是从命令行)。我本来希望这样做$(basename "$SHELL"),但是如果我的登录 shell 是zsh并且我在 some_script.sh 中有以下代码

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

我用 运行它bash some_script.sh,它仍然列出zsh而不是bash即使使用的解释器是/bin/bash.我不确定为什么 shell 设计者选择显示默认 shell 而不是当前 shell,但这似乎是我们所坚持的。

还有一些其他稍微类似的问题(这里这里),但他们的答案在很多方面都有不足。

  1. 他们经常假设用户正在尝试找出他们当前正在使用的交互式 shell,但这是疯狂的。我知道我要在哪个 shell 中输入命令——我需要可以在任何地方运行的代码来确定哪个 shell正在使用。
  2. 他们经常提供几种不同的东西来尝试——就像你在命令行上一样,可以随意摆弄,直到你知道你正在使用 bash——但我需要一件在所有情况下都可靠的东西。
  3. 他们经常提供脚本中完全无用的东西,例如echo $0.如上面的脚本所示,失败了。 (是的,它可以在交互式 shell 命令行中工作,但为什么您不知道您正在使用什么 shell?)
  4. 他们有时会发出命令(在我有限的测试中)包括正确的信息,如ps -p $$,但缺乏跨平台、跨 shell 兼容的 sed/awk 命令来通过管道获取只是外壳名称并忽略随之而来的其他信息。
  5. 它们包括只能在几个 shell 上运行的东西,比如$BASH_VERSION$ZSH_VERSION。我想支持尽可能多的shell,比如fish,,,cshtcsh

我如何可靠地准确探测任何 当前的壳?我正在寻找能够跨平台、在脚本中以及尽可能多且合理的 shell 工作的东西1

更新:当我发布这个问题时,我预计 shell 中内置了一些设施来为我们提供此信息,但事实似乎并非如此。既然现在看来不可避免地要依赖某些东西在外面贝壳,我应该更明确地说我要的是跨平台解决方案(虽然我对上面其他答案的反对暗示了这一点,但如果您不仔细阅读问题,可能很容易错过)。

更新2 如果有人仍然认为这与仅适用于 Linux 的问题重复,因为 Stéphane 的答案并非仅适用于 Linux,那么以下是我所要求的内容与他提供的内容之间的差异。 (请注意,他写的很巧妙,我不是敲它,但这并不能解决我的问题。)

  1. 我在找东西简单的可靠的, 那
  2. 可以添加到任何脚本或函数定义(将由 a.zshrc.bash_profile或其他任何来源)来分支。
  3. 您不能使用他的脚本作为将解释器名称传递给调用脚本/函数的外部实用程序,因为它始终由默认解释器解释并返回。这使得它很难或不可能用于我的目的。如果可能的话,仍然非常非常困难,而使其发挥作用的解决方案是不是答案中给出。因此他没有回答我的问题,因此它不是重复的。

如果你想看一些东西将要工作,看看GitHub 上的 ShellDetective。这可能会让您更容易看到 SE 上已经存在的内容与该问题正在寻找的内容之间的差异(实际上是为了满足该问题的需求而编写的,而其他任何地方都未满足该需求)。


(PS,如果你不相信有这样的用例,想象一下函数被源到.zshrc.bash_profile、 或.profile取决于正在使用的服务器以及它有哪些可用的 shell。它们是源代码的,所以它们没有 shebang 行。它们如果它们可以在任何 shell 中工作,那么它们是最有用的,但有时它们必须知道它们在哪个 shell 中才能知道如何运行。)


1我不关心那些不是任何传统意义上的贝壳的“贝壳”。我不关心某个人为自己写的一些 shell。我只关心真正的贝壳这实际上是在野外发生的,可以想象有人在登录某些服务器时可能必须使用它,而他们不能在上面安装任何他们想要的 shell。

答案1

我通过这种组合取得了很好的效果:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

在 Tru64 (OSF/1) 上,输出在 shell 两边有括号。粘上tr -d '()'以将其移除。

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

似乎适用于所有 shell、Solaris 10 和 RHEL 5.7 / 6.4。没有测试其他发行版,例如 Debian、Ubuntu 或 Mint,但我认为它们应该都工作相同;我也不知道 FreeBSD 是否可以工作。

在此输入图像描述

答案2

所以我仍在努力充实这一点,但我认为我有一个可行的想法。正如您所注意到的,您想要做的事情即使不是不可能,也是极其难以在所有 shell 中完成的(您添加到多语言的每个变体都会以大于线性的速度增加复杂性)。如果你分成 Bourne(sh、ash、dash、bash、ksh、pdksh、zsh 等)和 c 风格 shell(csh、tcsh、fish 等),你可能可以做到这一点,但csh变体之间的变化呈现出各种有趣的变化挑战。

因此,让我们用已知语言(带有 shebang 行的 bash、C、perl、python 等)编写检测例程,并确定父级可执行文件的名称。然后,我们可以使用两个技巧将信息返回给父级,即返回值和写入标准输出的单个单词。在 sh 下降的 shell 上,我们将返回 0 并写入 shell 的名称,因为它们都有良好的反引号处理。在 C 系列 shell 上,我们可以开始增加需要解决的每组不兼容性的返回值。返回值超过 200 将指示错误,从一切开始似乎都很好,但在 200 时我无法分辨出我的父母是谁。

内部程序在linux上看起来很简单(/proc/ppid/exe成功了一半)。我很确定这可以在 bsd 上使用 ps 选项完成,但我目前没有正在运行的 bsd 系统来测试,而且我的 mac 需要一个新的硬盘(无论如何只有 10.4)。尽管它显着减少了 shell 语法问题,但它确实引入了一组不同的兼容性问题。我仍然认为它有可能性。

答案3

尝试这样的事情

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin

答案4

这个怎么样,抱歉,我没有很多平台可以尝试

local __shell=`ps $$ -o comm=""`

相关内容