我在一个相对异构的环境中工作,我可能在不同的 HPC 节点、虚拟机或我的个人工作站上运行不同版本的 Bash。因为我将登录脚本放在 Git 存储库中,所以我希望.bashrc
全面使用相同的脚本,而不会有很多“如果这个主机,则...”类型的混乱。
我喜欢Bash ≤ 4.1 的默认行为会扩展cd $SOMEPATH
为cd /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]}
是必需的,以及使用-eq
and -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
相关阅读:套装和购物 - 为什么是两个?