1

I need to capture groups from a regular expression. But it seems I fail at grasping the concept of the bash variable BASH_REMATCH, as I can't get some groups. Here is my code:

# I want to get the values around the first '=' if it exists
inp="short =  some word  long = span desc=sth to ' be ' described value=45"
regex="\s*(\w*)\s*=\s*(.*)"

if [[ $inp =~ $regex ]]; then 
  echo; 
  echo -e "input: \"$inp\""; 
  echo -e "regex: \"$regex\"";   
  echo "matching groups: ${#BASH_REMATCH[*]}"; 
  for i in $(seq 0 $(( ${#BASH_REMATCH[*]}-1 ))); do 
    echo -e "$i: \"${BASH_REMATCH[$i]}\""; 
  done; 
fi

input: "short =  some word  long = span desc=sth to ' be ' described value=45"
regex: "\s*(\w*)\s*=\s*(.*)"
matching groups: 3
0: "=  some word  long = span desc=sth to ' be ' described value=45"
1: ""
2: "  some word  long = span desc=sth to ' be ' described value=45"

I expect the first group to be "short". Why isn't it recognized? If I test my regular expression on regex101.com it tells me the Group 1 i `short". Here is the link: https://regex101.com/r/oZGQS6/1


Edit 1

The first group is recognized using sed (I used the same regex except that I escaped the grouping parenthesis):

$ sed 's/\s*\(\w*\)\s*=\s*\(.*\)/\1\n\2/' <<< $inp
short
some word  long = span desc=sth to ' be ' described value=45

EDIT 2

As suggested I tried putting anchors to the regex and no result is recognized this time:

regex="^\s*(\w*)\s*=\s*(.*)"
regex="^\s*(\w*)\s*=\s*(.*)$"
regex="^\s*(\w+)\s*=\s*(.*)$"

None of these regex work, I have no result at all.

I checked the hexadecimal values of the string:

$ od -vAn -tx1c <<<"$inp"
           73  68  6f  72  74  20  3d  20  20  73  6f  6d  65  20  77  6f
           s   h   o   r   t       =           s   o   m   e       w   o
           72  64  20  20  6c  6f  6e  67  20  3d  20  73  70  61  6e  20
           r   d           l   o   n   g       =       s   p   a   n    
           64  65  73  63  3d  73  74  68  20  74  6f  20  27  20  62  65
           d   e   s   c   =   s   t   h       t   o       '       b   e
           20  27  20  64  65  73  63  72  69  62  65  64  20  76  61  6c
               '       d   e   s   c   r   i   b   e   d       v   a   l
           75  65  3d  34  35  0a                                        
           u   e   =   4   5  \n 

Doesn't seem to be weird characters.

For info, I am using bash v 4.4.0, on mac:

$ bash --version
GNU bash, version 4.4.0(1)-release (x86_64-apple-darwin15.6.0)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

EDIT 3

Some new. I tried it on a linux machine, using bash v. 4.1.2, inferior then:

$ bash --version
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

All those three regex work:

regex="\s*(\w*)\s*=\s*(.*)"
regex="^\s*(\w*)\s*=\s*(.*)"
regex="^\s*(\w*)\s*=\s*(.*)$"
regex="^\s*(\w+)\s*=\s*(.*)$"

I get the result:

input: "short =  some word  long = span desc=sth to ' be ' described value=45"
regex: "^\s*(\w*)\s*=\s*(.*)"
matching groups: 3
0: "short =  some word  long = span desc=sth to ' be ' described value=45"
1: "short"
2: "some word  long = span desc=sth to ' be ' described value=45"

This is exactly the result I expect. But why doesn't it work correctly on my mac? Bash version is more recent. I'd like a solution that work with all recent versions of bash.

  • Your regex works here as you intended it. Maybe it could be extended to be more stringent in what it match with this: regex="^\s*(\w+)\s*=\s*(.+)$". This will require that at least one match does happen (using a + instead of an *) and covering the whole string with ^ (start) and $ (end). – sorontar Jan 5 '17 at 11:56
  • Also, maybe your string contains some odd character (I believe it doesn't but we need something to explain your results). Use od -vAn -tx1c <<<"$inp" – sorontar Jan 5 '17 at 11:57
  • It doesn't work at all. After few more tests it seems the propblem is platform specific. I don't know how that's possible, I am using mac. Please check my edits – kaligne Jan 9 '17 at 10:56
2

Bash's regex are not anchored. That means that they may match anywhere in the string. It depends on your regex engine. Here, the match starts on the equal sign, as shown by BASH_REMATCH[0].

Solution: add a ^ at the beginning of the regex string.

[update] As said above, bash use your regex engine (man 3 regex) which may differ from one platform to another. If you have problem with your regex, avoid \letter shortcuts and use their Posix equivalent instead.

For example, instead of regex="^\s*(\w*)\s*=\s*(.*)"
use regex="^[[:space:]]*([_[:alnum:]]*)[[:space:]]*=[[:space:]]*(.*)"

  • Actually this does not work.. Putting the anchors in the regex does not return any result. I am using bash v. 4.4.0. – kaligne Jan 9 '17 at 10:44
  • it seems the propblem is platform specific. I don't know how that's possible, I am using mac. Please check my edits – kaligne Jan 9 '17 at 10:56
  • @user3298319 See my update – xhienne Jan 9 '17 at 11:35
  • Perfect it works many thanks! This way, the '^' anchor is not needed. But still I'll leave it in case. – kaligne Jan 9 '17 at 11:46
  • Glad it works. It's better to keep the anchor to make it clear what you want. – xhienne Jan 9 '17 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.