I am trying to create a git commit-msg hook, which includes a simple regex to verify the log message. It is not working using the usual bash syntax.
I tried it using a very simple regex, and it is still not working
Any idea what I might be doing wrong?
Here is a simple example including an obvious match, but it is printing out "no match"
#!/bin/bash
message="[GLSD-0000]"
echo "Message: '$message'"
regex="\[GLSD-\w+\]"
if [[ $message =~ $regex ]]; then echo "yes";else echo "no match!"; fi
Using git version 2.18.0.windows.1
A few things to note from you attempt made,
Your interpreter does #!/bin/sh does not support the regex operation in bash, even-though you have not used it. You should be using the bourne again shell bash (#!/usr/bin/env bash).
The [[..]] with an = does a string match on left side operand with the right side operand. For regular expression support, you need to use the ~ operator
The bash only supports a minimal version of ERE (Extended Regular expression) in which \w (all words) is not supported.
So in a script targeted for a bash shell, you should be writing it as
#!/usr/bin/env bash
# equivalent of '\w' in the regex flavor supported by bash
re='\[GLSD-[A-Za-z0-9_]+\]'
msg='[GLSD-0000]'
[[ $msg =~ $re ]] && echo "yes" || echo "no match!"
Related
So I created myself a cute little bash script:
#!/usr/bin/env bash
TEXT="FOO BAR"
REGEX="FOO (.+)"
if [[ $TEXT =~ $REGEX ]]; then
echo "Matched ${BASH_REMATCH[1]}"
else
echo "No match."
fi
Pretty basic. If I run ./cutescript.sh, then I get the result:
Matched BAR
But if I source the script, . ./cutescript.sh, then it still matches, but BASH_REMATCH[1 is empty.
Wat.
I'm on MacOS Catalina 10.15.7 (19H2), executing this in zsh, but with the bash shebang.
Can anyone explain this irregularity?
When you read the file with ., it gets executed by whatever shell you're running - in this case, you said you're in zsh. The name of the BASH_REMATCH array is, as the name implies, specific to bash; if your shell is zsh, for example, then the matched text in this case would be found in $match[1] instead. And I don't think ksh does true regex matching at all.
Now, armed with the knowledge that BASH_REMATCH doesn't exist natively in zsh, I did a little more digging:
This post is actually a duplicate. There's another question here that explains the solution: BASH_REMATCH doesn't capture
Setting options KSH_ARRAYS BASH_REMATCH allows zsh to emulate bash's regular expression features.
A simple way to make the above script compatible with zsh is:
#!/usr/bin/env bash
# Ensures that BASH_REMATCH works if called in zsh.
setopt KSH_ARRAYS BASH_REMATCH
TEXT="FOO BAR"
REGEX="FOO (.+)"
if [[ $TEXT =~ $REGEX ]]; then
echo "Matched ${BASH_REMATCH[1]}"
else
echo "No match."
fi
unsetopt KSH_ARRAYS BASH_REMATCH
Also another related question: What is the zsh equivalent for $BASH_REMATCH[]?
I know I can use grep, awk etc, but I have a large set of bash scripts that have some conditional statements using =~ like this:
#works
if [[ "bar" =~ "bar" ]]; then echo "match"; fi
If I try and get it to do a logical OR, I can't get it to match:
#doesn't work
if [[ "bar" =~ "foo|bar" ]]; then echo "match"; fi
or perhaps this...
#doesn't work
if [[ "bar" =~ "foo\|bar" ]]; then echo "match"; fi
Is it possible to get a logical OR using =~ or should I switch to grep?
You don't need a regex operator to do an alternate match. The [[ extended test operator allows extended pattern matching options using which you can just do below. The +(pattern-list) provides a way to match one more number of patterns separated by |
[[ bar == +(foo|bar) ]] && echo match
The extended glob rules are automatically applied when the [[ keyword is used with the == operator.
As far as the regex part, with any command supporting ERE library, alternation can be just done with | construct as
[[ bar =~ foo|bar ]] && echo ok
[[ bar =~ ^(foo|bar)$ ]] && echo ok
As far why your regex within quotes don't work is because regex parsing in bash has changed between releases 3.1 and 3.2. Before 3.2 it was safe to wrap your regex pattern in quotes but this has changed in 3.2. Since then, regex should always be unquoted.
You should protect any special characters by escaping it using a backslash. The best way to always be compatible is to put your regex in a variable and expand that variable in [[ without quotes. Also see Chet Ramey's Bash FAQ, section E14 which explains very well about this quoting behavior.
I have a CSV being read into a script that has the phrases:
This port supports SSLv3/TLSv1.0.
This port supports TLSv1.0/TLSv1.1/TLSv1.2.
This port supports TLSv1.2.
What I'm looking to do is setup a REGEX variable on the word/number: TLSv1.0
Then reference that variable in an IF/Then statement. The problem I'm
having is getting the regex to see the TLSv1.0. Could somebody help me
craft my BASH script to see TLSv1.0 when it's along a line that starts off with "This port supports"?
#!/bin/sh
REGEX="\TLSv1.0\"
cat filename.csv | awk -F"," '{gsub(/\"/,"",$4);print $5}' | sed s/\"//g |
while IFS=" " read pluginoutput
do
if [[ "$pluginoutput" =~ $REGEX ]]; then
.
. rest of my code
.
You can see that I'm trying to set the regex in the variable, but it just isn't working. Obviously a typo or something. Does anybody have a regex suggestion?
Thanks,
There are a lot of things wrong here. To pick some key ones:
#!/bin/sh specifies that you want your script to be interpreted with a POSIX-compliant interpreter, but doesn't specify which one. Many of these, like ash or dash, don't have [[ ]], =~, or other extensions which your code depends on. Use #!/bin/bash instead.
In REGEX="\TLSv1.0\", the "s are data, not syntax. This means that they're part of the content being searched for when you do [[ $string =~ $regex ]]. By contrast, regex=TLSv1.0, regex="TLSv1.0" or regex='TLSv1.0' will all have the identical effect, of assigning TLSv1.0 as the content of the regex variable.
That said, as a point on regex syntax, you probably want regex='TLSv1[.]0' -- that way it will only match a ., as opposed to treating the dot as a match-any-character wildcard (as it is in regular-expression syntax).
Personally, I might do something more like the following (if I had a reason to do the parsing in bash rather than to let a single egrep call process all your input):
#!/bin/bash
regex='(^|,)"?This port supports .*TLSv1[.]0.*[.]"?($|,)'
while IFS= read -r line; do
[[ $line =~ $regex ]] && echo "Found TLSv1.0 support"
done
I moved my server to a new one (cheaper). Both have the same Linux (CentOS) in different versions (5.0 and 6.5). I have a shell script that filters a line out of a log:
if [ -f $URLFILE ]
then
echo "File found, getting userinfo..."
while read line;
do
if [[ $line =~ ".Userlist: .*" ]]
then
echo "Found user information."
echo $line > /home/....net.txt;
...
So, if the red line matches the regex, it should be echoed into the file. This works fine on the old system but the regex does not match on the new system (without any other changes). The regex is correct as far as a regex tester on the Internet tells me.
There are 2 problems:
sh doesn't support regex matching using =~ like bash
Regex should NOT be quoted and your variable should preferably be quoted.
You can use this in BASH:
if [[ "$line" =~ Userlist:[[:blank:]] ]]
OR just avoid using regex altogether and use glob matching:
if [[ "$line" == *"Userlist: "* ]]
my programs starts some services and store its output in tmp variable and I want to match the variable's content if it starts with FATAL keyword or not? and if it contains I will print Port in use using echo command
For example if tmp contains FATAL: Exception in startup, exiting.
I can do it by sed: echo $tmp | sed 's/^FATAL.*/"Port in use"/'
but I want to use the builtin if to match the pattern.
How can I use the shell built in features to match REGEX?
POSIX shell doesn't have a regular expression operator for UNIX ERE or PCRE. But it does have the case keyword:
case "$tmp" in
FATAL*) doSomethingDrastic;;
*) doSomethingNormal;;
esac
You didn't tag the question bash, but if you do have that shell you can do some other kinds of pattern matching or even ERE:
if [[ "$tmp" = FATAL* ]]; then
…
fi
or
if [[ $tmp =~ ^FATAL ]]; then
…
fi
if [ -z "${tmp%FATAL*}" ]
then echo "Start with"
else
echo "Does not start with"
fi
work on KSH, BASH under AIX. Think it's also ok under Linux.
It's not a real regex but the limited regex used for file matching (internal to the shell, not like sed/grep/... that have their own version inside) of the shell. So * and ? could be used