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


9

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

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

并且我用bash some_script.sh它运行zshbash即使使用的解释器还是列出了/bin/bash。我不确定为什么外壳设计人员选择显示默认外壳而不是当前外壳,但这似乎就是我们所坚持的。

还有其他一些稍微相似的问题(此处此处),但是它们的答案在许多方面都不够。

  1. 他们通常会假设用户试图弄清楚他们当前正在使用哪种交互式外壳,但这太疯狂了。我知道我要在其中键入命令的外壳程序-我需要可以在任何地方运行的代码,以便能够确定正在使用的外壳程序
  2. 他们通常会提供几种不同的方法来尝试-就像您在命令行上一样,并且可以四处弄弄,直到了解到您正在使用bash为止-但是我需要一件在所有情况下都可靠的方法。
  3. 它们通常会提供脚本中完全没有用的东西,例如echo $0。如上面的脚本所示,该操作失败。(是的,它可以在交互式shell命令行中工作,但是为什么您不知道使用的是哪个shell?)
  4. 他们有时会提供一些命令(在我的有限测试中),这些命令包含正确的信息,例如ps -p $$,但是缺少跨平台,跨外壳兼容的sed / awk命令来通过管道传递命令以获取外壳名称,而忽略随之而来的其他信息骑。
  5. 它们包括只能在几个shell上使用的东西,例如$BASH_VERSION$ZSH_VERSION。我要支持尽可能多的炮弹可能,如fishcshtcsh

如何可靠准确地检测到任何 当前的外壳?我正在寻找可以在各种平台,脚本和尽可能多的shell中正常运行的东西1

更新:当我发布此问题时,我希望外壳中内置一些功能来提供此信息,但事实并非如此。由于现在看来不可避免地要依赖Shell之外的东西,因此我应该更加明确地表明,我正在寻求跨平台解决方案(尽管我对上述其他答案表示反对,但如果您不这样做的话,可能会很容易错过它。没有仔细阅读问题)。

更新2 如果由于Stéphane的回答不是仅Linux,还是有人仍然认为这是仅Linux的问题的重复,那么这里就是我所要求的与他所提供的之间的区别。(请注意,他写的东西很巧妙,我没有敲门,但这并不能解决我的问题。)

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

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


(PS,如果您无法相信有这样的用例,请想象将这些函数派生到.zshrc.bash_profile.profile取决于正在使用的服务器以及可用的外壳的函数。它们的来源使其没有shebang行。如果它们可以在任何外壳中工作,则非常有用,但有时他们必须知道所处的外壳才能知道如何运行。)


1我不关心“贝壳”,它不是任何传统意义上的贝壳。我不关心有人为自己编写的shell。我只关心真正出现在外壳中的真实外壳,并且可以想象有人在登录某些服务器时可能会不得不使用的外壳,而他们不能在其中安装所需的任何外壳。


要阅读有关Python是否为一个壳的完全偏离主题的讨论:看这里
反传统的

2
引用吉尔斯(Gillles)对问题的回答“终端”,“外壳”,“ tty”和“控制台”之间的确切区别是什么?  —“ shell是用户登录时看到的主要界面,其主要目的是启动其他程序。” IMO,这足以将Perl和Python从“ shell”类别中消除。
G-Man说'恢复莫妮卡'

大约相关:兼容性脚本:节省$?供以后使用。建议的方法:通过说出不同shell语法的差异的结尾sh -c "…",然后使用POSIX shell语法完成大部分工作。
G-Man说'恢复莫妮卡'

2
为什么要得到那个?有些外壳(可用作登录外壳)具有非常不兼容的语法(某些外壳是类似Lisp的语言!),所以我不明白为什么要这么做。如果您要编码可源文件,则需要编码多个变体,并告诉用户选择适当的变体。
Basile Starynkevitch 2015年

1
我的第二个问题是伪装外壳的示例。如果脚本由解释器可执行文件执行,而该脚本的名称与解释器解释的语言不匹配,该怎么办?如果我使用csh的副本,请将其重命名为fish并用于运行您的脚本,您要fish还是csh
吉尔(Gilles)'所以

Answers:


5

通过这种组合,我取得了不错的成绩:

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

在Tru64(OSF / 1)上,输出在外壳周围带有括号。钉在tr -d '()'删除它们。

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

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

在此处输入图片说明


3
在脚本中不起作用(将返回脚本名称)
Qw3ry

2

所以我仍在努力充实,但我认为我有一个可行的想法。正如您已经注意到的那样,您尝试做的事情即使不是不可能,也很难在所有shell中完成(添加到polyglot中的每个变体都以大于线性速率的方式增加了复杂性)。如果拆分为Bourne(sh,ash,dash,dash,bash,ksh,pdksh,zsh等)和c样式的shell(csh,tcsh,fish等),则可能会这样做,但是变体之间的差异会csh带来各种有趣的变化挑战。

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

内部程序在linux上似乎很容易(成功了/proc/ppid/exe一半)。我很确定这可以在带有ps选项的bsd上完成,但是目前我没有正在运行的bsd系统可以测试,我的mac需要一个新的硬盘驱动器(无论如何也只有10.4)。尽管它大大减少了Shell语法问题,但确实引入了一组不同的兼容性问题。我仍然认为它有可能。


这就是洞察力,我上最终解决需要一个外部程序做的工作,虽然我读的G-人的有关意见时得到了它sh -c "...",这是他从了另一个问题。(对不起:我起初跳过了您的回答,因为我没有看到任何代码,只有稍后再阅读。)
iconoclast

@iconoclast的问题中存在一个悖论。他想从可以从任何外壳程序中获取的脚本(请注意,某些外壳程序不支持函数)中调用该脚本,因此该脚本大概已经是多语言的,因此必须已经对弄清解释它的内容提供了一些支持。根据我的经验,编写多语言代码(我的which_intepreter是一个极端的示例,但也看到那里只有一个shell是非常困难的,而且通常不值得付出努力。)
StéphaneChazelas 2015年

@StéphaneChazelas:我认为你是正确的,它通常不值得的努力(当然一般不会,也许永远不会),但我不准备放弃......我想问题分解成2点的问题真的:1获得shell名称,然后第二个使用它。两者(至少实际上)都需要一些外部帮助,以避免可能使用的外壳的限制。我已经用Crystal编写的一个小型实用程序解决了第一个问题,并且当我有时间时也想解决第二个问题。顺便说一句,您的解决方案很棒,但显然非常复杂。
iconoclast

@iconoclas,假设您可以可靠地检索shell名称(请注意,shell之间的命令替换和变量赋值语法有所不同),则您将无法执行case $shell in sh)...(仅Bourne语法)或switch ($shell)(仅csh)这样的操作。test "$shell" = "sh" && do-something在工作csh/ sh/ rc/ es,而不是fish...
斯特凡Chazelas

是的,我已经遇到了这个问题。最糟糕的是,fish甚至不会加载其语法不喜欢的脚本,因此您不能仅将STDERR发送到/dev/null。但是我有一个解决方案,一旦一切正常,我将发布该解决方案。
iconoclast

1

尝试这样的事情

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

csh和中失败tcsh
iconoclast

稍加修改,它就可以正常运行fish,但仅在以下情况下可以运行fishps h -p %self -o args='' | cut -f1 -d' '。这造成了鸡和鸡蛋的问题...您必须知道自己fish才能执行fish代码的版本...
iconoclast

@ iconoclast-- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`将与CSH一起使用,但不能与其他人一起使用
-DarkHeart

@DarkHeart:实际上,失败了,因为变量赋值不是csh/ 的问题tcsh,而是这部分:ps -p $$ -o args='' | cut -f1 -d' ',它-sh在on csh-cshon 返回tcsh
iconoclast

1

我相信您不能总是获得当前shell的名称,并且我认为您应该意识到可能的限制。

在Linux发行版中,大多数用户将其bash 作为登录和交互式外壳程序(因为bash在大多数发行版中它是默认外壳程序)。一些用户会将其shell设置为zshcsh(和变体)或fish

(至于其他的意见和答案解释,寻找可靠的外壳出bashzshtcshfish已具有挑战性)

但一些奇怪的用户可以登录shell设置为完全不同的东西-一个Lisp解释,scshes,一些脚本语言点菜的Python,或者Ocaml,或Perl等...-,这是他们的自由这样做。可能有些人正在编码自己的shell并以交互方式使用它。即使您发现了他们奇怪的外壳,也将无法对其做任何有用的事情(因此,我相信您不应尝试获取外壳的名称)。

因此,我想您正在编码一些可源文件(也许生成一个文件)以配置某些软件。因此,只需说明您在做什么,然后为bash(以及zshtcsh)... 的常见情况编写代码。


-2

自行承担以下免责声明

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}

它给了我一个空白行...但是,如果我了解您要尝试执行的操作,这将非常聪明
iconoclast

除非有中的-f选项,否则它不应该给您空白readlink,某些实现中没有此切换
gwillie 2015年

我在OS X上,手册页列出-f了可用选项,format并以参数作为参数。
iconoclast

如果$ 0是一个脚本(以#!/ ...开头),在某些特殊情况下可能会起作用,但这不是通常的设置。在这里,大多数$ 0指向ELF文件。

readlink无法在我的puredarwin vm上工作,所以我不知道发生了什么。
gwillie
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.