3

I'm using the =~ in one of my Bash scripts. However, I need to make the script compatible with Bash versions that do not support that operator, in particular the version of Bash that ships with msysgit which is not build against libregex. I have a work-around that uses expr match instead, but that again does not work on Mac OS X for some reason.

Thus, I want to use the expr match only if [ -n "$MSYSTEM" -a ${BASH_VERSINFO[0]} -lt 4 ] and use =~ otherwise. The problem is that Bash always seems to parse the whole script, and even if msysgit Bash would execute the work-around at runtime, it still stumbles upon the =~ when parsing the script.

Is it possible to conditionally execute code in the same script depending on the Bash version, or should I look into another way to address this issue?

Community
  • 1
  • 1
sschuberth
  • 28,386
  • 6
  • 101
  • 146
  • 1
    May be to separate "=~" related code to another file. And then import it on your condition. Like if [ -n "$MSYSTEM" -a ${BASH_VERSINFO[0]} -lt 4 ]; then source "mycode.sh". In this case bash will parse code only during importing another script. – Murad Tagirov Aug 12 '15 at 08:25
  • I was thinking about that, too, and while it probably would work, I'd prefer a solution that does not require to create additional files. – sschuberth Aug 12 '15 at 08:31
  • Or may be define your code in variable and just call it? Like var='Your bash code'. And then just use $var. – Murad Tagirov Aug 12 '15 at 08:35
  • Can you replace the regular expression with a pattern match instead? For example, use `[[ $foo = b*(a) ]]` instead of `[[ $foo =~ ba* ]]`. – chepner Aug 12 '15 at 12:10
  • Not sure ... my regex is `^\[[0-9]+\][[:space:]]`. Is there an equivalent pattern match? – sschuberth Aug 12 '15 at 12:23

3 Answers3

2

In your case, you can replace the regular expression with an equivalent pattern match.

[[ $foo = \[+([0-9])\][[:space:]]* ]]

Some explanations:

  • Patterns are matched against the entire string. The following regexes and patterns are equivalent:

    1. ^foo$ and foo
    2. ^foo and foo*
    3. foo$ and *foo
    4. foo and *foo*
  • +(...) matches one or more occurrences of the enclosed pattern, which in this case is [0-9]. That is, if $pattern and $regex match the same string, then so do +($pattern) and ($regex)+.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Though this requires extended glob patterns to be enabled (`shopt -s extglob`). – tripleee Aug 12 '15 at 14:58
  • 1
    Not inside `[[ ... ]]`, which always recognizes extended patterns. `extglob` allows you to use extended patterns for pathname generation as well (`ls @([0-9]).txt`, for example). – chepner Aug 12 '15 at 14:59
  • (I was going to provide a citation for that, either in the man page or in the release notes, but now I can't seem to find it. I think it's true going back to at least 3.2.) – chepner Aug 12 '15 at 15:13
  • Hmm, I get `-bash: syntax error in conditional expression: unexpected token \`('` on `GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)` – tripleee Aug 12 '15 at 15:15
  • Yeah. Now I'm not sure what I'm remembering. I can confirm it's unnecessary in 4.1 at least. – chepner Aug 12 '15 at 15:17
  • Also, as I just found this does not work on Mac OS X 10.10.4, I'm getting `syntax error in conditional expression: unexpected token `('`. – sschuberth Aug 13 '15 at 07:19
  • You just need to use `shopt -s extglob` as @tripleee pointed out. – chepner Aug 19 '15 at 20:03
1

My current solution is to use grep -q on all platforms instead. This avoids any conditionals or complicated code constructs.

Probably using eval to parse the code containing =~ only at runtime would have worked, too, but then again that would have made the code more complicated to read.

sschuberth
  • 28,386
  • 6
  • 101
  • 146
0

For this particular pattern, an equivalent but portable case statement can be articulated. It needs to have a fairly substantial number of different glob patterns to enumerate all the corner cases, though.

case $foo in
  [![]*  | \[[!0-9]* | *[!][0-9[:space:]]* | *[!0-9[:space:]] | \
  *\]*[![:space:]] | *[!0-9]\]* | \[*\[* | *\]*\]* )
    return 1;;  # false
  \[*[0-9]*\]* )
    return 0;;  # true
  *)
    return 1;;  # false
esac
tripleee
  • 175,061
  • 34
  • 275
  • 318