如何识别脚本中是否正在使用bash或dash?

How to recognize whether bash or dash is being used within a script?

我正在写一个bash脚本,当在Ubuntu中使用"sh"命令时它会抛出一个错误(它似乎与dash不兼容,我正在学习这个主题)。所以我想检测一下是否使用了dash而不是bash来抛出错误。

如何在脚本上下文中检测它?有可能吗?


您可以检查是否存在特定于shell的变量:

例如,bash定义$BASH_VERSION。由于在dash中运行时不会定义该变量,因此可以使用它来区分:

1
[ -n"$BASH_VERSION" ] && isBash=1

事后想一想:如果您想避免依赖变量(可以想象,变量设置可能不正确),您可以通过确定调用的可执行文件,以及如果它是一个符号链接,跟随它到它的(最终)目标,来尝试获取运行脚本的shell可执行文件的最终名称。

下面的shell函数getTrueShellExeName()就是这样做的;例如,它会在Ubuntu上返回'dash'以运行sh的脚本(无论是显式运行还是通过shebang #!/bin/sh运行),因为sh在那里与dash对称链接。

注意,函数的目标是双重的:

  • 便携:
    • 使用所有与posix兼容(bourne-like)的shell,
    • 至少在大多数平台上,关于使用的实用程序和选项,请参见下面的警告。
  • 在所有调用场景中工作:
    • 源代码(无论是否来自登录shell)
    • 通过shebang线独立执行
    • 通过作为文件名参数传递给shell可执行文件来执行
    • 通过将其内容通过stdin传输到shell可执行文件来执行

Caveats:

  • 在至少一个平台上,osx-sh不是一个符号链接,即使它实际上是bash。在这里,函数将在使用sh运行的脚本中返回'sh'
  • 该函数使用readlink,虽然它不是由posix强制执行的,但它在大多数现代平台上都存在——尽管有不同的语法和特性。因此,使用gnu readlink-f选项来查找symlink的最终目标不是一个选项。(我个人所知道的唯一一个没有readlink实用程序的现代平台是HP-UX-请参阅https://stackoverflow.com/a/24114056/45375了解在所有posix平台上都能工作的递归readlink实现。)
  • 该函数使用which实用程序(zsh除外,它是一个内置的工具),虽然它不是由posix授权的,但在大多数现代平台上都存在。

函数的示例用法:

1
["$(getTrueShellExeName)" = 'bash' ] && isBash=1

壳函数getTrueShellExeName()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getTrueShellExeName() {
  local trueExe nextTarget 2>/dev/null # ignore error in shells without `local`
  # Determine the shell executable filename.
  trueExe=$(ps -o comm= $$) || return 1
  # Strip a leading"-", as added e.g. by OSX for login shells.
  ["${trueExe#-}" ="$trueExe" ] || trueExe=${trueExe#-}
  # Determine full executable path.
  ["${trueExe#/}" !="$trueExe" ] || trueExe=$([ -n"$ZSH_VERSION" ] && which -p"$trueExe" || which"$trueExe")
  # If the executable is a symlink, resolve it to its *ultimate*
  # target.
  while nextTarget=$(readlink"$trueExe"); do trueExe=$nextTarget; done
  # Output the executable name only.
  printf '%s
'
"$(basename"$trueExe")"
}


使用$0(这是正在调用的shell的可执行文件的名称)。

1
echo $0

给予

1
/usr/bin/dash

为了冲刺和

1
/bin/bash

对于bash。参数替换

1
${0##*/}

只给出"dash"或"bash"。这可以在测试中使用。


另一种方法可能是测试shell功能是否可用,例如给出一个想法…

1
[[ 1 ]] 2>/dev/null && echo could be bash || echo not bash, maybe dash