Bash match string with regex - regex

I am using bash 4.1.10(4)-release and I trying to use a regex to match against two capital letters [A-Z]{2} and then anything. So for example BXCustomerAddress, CAmaterial would be acceptable, but WarehouseMessage would not. I have the following script for testing purposes:
#!/bin/bash
if [[ "Ce" =~ [A-Z]{2} ]]; then
echo "match"
fi
My questions are:
When I run this script, why does it return that I have a match?
Does anyone know how I could achieve my desired results?

Looks like you have shopt nocaseglob turned on. Turn it off using:
shopt -u nocaseglob
Now [[ "Ce" =~ [A-Z]{2} ]] should not match and will return false.

Check the value of the shell option nocasematch:
$ shopt nocasematch
nocasematch off

shopt nocasematch is probably set to on. Turn it off with
shopt -u nocasematch
From the Bash Reference Manual:
nocasematch
If set, Bash matches patterns in a case-insensitive fashion when
performing matching while executing case or [[ conditional commands.

After trying out many different combinations, this is what gave me the expected behavior:
#!/bin/bash
# [A-Z][A-Z] will not work
# [:upper:][:upper:] will not work
# [[A-Z]][[A-Z]] will not work
# [[:upper:]][[:upper:]] does work
echo "test one"
if [[ "CA" =~ ^([[:upper:]][[:upper:]])+ ]]; then
echo "match"
fi
echo "test two"
if [[ "Ce" =~ ^([[:upper:]][[:upper:]])+ ]]; then
echo "match"
fi
I get the expected results of:
test one
match
test two
Thanks for everyone's help

Related

how to construct regex to compare case insensitive strings in shell script?

I am passing command line arguments to a shell script and it is being compared aganist a regular expression. The following code is case-sensitive:
[[ $1 =~ ^(cat)|(dog)$ ]] && echo "match" || echo "no match"
How can I modify this regex that will ignore cases? I would be able to pass cAt and it should match.
I want to use /i regex flag as it ignores cases. But how do I use it inside a shell script? I have tried [[ $1 =~ /(cat)|(dog)/i ]] but the script exited with a syntax error.
StackOverflow has a similar question but it does not answer my inquiry. I want to use test to compare both strings and not interested to use shopt -s nocasematch or grep <expression>
just use
shopt -s nocasematch
before your command.
alternatively
shopt -s nocasematch && [[ 'doG' =~ (cat)|(dog) ]] && echo 'hi' || echo 'no match'

Regex stored in a shell variable doesn't work between double brackets

The below is a small part of a bigger script I'm working on, but the below is giving me a lot of pain which causes a part of the bigger script to not function properly. The intention is to check if the variable has a string value matching red hat or Red Hat. If it is, then change the variable name to redhat. But it doesn't quite match the regex I've used.
getos="red hat"
rh_reg="[rR]ed[:space:].*[Hh]at"
if [ "$getos" =~ "$rh_reg" ]; then
getos="redhat"
fi
echo $getos
Any help will be greatly appreciated.
There are a multiple things to fix here
bash supports regex pattern matching within its [[ extended test operator and not within its POSIX standard [ test operator
Never quote our regex match string. bash 3.2 introduced a compatibility option compat31 (under New Features in Bash 1.l) which reverts bash regular expression quoting behavior back to 3.1 which supported quoting of the regex string.
Fix the regex to use [[:space:]] instead of just [:space:]
So just do
getos="red hat"
rh_reg="[rR]ed[[:space:]]*[Hh]at"
if [[ "$getos" =~ $rh_reg ]]; then
getos="redhat"
fi;
echo "$getos"
or enable the compat31 option from the extended shell option
shopt -s compat31
getos="red hat"
rh_reg="[rR]ed[[:space:]]*[Hh]at"
if [[ "$getos" =~ "$rh_reg" ]]; then
getos="redhat"
fi
echo "$getos"
shopt -u compat31
But instead of messing with those shell options just use the extended test operator [[ with an unquoted regex string variable.
There are two issues:
First, replace:
rh_reg="[rR]ed[:space:].*[Hh]at"
With:
rh_reg="[rR]ed[[:space:]]*[Hh]at"
A character class like [:space:] only works when it is in square brackets. Also, it appears that you wanted to match zero or more spaces and that is [[:space:]]* not [[:space:]].*. The latter would match a space followed by zero or more of anything at all.
Second, replace:
[ "$getos" =~ "$rh_reg" ]
With:
[[ "$getos" =~ $rh_reg ]]
Regex matches requires bash's extended test: [[...]]. The POSIX standard test, [...], does not have the feature. Also, in bash, regular expressions only work if they are unquoted.
Examples:
$ rh_reg='[rR]ed[[:space:]]*[Hh]at'
$ getos="red Hat"; [[ "$getos" =~ $rh_reg ]] && getos="redhat"; echo $getos
redhat
$ getos="RedHat"; [[ "$getos" =~ $rh_reg ]] && getos="redhat"; echo $getos
redhat

Regex not matching name in filepath

I have a folder with ipa files. I need to identify them by having a appstore or enterprise in the filename.
mles:drive-ios-swift mles$ ls build
com.project.drive-appstore.ipa
com.project.test.swift.dev-enterprise.ipa
com.project.drive_v2.6.0._20170728_1156.ipa
I've tried:
#!/bin/bash -veE
fileNameRegex="**appstore**"
for appFile in build-test/*{.ipa,.apk}; do
if [[ $appFile =~ $fileNameRegex ]]; then
echo "$appFile Matches"
else
echo "$appFile Does not match"
fi
done
However nothing matches:
mles:drive-ios-swift mles$ ./test.sh
build-test/com.project.drive-appstore.ipa Does not match
build-test/com.project.drive_v2.6.0._20170728_1156.ipa Does not match
build-test/com.project.test.swift.dev-enterprise.ipa Does not match
build-test/*.apk Does not match
How would the correct script look like to match build-test/com.project.drive-appstore.ipa?
You are confusing between the glob string match with a regex match. For a greedy glob match like * you can just use the test operator with ==,
#!/usr/bin/env bash
fileNameGlob='*appstore*'
# ^^^^^^^^^^^^ Single quote the regex string
for appFile in build-test/*{.ipa,.apk}; do
# To skip non-existent files
[[ -e $appFile ]] || continue
if [[ $appFile == *${fileNameGlob}* ]]; then
echo "$appFile Matches"
else
echo "$appFile Does not match"
fi
done
produces a result
build-test/com.project.drive_v2.6.0._20170728_1156.ipa Does not match
build-test/com.project.drive-appstore.ipa Matches
build-test/com.project.test.swift.dev-enterprise.ipa Does not match
(or) with a regex use greedy match .* as
fileNameRegex='.*appstore.*'
if [[ $appFile =~ ${fileNameRegex} ]]; then
# rest of the code
That said to match your original requirement to match enterprise or appstore string in file name use extended glob matches in bash
Using glob:
shopt -s nullglob
shopt -s extglob
fileExtGlob='*+(enterprise|appstore)*'
if [[ $appFile == ${fileExtGlob} ]]; then
# rest of the code
and with regex,
fileNameRegex2='enterprise|appstore'
if [[ $appFile =~ ${fileNameRegex2} ]]; then
# rest of the code
You can use the following regex to match appstore and enterprise in a filename:
for i in build-test/*; do if [[ $i =~ appstore|enterprise ]]; then echo $i; fi; done

Matching regex in bash

I'm trying to match the parameters of a bash script with a regex
mykill.bash [-l] [-s SIGNO] pattern1 pattern2
I'm using this expression:
regex = ^(-l)?(\s-s\s[0-9]+)?(\s[a-zA-Z0-9]+){1,2}$ <br>
if [[ $# =~ $regex ]]; then echo 'cool'
for example ./mykill.bash -l -s 33 abc gives $#='-l -s 33 abc' which passes the debuggex.com tests (see image
but it doesn't work in my script
You have bash problems, not a regex problem.
When assigning variables in bash: no space around the = please.
Then if you want to preserve backslashes and whitespace in the regex, use single quotes around it, otherwise bash eats them for breakfast. You don't need to quote cool. And close the if with a fi.
regex='^(-l)?(\s-s\s[0-9]+)?(\s[a-zA-Z0-9]+){1,2}$ <br>'
if [[ $# =~ $regex ]]; then echo cool; fi
Or use the simpler form of the conditional:
[[ $# =~ $regex ]] && echo cool
Some versions of bash does not catch \s the way we are used to in other languages/flavors.
One version it does not catch \s is on my MacBook Air with GNU bash, version 3.2.48(1)-release-(x86_64-apple-darwin12).
And you should not have spaces around = when assigning a value to a variable.
As Ken-Y-N said in the comments to your post, your pattern has some problems as well, I fixed it in my code below.
This should do it if \s is the problem:
#!/bin/bash
re='^(-l\ )?(-s\ [0-9]+\ )?([a-zA-Z0-9]+)(\ [a-zA-Z0-9]+)?$'
if [[ $# =~ $re ]]; then
echo 'cool'
fi
There's no need to escape the spaces like i did, but I find it easier to read this way.
As per your input, this will match
if [[ $# =~ -l\ -s\ [0-9]+\ [a-zA-Z]+ ]]
then
echo 'cool'
else
echo 'check again'
fi

Why does BASH_REMATCH not work for a quoted regular expression?

The code is like this:
#!/bin/bash
if [[ foobarbletch =~ 'foo(bar)bl(.*)' ]]
then
echo "The regex matches!"
echo $BASH_REMATCH
echo ${BASH_REMATCH[1]}
echo ${BASH_REMATCH[2]}
fi
When I try to run it, it doesn't display anything:
bash-3.2$ bash --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
Copyright (C) 2007 Free Software Foundation, Inc.
bash-3.2$ /bin/bash test_rematch.bash
bash-3.2$
Does anyone have ideas about this?
In your bash REGEX, you should remove quotes. That's why that doesn't work.
If you have space, I recommend to use this way :
#!/bin/bash
x='foo bar bletch'
if [[ $x =~ foo[[:space:]](bar)[[:space:]]bl(.*) ]]
then
echo The regex matches!
echo $BASH_REMATCH
echo ${BASH_REMATCH[1]}
echo ${BASH_REMATCH[2]}
fi
You can also assign the quoted regexp to a variable:
#!/bin/bash
x='foobarbletch'
foobar_re='foo(bar)bl(.*)'
if [[ $x =~ $foobar_re ]] ; then
echo The regex matches!
echo ${BASH_REMATCH[*]}
echo ${BASH_REMATCH[1]}
echo ${BASH_REMATCH[2]}
fi
This not only supports simple quoting but gives the regexp a name which can help readability.
Thanks to your debugging statement, echo The regex matches!, you should have noticed there is no problem with BASH_REMATCH, since the if statement evaluates to false.
In bash, regular expressions used with =~ are unquoted. If the string on the right is quoted, then it is treated as a string literal.
If you want to include whitespaces in your regex's, then use the appropriate character classes, or escape your space if you want a space.