Why bash if =~ regex negate a character do not work - regex

test="blah*blah"
if ! [[ ${test} =~ [\*] ]] ; then echo ok; else echo failed; fi
=> failed
test="blah/blah"
if ! [[ ${test} =~ [\*] ]] ; then echo ok; else echo failed; fi
=> ok
test="blah/blah"
if [[ ${test} =~ [^\*] ]] ; then echo ok; else echo failed; fi
=> ok
test="blah*blah"
if [[ ${test} =~ [^\*] ]] ; then echo ok; else echo failed; fi
=> ok ??? WHY ?
What is the secret to use a negate pattern for a character somewhere in string ?

Your regular expression (no need to escape * inside a bracket expression)
[^*]
matches any single character that is not an *. Because regular expressions are not implicitly anchored, as long as any character in the string is not a *, the match succeeds. Anchoring it
^[^*]$
matches exactly those one-character strings that are not *. It won't match any longer string.
If you add a * after it, you get a regular expression that matches 0 or more consecutive characters that are not *. Anchoring it gives you a regular expression that matches any string (including the empty string) that consists of only non-* characters.
^[^*]*$
If you only want to match non-empty strings, makes sure the string starts with non-* character, than check that the rest (if any) are also not *.
^[^*][^*]*$
That can be shortened by using + instead of * for repetition. (* matches 0 or more; + matches 1 or more.)
^[^*]+$

Related

bash IF not matching variable that contains regex numbers

DPHPV = /usr/local/nginx/conf/php81-remi.conf;
I am unable to figure out how to match a string that contains any 2 digits:
if [[ "$DPHPV" =~ *"php[:digit:][:digit:]-remi.conf"* ]]
You are not using the right regex here as * is a quantifier in regex, not a placeholder for any text.
Actually, you do not need a regex, you may use a mere glob pattern like
if [[ "$DPHPV" == *php[[:digit:]][[:digit:]]-remi.conf ]]
Note
== - enables glob matching
*php[[:digit:]][[:digit:]]-remi.conf - matches any text with *, then matches php, then two digits (note that the POSIX character classes must be used inside bracket expressions), and then -rem.conf at the end of string.
See the online demo:
#!/bin/bash
DPHPV='/usr/local/nginx/conf/php81-remi.conf'
if [[ "$DPHPV" == *php[[:digit:]][[:digit:]]-remi.conf ]]; then
echo yes;
else
echo no;
fi
Output: yes.

Regex/Shell - how to match all except those with specific pattern

I need a regex in shell to match all strings except those with specific pattern.
My specific pattern can be variable, i.e. (i|I)[2 digits numbers](u|U)[2 digits numbers] in every string should not match.
For example :
Some.text.1234.text => should match
Some.text.1234.i10u20.text => shouldn't match
Some.text.1234.I01U02.text => shouldn't match
Some.text.1234.i83U23.text => shouldn't match
You can try with that:
^(?!.*[tuTU]\d{2}).*$
Demo
Explanation:
^ start of a line
?!.* negative look ahead
[tuTU]\d{2} check if there exists such character following 2 digits only
.*$ if previous condition is negative then match entire string to end of string $
The Bash script checking if a string matches a regex or not can look like
f='It_is_your_string_to_check';
if [[ "${f^^}" =~ I[0-9]{2}U[0-9]{2} ]]; then
echo "$f is invalid";
else
echo "$f is valid"
fi;
Here, "${f^^}" turns the string into uppercase (so as not to use (U|u) and (I|i)), and then =~ operator triggers a regex check here since the pattern on the right side is not quoted. You may play it safe and define the regex pattern with a separate single-quoted string variable and use
rx='I[0-9]{2}U[0-9]{2}'
if [[ "${f^^}" =~ $rx ]]; then ...
See a Bash demo online:
s='Some.text.1234.text
Some.text.1234.i10u20.text
Some.text.1234.I01U02.text
Some.text.1234.i83U23.text'
for f in $s; do
if [[ "${f^^}" =~ I[0-9]{2}U[0-9]{2} ]]; then
echo "$f is invalid";
else
echo "$f is valid"
fi;
done;
Output:
Some.text.1234.text is valid
Some.text.1234.i10u20.text is invalid
Some.text.1234.I01U02.text is invalid
Some.text.1234.i83U23.text is invalid

How to match signs in Bash?

How to match below signs in Bash?
+
-
/
*
This my code which I try to use but it did not work.
is_sign(){
yoursign=$1
re="^[+,-,\/,\*]$"
if ! [[ $yoursign =~ $re ]] ; then
echo "Not a sign"
return 2
else
return 0
fi
}
is_sign $1
The pattern you used contains the following issues:
You escape special characters thinking it will make them literal chars, but in fact, in POSIX bracket expressions, backslashes are treated as regular literal backslashes, and all you did is you also allow a literal \ to be matched with the regex
- must be either at the start or end of a POSIX bracket expression if you want to match a literal hyphen
By adding commas to the regex, it can now also match a comma.
Use
re='^[+/*-]$'
Demo:
is_sign() {
yoursign=$1
re='^[+/*-]$'
if ! [[ $yoursign =~ $re ]] ; then
echo "Not a sign"
#return 2
else
echo "Yeah, is a sign"
#return 0
fi
}
echo "$(is_sign "+")" # => Yeah, is a sign
echo "$(is_sign "m")" # => Not a sign
You have two solutions:
Regular expression:
re='^[+/*-]$'
if ! [[ $yoursign =~ $re ]] ; then
Glob:
glob='[+/*-]'
if ! [[ $yoursign == $glob ]] ; then
Please note that the minus sign - is special in a character class (both in a glob and in a regular expression), so you need to either put it in the beginning or at the end

Bash Regex comparison not working

keyFileName=$1;
for fileExt in "${validTypes[#]}"
do
echo $fileExt;
if [[ $keyFileName == *.$fileExt ]]; then
keyStatus="true";
fi
done;
I am trying to check the file extension of a file passed in against an array of multiple file extensions. However it doesn't seem to be working properly. Any help?
validTypes=(".txt" ".mp3")
keyFileName="$1"
for fileExt in "${validTypes[#]}"
do
echo $fileExt;
if [[ $keyFileName =~ ^.*$fileExt$ ]]; then
keyStatus="true";
echo "Yes"
fi
done;
Effectively, you could change your if statement to either:
if [[ $keyFileName == ?*$fileExt ]] # Glob pattern case, ? denotes single char
or:
if [[ $keyFileName =~ .*$fileExt ]] # Regex case, . denotes single char
Looping over the array to do a regex match on each element seems rather inefficient. You're using regex; it's easy to combine the expressions and avoid looping at all.
Mangling the array into a valid regex is not entirely trivial, though. Here's my attempt:
validTypes=('\.txt' '\.mp3')
fileExtRe=$(printf '|%s' "${validTypes[#]}"
# Trim off the first alternation, add parens and anchor
fileExtRe="(${fileExtRe#?})$"
if [[ $keyFileName =~ $fileExtRe ]]; then
:
Notice how the elements in validTypes are regular expressions now, with the dot escaped to only match a literal dot.

How should I get bash 3.2 to find a pattern between wildcards

Trying to compare input to a file containing alert words,
read MYINPUT
alertWords=( `cat "AlertWordList" `)
for X in "${alertWords[#]}"
do
# the wildcards in my expression do not work
if [[ $MYINPUT =~ *$X* ]]
then
echo "#1 matched"
else
echo "#1 nope"
fi
done
The =~ operator deals with regular expressions, and so to do a wildcard match like you wanted, the syntax would look like:
if [[ $MYINPUT =~ .*$X.* ]]
However, since this is regex, that's not needed, as it's implied that it could be anywhere in the string (unless it's anchored using ^ and/or $, so this should suffice:
if [[ $MYINPUT =~ $X ]]
Be mindful that if your "words" happen to contain regex metacharacters, then this might do strange things.
I'd avoid =~ here because as FatalError points out, it will interpret $X as a regular expression and this can lead to surprising bugs (especially since it's an extended regular expression, so it has more special characters than standard grep syntax).
Instead, you can just use == because bash treats the RHS of == as a globbing pattern:
read MYINPUT
alertWords=($(<"AlertWordList"))
for X in "${alertWords[#]}"
do
# the wildcards in my expression do work :-)
if [[ $MYINPUT == *"$X"* ]]
then
echo "#1 matched"
else
echo "#1 nope"
fi
done
I've also removed a use of cat in your alertWords assignment, as it keeps the file reading inside the shell instead of spawning another process to do it.
If you want to use patterns, not regexes for matching, you can use case:
read MYINPUT
alertWords=( `cat "AlertWordList" `)
for X in "${alertWords[#]}"
do
# the wildcards in my expression do not work
case "$MYINPUT" in
*$X* ) echo "#1 matched" ;;
* ) echo "#1 nope" ;;
esac
done