HOWTO:从shell脚本中检测bash

Kan*_*hen 9 bash shell scripting

方案是要求用户提供脚本文件:

$ source envsetup.sh
Run Code Online (Sandbox Code Playgroud)

此脚本文件可能仅使用bash功能,因此我们检测到正在运行的shell是否为bash.

对于与bash共享通用语法的其他shell,例如sh,zsh,ksh,我想报告一个警告.

在Linux,Cygwin,OS X中检测当前shell的最可靠方法是什么?

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

D.S*_*ley 14

您可以查看一些环境变量,但是其中许多变量无法检测是否从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: 
Run Code Online (Sandbox Code Playgroud)

有许多特定于各种shell的变量,除了它们习惯被子壳继承,这是环境真正破坏的地方.唯一有用的是ps -o command -p $$.从技术上讲,它为您提供了shell运行的命令名称.在大多数情况下,这将起作用...因为应用程序是使用exec系统调用的某种变体启动的,并且它允许命令的名称和可执行文件不同,所以它也可能失败.考虑:

bash$ exec -a "-csh" bash
bash$ echo "$0, $SHELL, $BASH"
-csh, /bin/bash, /bin/bash
bash$ ps -o command -p $$
COMMAND
-csh
bash$
Run Code Online (Sandbox Code Playgroud)

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

  • `lsof -p $$ | lsof -p $$ | awk '(NR==2) {print $1}' `在 Darwin 上不可靠,因此 `ps -o command -p $$` 是最好的选择。 (2认同)

Fri*_*ner 10

这也有效

[ -z "$BASH_VERSION" ] && return
Run Code Online (Sandbox Code Playgroud)

  • 也可以为 zsh 执行此操作,因此 `if [ -n "${BASH_VERSION}" ]; 然后... elif [ -n "${ZSH_VERSION}" ]; 然后...... fi` (4认同)
  • 有脚本:`#!/bin/sh \n echo $BASH_VERSION`,甚至从 bash shell 中使用 sh 或 csh 运行它:`$sh ./test.sh`,您将从用户首选的环境变量中获取环境变量bash 版本....完全误导了运行脚本 (3认同)

Dum*_*0re 9

这是一个很好的方式:

if test -z "$(type -p)" ; then echo bash ; else echo sh ; fi
Run Code Online (Sandbox Code Playgroud)

你当然可以用你想要的任何东西替换"echo"语句.

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

讨论:

  • $SHELL变量表示用户首选的shell ...它不会告诉您当前正在运行的shell.

  • 测试$BASH_VERSION是一个99%的好主意,但如果有些明智的人将该名称的变量粘贴到sh环境中,它可能会失败.此外,它并没有告诉你很多关于哪个非bash shell正在运行.

  • 这种$(type -p)方法非常简单,即使有些聪明人在你的文件中创建了一个名为"-p"的文件也能正常工作$PATH.此外,它可以用作4路鉴别的基础,或者是5路鉴别的80%,如下所述.

  • 将hash-bang放在#!脚本的顶部并不能保证它会被提供给您选择的解释器.例如,无论在顶部出现什么hash-bang(如果有的话),我~/.xinitrc都会被解释/bin/sh.

  • 好的做法是测试两种语言中可靠存在的某些功能,但行为不同.相反,它会不会是一般的安全试试你想要的功能,并看它是否失败.例如,如果您想使用内置declare功能并且它不存在,它可以运行程序,并且具有无限的下行潜力.

  • 有时使用最小公分母特征集来编写兼容代码是合理的...但有时却不是.大多数添加的功能都是出于某种原因而添加的.由于这些解释器"几乎"图灵完成,因此"几乎"保证可以模仿另一个......可能,但不合理.
  • 有两层不兼容:语法和语义.例如,csh的if-then-else语法与bash完全不同,编写兼容代码的唯一方法就是不使用if-then-else语句.这是可能的,但它会带来很高的成本.如果语法错误,则脚本根本不会执行.一旦你克服了这个障碍,有许多方法可以使合理的代码产生不同的结果,这取决于解释器的哪种方言正在运行.
  • 对于大型复杂程序,编写两个版本没有意义.用你选择的语言写一次.如果有人在错误的解释器下启动它,您可以检测到它并且只是exec正确的解释器.

  • 这里可以找到一个5路探测器:

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

    它可以区分:

    • 庆典
    • BSD-CSH
    • 短跑
    • ksh93的
    • zsh5

    此外,在我的Ubuntu Xenial盒子上,该5向检查还包括以下内容:

    • ash是一个破折号的符号链接
    • csh是/ bin/bsd-csh的符号链接
    • ksh是/ bin/ksh93的符号链接
    • sh是破折号的符号链接


use*_*857 6

我认为这将是最实用且跨 shell 兼容的

/proc/self/exe --version 2>/dev/null | grep -q 'GNU bash' && USING_BASH=true || USING_BASH=false
Run Code Online (Sandbox Code Playgroud)

解释:

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

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

结果是:

The shell pid = 34233
readlink (subprocess) pid = 34234
And again the running shells pid = 34233
Run Code Online (Sandbox Code Playgroud)

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

例子:

bash -c 'echo -n "readlink binary = "; readlink /proc/self/exe; echo -n "shell binary = "; readlink /proc/$$/exe'
Run Code Online (Sandbox Code Playgroud)

结果是:

readlink binary = /bin/readlink
shell binary = /bin/bash
Run Code Online (Sandbox Code Playgroud)

这是在 dash 和 zsh 中运行的结果,以及通过符号链接甚至通过副本运行 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'
Run Code Online (Sandbox Code Playgroud)