在Bash脚本中如何使用正则表达式否定一个测试?

279

我想使用GNU bash (版本为4.0.35(1)-release (x86_64-suse-linux-gnu)),使用正则表达式来否定一个测试。例如,当路径不存在时,我想将路径条件性地添加到PATH变量中,如下所示:

TEMP=/mnt/silo/bin
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/Scripts:
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/local/bin
if [[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH; else PATH=$PATH:$TEMP; fi
export PATH

我确定有无数种方法可以做到这一点,但我想知道的是条件是否可以以某种方式被否定,就像(错误的):

TEMP=/mnt/silo/bin
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/Scripts:
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
TEMP=/mnt/silo/local/bin
if ![[ ${PATH} =~ ${TEMP} ]] ; then PATH=$PATH:$TEMP; fi
export PATH
5个回答

319

你做对了,只需要在![[之间加一个空格就可以了,像这样:if ! [[


26
哎呀哟!就在我安全地避开 Perl 的星际特殊字符疯狂时,我发现自己迷失在 bash 空间(位置)中了!(我感到害怕,它让我的肚子像一条蟒蛇一样收缩。)谢谢! - David Rogers
4
你确定它总是在 [[ 前面而不是里面,比如 if [[ ! "$value" =~ $regex ]]; 这样的写法吗?例如, Sublime Text 如果将它放在外面,会显示奇怪的高亮效果:https://i.imgur.com/AQWuFtf.png - Artfaith
IntelliJ 给出了这个警告:在 ! 后面缺少必需的空格。请参见 SC1035 - Richard Tyler Miles
1
@RichardTylerMiles 这个答案 确实 在感叹号后面有空格。您也可以将感叹号放在双括号内,但我更喜欢将其放在外面。 - SiegeX

191

你也可以将感叹号放在括号内:

if [[ ! $PATH =~ $temp ]]

但是你应该锚定模式以减少误报:

temp=/mnt/silo/bin
pattern="(^|:)$temp(:|$)"
if [[ ! $PATH =~ $pattern ]]

正则表达式会在开头或结尾寻找一个冒号(或两者都有)。我建议习惯使用小写或混合大小写的变量名,以减少与shell变量名称冲突的可能性。


啊,谢谢你提醒我关于锚定的事情。对于一个Bash初学者来说,使用小写或混合变量名的想法很令人困惑,因为到目前为止我看到的建议是使用大写字母。我理解你所说的观点,但我还没有看到足够的Bash脚本示例,以便从(我对)这本教程的理解中舒适地偏离。 - David Rogers
5
大家相信我们说的话。应该避免使用用户定义的大写变量。在Bash中,所有变量都是用$扩展的,因此没有理由将它们转换为大写以使其更加突出。 - SiegeX
我认为if [[ ! $foo =~ bar ]]if ! [[ $foo =~ bar ]]更安全,因为它使得在if语句中引入更多条件更容易。 - Cristian Todea

28

最安全的方式是将正则表达式的否定符号放在 [[ ]] 内,像这样:

if [[ ! ${STR} =~ YOUR_REGEX ]]; then

否则在某些系统上可能会失败。


11

正如SiegeX已经指出,你可以否定测试。

然而,你不应该使用正则表达式 - 如果你的路径包含特殊字符,它可能会失败。请尝试使用以下方法代替:

[[ ":$PATH:" != *":$1:"* ]]

(来源)


2
使用这种形式的另一个原因是它不会意外匹配子字符串(例如,由于“/usr/bin”已经存在,而未能将“/bin”添加到路径中)。 - Gordon Davisson
我花了一些时间才明白左边的冒号如何给我想要的锚定。将模式通过在要搜索的字符串上添加材料来缩小是值得记住的思路。我不明白为什么路径中的特殊字符会破坏正则表达式的解决方案,但对bash模式解决方案却没影响。你能举个例子吗? - David Rogers
我认为这种方法在所有情况下都不能可靠地工作。在我的看法中,正则表达式匹配更优秀。 - Felipe Alvarez

7

我喜欢在不使用条件运算符的情况下简化代码,例如:

TEMP=/mnt/silo/bin
[[ ${PATH} =~ ${TEMP} ]] || PATH=$PATH:$TEMP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接