How to use patterns in a case statement?
Asked Answered
C

4

108

The man page says that case statements use "filename expansion pattern matching".
I usually want to have short names for some parameters, so I go:

case $1 in
    req|reqs|requirements) TASK="Functional Requirements";;
    met|meet|meetings) TASK="Meetings with the client";;
esac

logTimeSpentIn "$TASK"

I tried patterns like req* or me{e,}t which I understand would expand correctly to match those values in the context of filename expansion, but it doesn't work.

Clova answered 29/12, 2010 at 13:57 Comment(0)
V
213

Brace expansion doesn't work, but *, ? and [] do. If you set shopt -s extglob then you can also use extended pattern matching:

  • ?() - zero or one occurrences of pattern
  • *() - zero or more occurrences of pattern
  • +() - one or more occurrences of pattern
  • @() - one occurrence of pattern
  • !() - anything except the pattern

Here's an example:

shopt -s extglob
for arg in apple be cd meet o mississippi
do
    # call functions based on arguments
    case "$arg" in
        a*             ) foo;;    # matches anything starting with "a"
        b?             ) bar;;    # matches any two-character string starting with "b"
        c[de]          ) baz;;    # matches "cd" or "ce"
        me?(e)t        ) qux;;    # matches "met" or "meet"
        @(a|e|i|o|u)   ) fuzz;;   # matches one vowel
        m+(iss)?(ippi) ) fizz;;   # matches "miss" or "mississippi" or others
        *              ) bazinga;; # catchall, matches anything not matched above
    esac
done
Volcanic answered 29/12, 2010 at 16:48 Comment(4)
This answer helped me because I had enclosed my pattern in double quotes ("a*") and that wasn't workingOleneolenka
Some versions of bash won't support @(...) even with shopt -s extglob enabledQuintuplicate
@YzmirRamirez: I'd be curious to know which versions. extglob has been in Bash since bash-2.02-alpha1 (about 1998).Volcanic
@phyatt that link helped me solve a more complicated case problem.Residential
S
49

I don't think you can use braces.

According to the Bash manual about case in Conditional Constructs.

Each pattern undergoes tilde expansion, parameter expansion, command substitution, and arithmetic expansion.

Nothing about Brace Expansion unfortunately.

So you'd have to do something like this:

case $1 in
    req*)
        ...
        ;;
    met*|meet*)
        ...
        ;;
    *)
        # You should have a default one too.
esac
Solferino answered 29/12, 2010 at 14:25 Comment(0)
G
10

if and grep -Eq

arg='abc'
if printf '%s' "$arg" | grep -Eq 'a.c|d.*'; then
  echo 'first'
elif printf '%s' "$arg" | grep -Eq 'a{2,3}'; then
  echo 'second'
fi

where:

  • -q prevents grep from producing output, it just produces the exit status
  • -E enables extended regular expressions

I like this because:

One downside is that this is likely slower than case since it calls an external grep program, but I tend to consider performance last when using Bash.

case is POSIX 7

Bash appears to follow POSIX by default without shopt as mentioned by https://mcmap.net/q/202556/-how-to-use-patterns-in-a-case-statement

Here is the quote: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_01 section "Case Conditional Construct":

The conditional construct case shall execute the compound-list corresponding to the first one of several patterns (see Pattern Matching Notation) [...] Multiple patterns with the same compound-list shall be delimited by the '|' symbol. [...]

The format for the case construct is as follows:

case word in
   [(] pattern1 ) compound-list ;;
   [[(] pattern[ | pattern] ... ) compound-list ;;] ...
   [[(] pattern[ | pattern] ... ) compound-list]
esac

and then http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 section "2.13. Pattern Matching Notation" only mentions ?, * and [].

Gooseherd answered 1/7, 2017 at 8:59 Comment(0)
V
0

I came across this Q&A a few times now, here is my slightly different point of view now.

I usually want to have short names for some parameters

Of course I prefer shorter code too. However...

case $1 in
    "req"|"reqs"|"requirements"|"many more"|"strings"| \
    "and"|"patterns"|"here")
        TASK="Functional Requirements";;
    "met"|"meet"|"meetings")
        TASK="Meetings with the client";;
esac

... works, and I don't get any complaints from shellcheck. You could even put every pattern on a separate line and sort them alphabetically if that pleases you.

Varini answered 30/1 at 21:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.