如何:从 shell 脚本检测 bash
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3199893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HOWTO: Detect bash from shell script
提问by Kan-Ru Chen
The scenario is that users are asked to source a script file:
该场景是要求用户提供脚本文件的来源:
$ source envsetup.sh
This script file may use bash only feature so we have detect the running shell is bash or not.
此脚本文件可能仅使用 bash 功能,因此我们已检测到正在运行的 shell 是否为 bash。
For other shells that share common syntax with bash, for example, sh, zsh, ksh, I'd like to report a warning.
对于其他与 bash 共享通用语法的 shell,例如 sh、zsh、ksh,我想报告一个警告。
What is the most reliable way to detect the current shell across Linux, Cygwin, OS X?
跨 Linux、Cygwin、OS X 检测当前 shell 的最可靠方法是什么?
What I know is $BASH, but I am wondering the chances it could fail.
我所知道的是 $BASH,但我想知道它失败的可能性。
回答by D.Shawley
There are a bunch of environment variables that you can look at but many of them will not detect if a different shell is spawned from bash. Consider the following:
您可以查看许多环境变量,但其中许多变量无法检测是否从 bash 生成了不同的 shell。考虑以下:
bash$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: bash$ exec -a "-csh" bash
bash$ echo "[ -z "$BASH_VERSION" ] && return
, $SHELL, $BASH"
-csh, /bin/bash, /bin/bash
bash$ ps -o command -p $$
COMMAND
-csh
bash$
, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: -bash, PS1: bash$ , prompt:
bash$ csh
[lorien:~] daveshawley% echo "SHELL: $SHELL, shell: $shell, $0: if test -z "$(type -p)" ; then echo bash ; else echo sh ; fi
, 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]: /proc/self/exe --version 2>/dev/null | grep -q 'GNU bash' && USING_BASH=true || USING_BASH=false
, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: sh, PS1: bash$ , prompt:
bash$ zsh
% echo "SHELL: $SHELL, shell: $shell, ARGV[0]: $ bash -c 'echo "The shell pid = $$"; echo -n "readlink (subprocess) pid = "; readlink /proc/self; echo "And again the running shells pid = $$"'
, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: zsh, PS1: % , prompt: %
% ksh
$ echo "SHELL: $SHELL, shell: $shell, ARGV[0]: The shell pid = 34233
readlink (subprocess) pid = 34234
And again the running shells pid = 34233
, PS1: $PS1, prompt: $prompt"
SHELL: /bin/bash, shell: , ARGV[0]: ksh, PS1: bash$ , prompt:
There are a number of variables specific to the various shells except that they have a habit of being inherited by sub-shells which is where the environment thing really breaks. The only thing that almost works is ps -o command -p $$. This technically gives you the command name that the shell is running as. In most cases this will work... since applications are started with some variant of the execsystem call and it allows for the name of the command and the executable to differ, it is possible for this to fail as well. Consider:
有许多特定于各种 shell 的变量,除了它们有被子 shell 继承的习惯,这是环境问题真正中断的地方。唯一几乎有效的是ps -o command -p $$. 这在技术上为您提供了 shell 正在运行的命令名称。在大多数情况下,这会起作用……因为应用程序是通过exec系统调用的某些变体启动的,并且它允许命令的名称和可执行文件的名称不同,因此这也可能会失败。考虑:
bash -c 'echo -n "readlink binary = "; readlink /proc/self/exe; echo -n "shell binary = "; readlink /proc/$$/exe'
Another trick is to use lsof -p $$ | awk '(NR==2) {print $1}'. This is probably as close as you can get if you are lucky enough to have lsofhandy.
另一个技巧是使用lsof -p $$ | awk '(NR==2) {print $1}'. 如果您足够幸运,这可能是您所能获得的最接近的结果lsof。
回答by Fritz G. Mehner
This works also
这也有效
readlink binary = /bin/readlink
shell binary = /bin/bash
回答by Dumbled0re
Here is a nice way:
这是一个很好的方法:
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'
and you can of course replace the "echo" statements with anything you want.
你当然可以用你想要的任何东西替换“echo”语句。
=================
==================
Discussion:
讨论:
The
$SHELLvariable indicates the user's preferred shell ... which tells you nothing about the shell that is running at the moment.Testing
$BASH_VERSIONis a 99% good idea, but it could fail if some wise-guy sticks a variable of that name into the sh environment. Furthermore, it doesn't tell you much about whichnon-bash shell is running.The
$(type -p)method is super-easy, and works even if some wise guy creates a file called "-p" in your$PATH. Furthermore, it can be used as the basis for a 4-way discrimination, or 80% of a 5-way discrimination, as discussed below.Putting a hash-bang i.e.
#!at the top of your script does notguarantee that it will be fed to the interpreter of your choice. For example, my~/.xinitrcgets interpreted by/bin/shno matter what hash-bang (if any) appears at the top.Good practice is to test some feature that reliably exists in both languages, but behaves differently. In contrast, it would notbe safe in general to try the feature you want and see whether it fails. For instance, if you want to use the built-in
declarefeature and it's not present, it could run a program, and that has unlimited downside potential.- Sometimes it is reasonable to write compatible code, using the lowest-common-denominator feature set ... but sometimes it isn't. Most of those added features were added for a reason. Since these interpreters are ?almost? Turing-complete it is ?almost? guaranteed to be possible to emulate one with the other ... possible, but not reasonable.
- There are two layers of incompatibility: syntax and semantics. For instance, the if-then-else syntax for csh is so different from bash that the only way to write compatible code would be to do without if-then-else statements altogether. That's possible, but it imposes a high cost. If the syntax is wrong, the script won't execute at all. Once you get past that hurdle, there are dozens of ways in which reasonable-looking code produces different results, depending on which dialect of interpreter is running.
For a large, complicated program, it does not make sense to write two versions. Write it once, in the language of your choice. If somebody starts it under the wrong interpreter, you can detect that and simply
execthe right interpreter.A 5-way detector can be found here:
https://www.av8n.com/computer/shell-dialect-detect
It can discriminate:
- bash
- bsd-csh
- dash
- ksh93
- zsh5
Furthermore, on my Ubuntu Xenial box, that 5-way check also covers the following:
- ash is a symlink to dash
- csh is a symlink to /bin/bsd-csh
- ksh is a symlink to /bin/ksh93
- sh is a symlink to dash
该
$SHELL变量指示用户的首选外壳程序...它不会告诉您当前正在运行的外壳程序的任何信息。测试
$BASH_VERSION是一个 99% 的好主意,但如果一些聪明人将这个名称的变量粘贴到 sh 环境中,它可能会失败。此外,它并没有告诉您关于哪个非 bash shell 正在运行的太多信息。该
$(type -p)方法非常简单,即使某些聪明人在您的$PATH. 此外,它可以用作 4 向鉴别的基础,或 5 向鉴别的 80%,如下所述。将 hash-bang ie
#!放在脚本的顶部并不能保证它会被提供给您选择的解释器。例如,无论顶部出现什么 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 是破折号的符号链接
回答by user2059857
I think this would be the most practical and cross shell compatible
我认为这将是最实用和跨 shell 兼容的
##代码##Explanation:
解释:
/proc/selfwill always point to the current executing process, for example, running the following reveals the pid of readlinkit self (not the shell which executed readlink)
/proc/self将始终指向当前正在执行的进程,例如,运行以下会显示readlink其自身的 pid (而不是执行 readlink 的 shell)
Results in:
结果是:
##代码##Now:
/proc/self/exeis a symbolic link to the running executable
现在:
/proc/self/exe是运行中的可执行文件的符号链接
Example:
例子:
##代码##results in:
结果是:
##代码##And here is the results running in dash and zsh, and running bash through a symlink, and even through a copy.
这是在 dash 和 zsh 中运行的结果,并通过符号链接甚至通过副本运行 bash。
##代码##回答by Jeff
The SHELLenvironment variable will tell you what login shell is running.
在SHELL环境变量会告诉你登录shell运行。
Also, you can use ps $$to find the current shell, which can be used if you want to know what shell the script is running under (not necessarily the login shell). To whittle down the psoutput to just the shell name: ps o command= $$(not sure how cross-platform safe this is, but it works on Mac OS X).
此外,您可以使用ps $$查找当前 shell,如果您想知道脚本在哪个 shell 下运行(不一定是登录 shell),则可以使用它。将ps输出缩减为shell 名称:(ps o command= $$不确定跨平台安全性如何,但它适用于 Mac OS X)。
回答by iconoclast
I would recommend trying to detect the presence of the featureyou need rather than bashvs zshvs etc. If the feature is present, use it, if not use an alternative. If there's no good way to detect the feature other than using it and checking for errors, then although that's kind of ugly, I don't have a better solution. What matters is that it should work more reliably than trying to detect bash. And since other shells (that are still in development) may have this formerly-bash-only feature at some point in the future, it really deals with what matters, and allows you to avoid having to maintain a database of which versions of each shell support which feature and which don't.
我建议尝试检测您需要的功能的存在,而不是bashvs zshvs 等。如果该功能存在,请使用它,如果不使用替代品。如果除了使用它并检查错误之外没有其他好的方法来检测该功能,那么尽管这有点难看,但我没有更好的解决方案。重要的是它应该比试图检测更可靠地工作bash。并且由于其他 shell(仍在开发中)可能在未来的某个时候具有这个以前仅用于 bash 的功能,它真正处理重要的事情,并允许您避免必须维护每个 shell 的哪个版本的数据库支持哪些功能,哪些不支持。

