I'm trying to find a nice way of checking some text in order to set a variable. For instance to perform the logic: 'if the version string is 1.x then set a flag'. One way of doing this is:

if grep -q "^1.*" versionfile; then

which is a little verbose. If I want to switch on multiple possibilities then that's easier:

case `grep "^1.*" versionfile` in
    echo "version 1"

But is there a nice one line way of just setting a flag to be true or false if some text matches a regexp or a wildcard?

  • 1
    You should be aware that in your second example, your grepping a regex then your case is using globbing. So grep might find "1a" or "10" successfully and then the case would (correctly) pass it to the default (presumably). You should probably put a literal dot in the grep like this grep "^1\..*" ... – Dennis Williamson Feb 15 '10 at 14:21
versionFlag=`grep "^1.*" /tmp/test | wc -l`

versionFlag is then set to 0. Any data in test matches, versionFlag is set to the number of lines that match, so

if [ $versionFlag == 0 ] ; then
  echo false
  echo true 

edit: this can be done even more simply

versionFlag=$(grep "^1.*" /tmp/test -c)

which will return a count of instances in the file

  • That's the way I used to do it before I discovered case and grep -q. It doesn't work quite as well on OS X since the output of wc contains leading spaces, so I had to pipe through sed, yada, yada... – the_mandrill Feb 15 '10 at 13:18
  • updated. perhaps the grep -c method will work in OSX :) – Andy Feb 15 '10 at 13:23
  • Lovely, the grep -c does the job. I didn't know you could do $(...) as well as the backtick operator too. – the_mandrill Feb 15 '10 at 13:36
  • 2
    The construct $() is much preferred over backticks. mywiki.wooledge.org/BashFAQ/082 – Dennis Williamson Feb 15 '10 at 14:15

You can do something like this in bash 3.x (older versions don't support regex matching)

version=`cat versionfile`
[[ $version =~ "^1.*" ]] && versionFlag=1
  • Thanks, I didn't know you could do direct regexp matches. Shame though that it doesn't provide a way of setting versionFlag to 0 if it doesn't match – the_mandrill Feb 15 '10 at 13:19
  • That won't work unless you remove the quotes. But then it will match version numbers like 1234abc.@(/: - Try: [[ $version =~ ^1\..* ]] (1 dot anything) or [[ $version =~ ^1\.[[:digit:]]{1,3}$ ]] (e.g. 1.1 or 1.789 but not 1.2345 or 1.1a). – Dennis Williamson Feb 15 '10 at 14:11

There isn't a ternary operator (e.g. ?:) in bash, so you are stuck with your if, then construct over multiple lines. You could use a different language that does have a ternary operator (PERL for example).

Mind you, a lot of folk discourage use of ternary operators as they aren't as easy to read in code - in other words, your comments end up longer than the multiple lines of code would have been.

I doubt there is a performance benefit of either method.

  • Guess the other answers are roll your own ternary operators :-) – dunxd Feb 15 '10 at 13:17
  • (However, there is a ternary integer arithmetic operator in Bash, but that's not helpful for this use.) – Dennis Williamson Feb 15 '10 at 14:13

Actually, this construct:

[[ $version =~ ^1\.+ ]] && versionFlag=1

is easily extended to support the "no-match" case; just do this:

[[ $version =~ ^1\.+ ]] && versionFlag=1 || versionFlag=0

Incidentally, note the change from '*' to '+' in the regex. Using the star, the regex will still incorrectly match on a version value of "10". (The '*' modifier means "match zero or more occurrences", i.e., you still don't need to have a decimal point; the '+' modifier means "match one or more occurrences".) Making this change will, however, mean that "1" (i.e., a value without a decimal) will not match; this might or might not be a problem for your application.

There is also a way to do this without using the regex-match operator, if you don't have it; try this:

[[ ${version#1.} != ${version} ]] && versionFlag=1 || versionFlag=0

This is just a pattern-match, not a regex, so if you really need regex functionality, it won't work, but for simple stuff, it's great. (If you're not familiar with this form of parameter expansion, the "${version#1.}" construct expands to the value of "version", but with the post-'#' pattern removed from the beginning of the value if it was originally there. Example: If "version" has a value of "1.03", the result of "${version#1.}" is "03". Since this differs from the original value, the "!=" test succeeds.)

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.