代码之家  ›  专栏  ›  技术社区  ›  Kan-Ru Chen

如何:从shell脚本检测bash

  •  8
  • Kan-Ru Chen  · 技术社区  · 16 年前

    场景是要求用户提供一个脚本文件:

    $ source envsetup.sh
    

    这个脚本文件可能只使用bash特性,所以我们已经检测到正在运行的shell是否是bash。

    在Linux、Cygwin和osx上检测当前shell最可靠的方法是什么?

    我知道的是$BASH,但我想知道它失败的可能性。

    6 回复  |  直到 16 年前
        1
  •  18
  •   D.Shawley    16 年前

    有很多环境变量可以查看,但是如果bash生成了不同的shell,其中很多都无法检测到。考虑以下几点:

    bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
    SHELL: /bin/bash, shell: , ARGV[0]: -bash, PS1: bash$ , prompt: 
    
    bash$ csh
    [lorien:~] daveshawley% echo "SHELL: $SHELL, shell: $shell, \$0: $0, PS1: $PS1, prompt: $prompt"
    SHELL: /bin/bash, shell: /bin/tcsh, ARGV[0]: csh, PS1: bash$ , prompt: [%m:%c3] %n%#
    
    [lorien:~] daveshawley% bash -r
    bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
    SHELL: /bin/bash, shell: , ARGV[0]: sh, PS1: bash$ , prompt:
    
    bash$ zsh
    % echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
    SHELL: /bin/bash, shell: , ARGV[0]: zsh, PS1: % , prompt: % 
    
    % ksh
    $ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $0, PS1: $PS1, prompt: $prompt"
    SHELL: /bin/bash, shell: , ARGV[0]: ksh, PS1: bash$ , prompt: 
    

    有许多特定于各种shell的变量,只是它们有一种被子shell继承的习惯,而子shell正是环境真正破坏的地方。唯一能起作用的就是 ps -o command -p $$ exec 系统调用,它允许命令和可执行文件的名称不同,这也可能失败。考虑:

    bash$ exec -a "-csh" bash
    bash$ echo "$0, $SHELL, $BASH"
    -csh, /bin/bash, /bin/bash
    bash$ ps -o command -p $$
    COMMAND
    -csh
    bash$
    

    另一个技巧是使用 lsof -p $$ | awk '(NR==2) {print $1}' . 如果你足够幸运的话,这可能是你能得到的最接近的 lsof

        2
  •  13
  •   Fritz G. Mehner    16 年前

    这同样有效

    [ -z "$BASH_VERSION" ] && return
    
        3
  •  11
  •   Dumbled0re    9 年前

    这里有一个很好的方法:

    if test -z "$(type -p)" ; then echo bash ; else echo sh ; fi
    

    =================

    • 这个 $SHELL 你对目前正在运行的shell一无所知。

    • $BASH_VERSION 这是一个99%的好主意,但如果某个聪明人将该名称的变量插入到sh环境中,它可能会失败。此外,它并没有告诉你很多关于 非bash shell正在运行。

    • $(type -p) 方法非常简单,即使一些聪明人在您的应用程序中创建了一个名为“-p”的文件,它也能工作 $PATH . 此外,如下文所述,它可以用作4向歧视或5向歧视的80%的基础。

    • #! 在你剧本的顶端 不 保证它会被提供给你选择的翻译。例如,我的 ~/.xinitrc 被解释为 /bin/sh 无论顶部出现什么样的hash bang(如果有的话)。

    • 不 declare 功能,它可以运行一个程序,这有无限的下行潜力。

    • 有时编写兼容的代码是合理的,使用最小公分母特性集。。。但有时不是。这些新增的功能大部分都是有原因的。因为这些解释器几乎是图灵完备的,所以几乎可以保证可以用一个来模仿另一个。。。可能,但不合理。
    • 有两层不兼容:语法和语义。例如,csh的if-then-else语法与bash非常不同,因此编写兼容代码的唯一方法是完全不使用if-then-else语句。这是有可能的,但要付出很高的代价。如果语法错误,脚本根本不会执行。一旦你越过了这个障碍,根据解释器运行的是哪种方言,有几十种方法可以让外观合理的代码产生不同的结果。
    • exec 正确的翻译。

    • 可在此处找到5路探测器:

      https://www.av8n.com/computer/shell-dialect-detect

      它可以辨别:

      • 猛击
      • bsd csh公司
      • 短跑
      • zsh5型

      此外,在我的Ubuntu Xenial框中,5路检查还包括以下内容:

      • 灰烬是破折号的符号
      • csh是/bin/bsd csh的符号链接
        4
  •  3
  •   user2059857    10 年前

    我认为这将是最实用和跨壳兼容

    /proc/self/exe --version 2>/dev/null | grep -q 'GNU bash' && USING_BASH=true || USING_BASH=false
    

    /proc/self 将始终指向当前正在执行的进程,例如,运行以下命令将显示 readlink 它自己(不是执行readlink的shell)

    $ bash -c 'echo "The shell pid = $$"; echo -n "readlink (subprocess) pid = "; readlink /proc/self; echo "And again the running shells pid = $$"'
    

    结果:

    The shell pid = 34233
    readlink (subprocess) pid = 34234
    And again the running shells pid = 34233
    

    现在: /proc/self/exe 是指向正在运行的可执行文件的符号链接

    bash -c 'echo -n "readlink binary = "; readlink /proc/self/exe; echo -n "shell binary = "; readlink /proc/$$/exe'
    

    结果:

    readlink binary = /bin/readlink
    shell binary = /bin/bash
    

    aron@aron:~$ cp /bin/bash ./my_bash_copy
    aron@aron:~$ ln -s /bin/bash ./hello_bash
    aron@aron:~$ 
    
    aron@aron:~$ dash -c '/proc/self/exe -c "readlink /proc/$$/exe"; zsh -c "/proc/self/exe --version"; ./hello_bash --version | grep bash; ./my_bash_copy --version | grep bash'
    /bin/dash
    zsh 5.0.7 (x86_64-pc-linux-gnu)
    GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
    GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
    aron@aron:~$ dash -c '/proc/self/exe -c "readlink /proc/$$/exe"; zsh -c "/proc/self/exe --version"; ./hello_bash --version | grep bash; ./my_bash_copy --version | grep bash'
    
        5
  •  1
  •   James Z.M. Gao    6 年前

    要改进@Dumble0re-answer以支持更多shell拨号盘:

    # original detect shell dialet, see: https://www.av8n.com/computer/shell-dialect-detect
    # improvement version: https://gist.github.com/gzm55/028912a3d4c2846790c7438d0863fd7f
    # `&&` will defeat the errexit option, see: http://www.binaryphile.com/bash/2018/08/13/approach-bash-like-a-developer-part-15-strict-mode-caveats.html
    eval `echo ! : 2>/dev/null` : && echo "[ERROR] must not be executed or sourced by csh!" && exit 64
    
    __DO_NOT_SUPPORT_FISH__=1
    
    # Now that csh and fish has been excluded, it is safe to continue
    
    __SHELL_DIALECT__=
    case "${__SHELL_DIALECT__:=$(
      PATH="/dev/null/$$" set +e
      PATH="/dev/null/$$" export PATH="/dev/null/$$"
      type -p 2>/dev/null >/dev/null
      echo $? $(type declare 2>/dev/null; echo err=$?)
    )}" in
    "0 "*"not found err=1") __SHELL_DIALECT__=mksh ;;
    "0 "*"not found err=127") __SHELL_DIALECT__=busybox ;; # ash busybox various
    "0 "*"shell builtin err="*) __SHELL_DIALECT__=bash4 ;;
    "1 "*"reserved word err="*) __SHELL_DIALECT__=zsh5 ;;
    "1 "*"shell builtin err="*) __SHELL_DIALECT__=bash3 ;;
    "2 err="*) __SHELL_DIALECT__=ksh93 ;;
    "127 "*"not found err="*) __SHELL_DIALECT__=dash ;; # ash debian various
    "127 err=127") __SHELL_DIALECT__=ash-bsd ;; # ash bsd-sh various
    *) __SHELL_DIALECT__="unknown:$__SHELL_DIALECT__" ;;
    esac
    
    # detect bash posix mode, on bash, SHELLOPTS is a read only variable
    case "$__SHELL_DIALECT__:${SHELLOPTS-}:" in
    bash*:posix:*) __SHELL_DIALECT__="$__SHELL_DIALECT__-posix" ;;
    *) ;;
    esac
    
    echo "__SHELL_DIALECT__=[$__SHELL_DIALECT__]"
    

    重要的是, bash 3 有不同的检测方法,应该是默认的 /bin/sh 在MacOS上。

    test 在 /usr/bin/test

        6
  •  0
  •   Jeff    16 年前

    这个 SHELL 环境变量将告诉您什么登录shell正在运行。

    另外,你可以使用 ps $$ 查找当前shell,如果您想知道脚本在哪个shell下运行,可以使用它(不一定是登录shell)。削尖 ps ps o command= $$

        7
  •  0
  •   iconoclast    13 年前

    我建议尝试检测 你需要而不是 bash 与 zsh 如果功能存在,则使用它,如果没有,则使用替代品。如果除了使用它并检查错误之外,没有更好的方法来检测特性,那么尽管这有点难看,但我没有更好的解决方案。重要的是,它应该比试图检测更可靠 猛击