How do I negate a test with regular expressions in a bash script?
Asked Answered
R

5

281

Using GNU bash (version 4.0.35(1)-release (x86_64-suse-linux-gnu), I would like to negate a test with Regular Expressions. For example, I would like to conditionally add a path to the PATH variable, if the path is not already there, as in:

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

I'm sure there are a million ways to do this, but what I would like to know is if the conditional can be negated somehow, as in (the erroneous):

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
Revolutionize answered 27/12, 2010 at 23:53 Comment(0)
C
326

You had it right, just put a space between the ! and the [[ like if ! [[

Chaffer answered 28/12, 2010 at 0:0 Comment(4)
Oye vey! Just when I safely sidestep the intergalactic special character madness of perl, I find myself lost in bash space (placement)! (I feel fear squeezing my gut like a python.) Thanks!Revolutionize
Are you sure that it's always before [[ and not inside, like if [[ ! "$value" =~ $regex ]];? For example, Sublime Text highlights it weirdly if outside: i.imgur.com/AQWuFtf.pngSelfknowledge
intelliJ gives this warning; You are missing a required space after the !. See SC1035.Renz
@RichardTylerMiles this answer does have the space after the '!'. You can also put the '!' inside the double-brackets, I just prefer it outside.Chaffer
I
193

You can also put the exclamation point inside the brackets:

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

but you should anchor your pattern to reduce false positives:

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

which looks for a match at the beginning or end with a colon before or after it (or both). I recommend using lowercase or mixed case variable names as a habit to reduce the chance of name collisions with shell variables.

Itin answered 28/12, 2010 at 2:17 Comment(3)
Ah, thanks for the reminder about anchoring. The idea of using lowercase or mixed variable names is confusing to a bash beginner, since the advice I have seen so far is to use uppercase. I understand the point you are making, but I have not seen enough bash scripting examples to feel comfortable deviating from (my understanding of) the cookbook.Revolutionize
@anyoneis trust us on this one. Use of user-defined uppercase variables should be avoided. All variables in bash are expanded with $ so there is no reason to uppercase them to make them stand out.Chaffer
I find if [[ ! $foo =~ bar ]] safer than if ! [[ $foo =~ bar ]], because it makes easier to introduce more conditions to the ifNorthman
I
29

the safest way is to put the ! for the regex negation within the [[ ]] like this:

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

otherwise it might fail on certain systems.

Insolent answered 21/10, 2011 at 7:32 Comment(0)
O
11

Yes you can negate the test as SiegeX has already pointed out.

However you shouldn't use regular expressions for this - it can fail if your path contains special characters. Try this instead:

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

(Source)

Operation answered 28/12, 2010 at 0:0 Comment(3)
Another reason to use this form it that it won't accidentally match substrings (e.g. fail to add "/bin" to the path because "/usr/bin" is already there).Compotation
It took me a while to understand how the colons on the left gave me the anchoring I wanted. the idea of reducing the pattern by adding material to the string to be searched is worth remembering. I don't understand why special characters in the path would disrupt the regex solution but not the bash pattern solution. Can you give me an example?Revolutionize
I don't think this will work reliably in all cases. Regex matching in superior IMHOEpizoon
F
7

I like to simplify the code without using conditional operators in such cases:

TEMP=/mnt/silo/bin
[[ ${PATH} =~ ${TEMP} ]] || PATH=$PATH:$TEMP
Fogarty answered 4/10, 2017 at 9:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.