如何防止不受支持的“shopt”选项在我的 .bashrc 中导致错误?

如何防止不受支持的“shopt”选项在我的 .bashrc 中导致错误?

我在一个相对异构的环境中工作,我可能在不同的 HPC 节点、虚拟机或我的个人工作站上运行不同版本的 Bash。因为我将登录脚本放在 Git 存储库中,所以我希望.bashrc全面使用相同的脚本,而不会有很多“如果这个主机,则...”类型的混乱。

喜欢Bash ≤ 4.1 的默认行为会扩展cd $SOMEPATHcd /the/actual/path按下Tab按键时的行为。在 Bash 4.2 及更高版本中,您需要shopt -s direxpand重新启用此行为,并且不可用截止至2029年2月4日。但这只是一个例子;另一个可能相关的shopt选项,complete_fullquote(虽然我不知道确切地它的作用)可能也改变了 v4.2 的默认行为。

但是,direxpand早期版本的 Bash 无法识别,如果我shopt -s direxpand在我的 中尝试这样做.bashrc,则会导致每次我使用旧版 Bash 登录到节点时都会在控制台上打印一条错误消息:

-bash: shopt: direxpand: invalid shell option name

我想做的是包裹一个条件shop -s direxpand以稳健的方式在 Bash > 4.1 上启用该选项,而不会影响旧版本的 Bash (IE,而不仅仅是将错误输出重定向到/dev/null)。

答案1

我不明白将错误重定向到/dev/null.如果您希望代码具有鲁棒性set -e,请使用常见的习惯用法… || true

shopt -s direxpand 2>/dev/null || true

如果您想在该选项不存在时运行一些后备代码,请使用以下返回状态shopt

if shopt -s direxpand 2>/dev/null; then
  … # the direxpand option exists
else
  … # the direxpand option does not exist
fi

但如果你真的不喜欢重定向错误,你可以使用完成机制来执行内省。这假设您没有 bash ≤ 2.03 且没有可编程完成功能的过时机器。

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

此方法避免了分叉,分叉在某些环境(例如 Cygwin)上速度很慢。简单的也是如此2>/dev/null,我认为在性能上你无法击败它。

答案2

检查direxpand输出中是否存在shopt,如果存在则启用它:

shopt | grep -q '^direxpand\b' && shopt -s direxpand

答案3

当你确定某个特定的shopt选项在 Bash 的某个主要/次要/补丁版本中可用,您可以检查$BASH_VERSION变量或数组的元素$BASH_VERSINFO[],以便有条件地启用它。

这是 Bash 4.2.29 或更高版本的测试,该版本direxpand 首次被引入到4.2系列:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

编辑:需要明确的是,这是一个过度设计的解决方案,用于简单地忽略来自登录脚本的错误消息,但无论如何我确实想记录它,以供我自己参考。

请注意 周围的大括号,其中${BASH_VERSINFO[index]}必需的,以及使用-eqand -gt,它进行整数而不是(与语言环境相关的)词法比较。如果不加引号,则运算符的 RHS==被视为 Bash [[/]]条件中的“extglob”模式,如前所述这里,这使得比正则表达式更美观的“开始于”比较,IMO。

$BASH_VERSINFO大批包含您在以下输出中看到的所有信息bash --version

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

当它不是从 Bash 版本被支持或改变其行为的文档中可以清楚地看出shopt,Luciano 提出的方法很好:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

...就像 Gilles 提出的解决方案一样,只需忽略错误 ( shopt -s direxpand 2>/dev/null),并可能检查$?是否绝对必要。

参考:1,2,3
相关阅读:套装和购物 - 为什么是两个?

相关内容