【问题标题】:How to get shell to self-detect using zsh or bash如何让 shell 使用 zsh 或 bash 进行自我检测
【发布时间】:2012-04-12 05:58:54
【问题描述】:

我有一个关于如何判断用户正在使用哪个 shell 的问题。假设一个脚本,如果用户使用 zsh,则将 PATH 放入他的.zshrc,如果使用 bash,则应放入 .bashrc。并相应地设置 rvmrc。

#!/usr/bin/env bash
export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
source ~/.zshrc

我尝试了以下方法,但它不起作用:(

if [[ $0 == "bash" ]]; then
  export PATH='/usr/local/bin:$PATH' >> ~/.bashrc
elif [[ $0 == "zsh" ]]; then
  export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
fi

# ... more commands ...

if [[ $0 == "bash" ]]; then
  [[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.bashrc
  source ~/.bashrc
elif [[ $0 == "zsh" ]]; then
  [[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.zshrc
  source ~/.zshrc
fi

【问题讨论】:

  • 把它放在.profile 中不是更有意义吗,它可以跨shell 移植,包括sh
  • @tripleee 嗨,因为我认为这是 bash 或 zsh 的特定内容...
  • $0 == "-zsh" 而不是 $0 == "zsh"

标签: bash zsh sh


【解决方案1】:

如果shell是Zsh,则定义变量$ZSH_VERSION。对于 Bash 和 $BASH_VERSION 也是如此。

if [ -n "$ZSH_VERSION" ]; then
   # assume Zsh
elif [ -n "$BASH_VERSION" ]; then
   # assume Bash
else
   # assume something else
fi

但是,这些变量仅告诉您使用哪个 shell 来运行上述代码。所以你必须在用户的shell中source这个片段。

作为替代方案,您可以使用 $SHELL 环境变量(它应该包含用户首选 shell 的绝对路径)并根据该变量的值猜测 shell:

case $SHELL in
*/zsh) 
   # assume Zsh
   ;;
*/bash)
   # assume Bash
   ;;
*)
   # assume something else
esac

当然,当/bin/sh 是指向/bin/bash 的符号链接时,上述操作会失败。

如果要依赖$SHELL,实际执行一些代码比较安全:

if [ -n "`$SHELL -c 'echo $ZSH_VERSION'`" ]; then
   # assume Zsh
elif [ -n "`$SHELL -c 'echo $BASH_VERSION'`" ]; then
   # assume Bash
else
   # assume something else
fi

无论使用哪个 shell 运行脚本,都可以从脚本运行最后一条建议。

【讨论】:

  • 您好,谢谢您的回答。我试过你的最后一种方法。当我驻留在 zsh 中时,我运行了您的脚本,但它仍然说我正在使用 bash。我拍了一张:screenshot
  • @juanitofatas 您在 bash 下运行脚本,所以它正确报告它在 bash 下运行。
  • @juanitofatas 您在终端中运行了 zsh。从那个交互式 zsh 中,您运行 bash 来解释脚本。在该脚本中,您测试了哪个 shell 执行了该脚本,答案是 bash。
  • @adl,最后一个代码块在 SHELL 前面缺少一个“$”。我尝试编辑它,但 StackOverflow 编辑至少需要更改 6 个字符。
  • @agconti ohmyzsh(一组配置文件,而不是 zsh 本身)不设置该变量是正常的。该变量在 Zsh 源代码github.com/zsh-users/zsh/blob/master/Src/params.c#L938 中设置,然后到处使用。
【解决方案2】:

只要echo $0 如果是 zsh 则为 -zsh,如果是 bash

则为 -bash

编辑:有时它返回 -zsh 有时返回 zsh 和 bash 一样,我知道为什么。

【讨论】:

  • 这并不适用于所有的 shell,但是当我坐在 CLI 上时,它仍然是我的首选方法——它的工作次数比不工作的次数多。对于 echo $0,诸如 Fish 之类的 shell 不会返回任何内容
  • -zsh-bash 用于表示登录shell。如果名称前面没有-,则用户使用的是非登录shell
  • 如果你在一个函数中,这不起作用,$0 将返回函数名。
  • 这在被脚本调用时也不起作用,它给出了脚本的名称
  • detected_shell=$(echo "${0//-/}") ... 你可以这样测试:if [[ -f "${HOME}/.${detected_shell}rc" ]]; then echo "Found ${HOME}/.${detected_shell}rc"; fi
【解决方案3】:

一个警告:你似乎问过的问题、你打算问的问题和你应该问的问题是三件不同的事情。

“用户正在使用哪个 shell”是模棱两可的。您的尝试看起来像是在尝试确定哪个 shell 正在执行您的脚本。这将始终是您在脚本的 #! 行中输入的任何内容,除非您的意思是让您的用户编辑该脚本,因此这对您没有用处。

我想你想问的是用户最喜欢的 shell 是什么。这无法完全可靠地确定,但您可以涵盖大多数情况。检查SHELL 环境变量。如果它包含fishzshbashkshtcsh,那么用户最喜欢的shell 可能就是那个shell。但是,这对您的问题来说是错误的问题。

.bashrc.zshrc.cshrc 等文件是 shell 初始化文件。它们不是定义环境变量的正确位置。在那里定义的环境变量仅在用户启动该 shell 的终端中可用,而不在从 GUI 启动的程序中可用。该定义还将覆盖用户可能在子会话中所做的任何自定义。

定义环境变量的正确位置是在会话启动文件中。这大多与用户选择的shell无关。不幸的是,没有一个地方可以定义环境变量。在很多系统上,~/.profile 可以工作,但这不是通用的。请参阅 https://unix.stackexchange.com/questions/4621/correctly-setting-environment 和我链接到的其他帖子以进行更长时间的讨论。

【讨论】:

  • “这总是你在脚本的 #! 行中放置的任何内容” - 这不是真的,因为用户可以轻松地将脚本文件名作为参数传递给任何 shell。
  • @MichaelMior:如果脚本未被标记为可执行,那么用户可能将脚本文件名作为参数传递给某个随机shell。根据您的安装过程,情况可能会,也可能不会。
  • 正如预期的那样,公认的解决方案只提供修辞上的警告和惩罚,而不是有意义的解决方案。每个人都想要adlauthoritative answer,它提供了几个解决实际问题的通用sn-ps。
  • @CecilCurry 我认为我的回答被接受了,因为它回答了提问者的意思。 adl 的回答是对标题的更详细处理,但这通常是错误的问题。
【解决方案4】:

你可以试试

 echo $SHELL

【讨论】:

  • 这失败了。在 Zsh 中,如果我打开一个 bash 子 shell 并执行此操作,它仍然会显示 /bin/zsh,这显然不是 shell。
【解决方案5】:

其他答案因set -u 而失败

  if [ ! -z ${ZSH_VERSION+x} ]; then
    echo "this is zsh"
    echo ${(%):-%x}
  elif [ ! -z ${BASH_VERSION+x} ]; then
    echo "this is bash"
    echo $BASH_SOURCE
  else
    echo "not recognized"
  fi

【讨论】:

  • 如果我从 zsh 打开一个 bash 子shell,这会成功,不像echo $SHELL
【解决方案6】:

另一种选择,可能不适用于所有 shell。

for x in $(ps -p $$)
do
  ans=$x
done
echo $ans

【讨论】:

  • ps -p $$ --no-headers -o cmd 好一点。
  • 不工作时除外:ps: unknown option -- no-headers
  • ps -f -o cmd= -p "$$" | sed '1{/^$/N;s/^\n//;t r;:r;/^\[/{:l;$s/^\[\(.*\)\]$/\1/;t;N;b l}}' 应该是健壮和完整的。 (sed 处理剥离 -f 后备注释和不符合 POSIX 标准的 ps 实现,当所有标题文本字段为空时仍然写入标题行。)看起来cmd 格式名称不是标准的,所以如果失败,您也可以尝试ps -o comm= -p "$$" | sed '1{/^$/N;s/^\n//}'
【解决方案7】:

我自己也有类似的问题,解决了:

_shell="$(ps -p $$ --no-headers -o comm=)"                                                                                                       
if [[ $_shell == "zsh" ]]; then                                                                                                                  
    read -q -s "?Do it?: "                                                                                                                    
fi                                                                                                                                               
elif [[ $_shell == "bash" || $_shell == "sh" ]]; then                                                                                              
    read -n 1 -s -p "Do it [y/n] "                                                                                                            
fi                                                                                                                                               

【讨论】:

    【解决方案8】:

    根据 Gilles 之前的回答,我是这样做的:

    if [ -n "$ZSH_VERSION" ]; then
      SHELL_PROFILE="$HOME/.zprofile"
    else
      SHELL_PROFILE="$HOME/.bash_profile"
    fi
    echo "export VAR1=whatever" >> $SHELL_PROFILE
    echo "INFO: Refreshing your shell profile: $SHELL_PROFILE"
    if [ -n "$ZSH_VERSION" ]; then
      exec zsh --login
    else
      source $SHELL_PROFILE
    fi
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-13
      • 2021-05-31
      • 2019-07-26
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多