Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

This is my first time bash scripting so I'm probably making an easy mistake.

Basically, I'm trying to write a script that gets the groups of a user, and if they are in a certain group, it will log that accordingly. Evidently there will be more functionality, but there's no point building that when I can't even get the regex working!

So far, I have this:

#!/bin/bash

regex="^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"

# example output
groups="username : username usergroup"

echo "$groups" >> /home/jrdn/log

if [[ "$groups" =~ $regex ]]; then
    echo "Match!" >> /home/jrdn/log
else
    echo "No match" >> /home/jrdn/log
fi

Every place I've tried that regex, it works. But in the bash script, it only ever outputs the $groups, followed by No match. So can someone tell me what's wrong with it?

share|improve this question
1  
What makes you think anything is wrong with it? –  manatwork Oct 3 '13 at 14:17
1  
@jrdnhannah then try to slowly re-create your target regexp, first match ^([a-zA-Z0-9\-_]+) then add the colon and so on... you should find out pretty soon, where is the problem. –  peterph Oct 3 '13 at 14:35
2  
Same here with bash 4.2.45. Escaping the underscore fixed it. Weird. @jrdnhannah could you write that up as an answer and accept it please? –  terdon Oct 3 '13 at 14:54
1  
Since I've only just signed up to the Unix SE, it requires me to wait 8 hours before answering my own. Happy to mark it as answered if somebody else does, though. –  hannahsbadger Oct 3 '13 at 15:07
4  
@terdon bash just calls libc's regex functions, probably. So it depends on the libc version, not the bash version. See my answer... (Or maybe even on the collation sequence you have in use) –  derobert Oct 3 '13 at 15:30

2 Answers 2

up vote 12 down vote accepted

From man 7 regex:

A bracket expression is a list of characters enclosed in "[]". …

… To include a literal '-', make it the first or last character…. [A]ll other special characters, including '\', lose their special significance within a bracket expression.

Trying the regexp with egrep gives an error:

$ echo "username : username usergroup" | egrep "^([a-zA-Z0-9\-_]+ : [a-zA-Z0-9\-_]+) (usergroup)$"
egrep: Invalid range end

Here is a simpler version, that also gives an error:

$ echo 'hi' | egrep '[\-_]'
egrep: Invalid range end

Since \ is not special, that is a range, just like [a-z] would be. You need to put your - at the end, like [_-] or:

echo "username : username usergroup" | egrep "^([a-zA-Z0-9_-]+ : [a-zA-Z0-9_-]+) (usergroup)$"
username : username usergroup

This should work regardless of your libc version (in either egrep or bash).

edit: This actually depends on your locale settings too. The manpage does warn about this:

Ranges are very collating-sequence-dependent, and portable programs should avoid relying on them.

For example:

$ echo '\_' | LC_ALL=en_US.UTF8 egrep '[\-_]'
egrep: Invalid range end
$ echo '\_' | LC_ALL=C egrep '[\-_]'
\_

Of course, even though it didn't error, it isn't doing what you want:

$ echo '\^_' | LC_ALL=C egrep '^[\-_]+$'
\^_

It's a range, which in ASCII, includes \, [, ^, and _.

share|improve this answer
    
Interesting. My egrep gives no error, just matches it correctly. –  manatwork Oct 3 '13 at 15:35
    
@manatwork your collation sequence probably allows the range.... –  derobert Oct 3 '13 at 15:36
    
I not know much about collation. You mean this: LC_COLLATE="en_US.UTF-8"? –  manatwork Oct 3 '13 at 15:40
    
@manatwork I've edited the question to give an example. Note it may be different on your system, because sometimes those collation (sorting) sequences change. –  derobert Oct 3 '13 at 15:41
1  
@manatwork Its OK, I almost filed a bug report before I noticed the attempt to escape -... –  derobert Oct 3 '13 at 15:44

General rule with regexps (and any bugs in larger pieces of code): cut it down and rebuild it step by step or use bisecting - whatever works better for you.

In this case the culprit turned out to be the underscore - escaping it with a backslash has made it work.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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