如何防止不支持的“购物”选项引起我的.bashrc错误?


9

我在相对异构的环境中工作,其中我可能在不同的HPC节点,VM或我的个人工作站上运行不同版本的Bash。因为我将登录脚本放在了Git仓库中,所以我想全面使用相同.bashrc的命令,而不会出现很多“如果是此主机,则...”类型的混乱情况。

喜欢 Bash≤4.1的默认行为,该行为会在按下键时扩展cd $SOMEPATH为。在bash 4.2及以上,则需要重新启用此行为,而并没有变得可用,直到4.2.29。不过,这只是一个例子。另一个可能相关的选项(尽管我不确切知道它的作用)也可能更改了v4.2的默认行为。cd /the/actual/pathTabshopt -s direxpandshoptcomplete_fullquote

但是,direxpand早期版本的Bash无法识别该错误,如果我尝试在shopt -s direxpand中运行.bashrc,则每次我使用较旧的Bash登录到节点时,都会在控制台上显示一条错误消息:

-bash: shopt: direxpand: invalid shell option name

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


我的答案没有帮助吗?
卢西亚诺·安德列斯·马提尼

@LucianoAndressMartini确实做到了,这就是我最终自己解决的解决方案.bashrc。我还是想要记录一下如何使用它$BASH_VERSINFO来询问正在运行的shell的主要/次要版本,以达到我自己的目的,这就是为什么我完成发布自己的答案的原因。:)
TheDudeAbides

看我的回答,我对将程序版本与Shell脚本进行比较有一些了解。
卢西亚诺·安妮丝·马蒂尼

Answers:


14

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

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

4
最好使它grep -q '^direxpand\b'在将来的bash版本或bash分支中包含一个作为子字符串包含删除的选项direxpand。在这种特定情况下不太可能,但是健壮起来并不需要花费太多。
吉尔斯(Gilles)'所以

谢谢卢西亚诺。我想回答我自己的问题,但是我的编辑经过同行评审之后,我会接受您的回答。也许您可以自己批准它们?
TheDudeAbides

4
Bash允许查询特定的shell选项,因此可以使用[ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand。没有更多的正则表达式问题!:-)
David Foerster,

@DavidFoerster我要扭转逻辑:用[ -n "blah" ] && shopt blah这种方式来说,是说“如果不支持direxpand,那么就不要这样做”。
丰富

1
@Rich:我的大多数shell脚本都包含set -e在顶部,因此我倾向于以这种方式使用快捷逻辑。
David Foerster,

16

我看不到将错误重定向到怎么了/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,我认为您不能在性能上胜过它。


不是我的大脑去的地方,但是我喜欢这个compgen建议。那就是大学级别的东西!避免重定向到/dev/null只是个人喜好。我想请求允许而不是宽恕,如果这有意义吗?:)
TheDudeAbides

+1是Bash可编程完成中完全出乎意料的教育,这迫使我不得不去阅读手册来解释compgen -A shopt -X ...甚至是什么意思。
TheDudeAbides

4
我读过有关compgenUnix和Linux上使用这种方式的@TheDudeAbides ,但我不知道是谁首先提出的。(在程序完成编程之前,我不再使用bash作为我的主外壳。)在编程中,寻求许可通常是一个糟糕的主意,因为由于编码的原因,存在权限检查与实际操作不匹配的风险错误(您没有完全检查自己认为要检查的内容),或者因为使用之前检查过的内容已更改
吉尔斯(Gilles)'所以

5

当您确定某个特定的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

编辑:明确地说,这是一个可笑的过度设计的解决方案,它只是忽略了来自您的登录脚本的错误消息,但是出于我自己的喜好,我确实想对其进行记录。

注意周围的括号,这必需的,使用和,这做的整数,而不是(区域设置相关的)词汇的比较。如果不带引号,则的RHS 操作被视为猛砸内“extglob”模式/ 条件语句,如注意这里,这使得更美观的“开始使用”比较不是一个正则表达式将,IMO。${BASH_VERSINFO[index]}-eq-gt==[[]]

$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")'

当它是不是从文件明确shopt在该版本的Bash(S)成为支持或改变他们的行为,由卢西亚诺提出的方法是好的:

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

...就像吉尔(Gillles shopt -s direxpand 2>/dev/null)提出的解决方案一样,就是忽略错误(),也许检查$?是否绝对必要。

参考文献:123
相关阅读:设置和禁用了javascript -为什么是两个?


您可能还可以使用类似if [[ $BASH_VERSION > 4.3 ]];(相匹配4.3.05.0等等,也是4.3.0-alpha我不知道后来的事实问题。)
ilkkachu

嗨@ilkkachu 感谢您所做的编辑,以涵盖Bashv5.x。该direxpand选项确实可用于Bash 4.2。我已经通过验证的该泊坞窗图像在v4.2.53运行docker run --rm bash:4.2 bash -c shopt | grep direxpand(和良好的措施,它确实是提供v4.1.17运行docker run --rm bash:4.1 bash -c shopt | grep direxpand)。
TheDudeAbides

啊,好的,我进行了测试4.2.0,结果发现它在那里不起作用。该更新日志也提到它加入bash-4.3-alpha。我想那时候需要检查${BASH_VERSINFO[2]}一下是否准确,但是我不知道哪个点发布了它……
ilkkachu

我认为我们已经基本证明了Gilles的观点。实际上,最好是尝试启用shell选项,然后在不支持该错误的情况下对其进行处理(或抑制该错误)。
TheDudeAbides
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.