pattern matching question

Hi guys
I have the following case statement in my script:

case $pn.$db in
   *?.fcp?(db))  set f ${pn} cp   ;;
   *?.oxa?(oxa) )  set oxa $pn  ;;
esac

Can somebody help me to understand how to interpret *?.fcp?(db)) or *?.oxa?(oxa) ?

I cannot figure out how in this case pattern maching works.
Thanks a lot for any advice .

You seem to be using the Korn shell (or a shell that supports the ksh extended globbing - bash (with shopt -s extglob ) or zsh ( setopt kshglob ).

This is the meaning of these patterns:

*?.fcp?(db)

* - matches any string, including the null string
? - matches any character
. - matches a literal dot
fcp - matches the literal string fcp
?(db) - matches 0 or 1 occurrences of the subpattern, the string db in this case.

Consider the following:

$ cat s
case $pn.$db in
   *?.fcp?(db)  ) printf '%s in the first case\n' "$pn.$db matched" ;;
   *?.oxa?(oxa) ) printf '%s in the second case\n' "$pn.$db matched" ;;
esac
$ pn=any_ db=fcp
$ . s
any_.fcp matched in the first case
$ pn=any_ db=fcpdb
$ . s
any_.fcpdb matched in the first case
$ pn=any_ db=fcpdbdb
$ . s
$
$ pn=any_ db=oxa
$ . s
any_.oxa matched in the second case

I hope this helps.

First, lets take this as an example of why comments are necessary!

Ok in the case statement, the patterns to match follow the shell's file name generation patterns (*=any string, ?=any character, ?(patternlist)). So:

*?.fcp?(db))

(the *? means the first part must have at least 1 character)
means: if the pattern we are analyzing in the case ($pn.$db)
starts with at least 1 character and
ends in "fcp" or "fcpdb" then the pattern is matched.

If I am correct, this could be written in an easier to
follow way. Here's an example script using ksh that accepts 2 args which are then analyzed
by the case statement:

#!/bin/ksh

pn=$1
db=$2

# original code
case $pn.$db in
   *?.fcp?(db))  print 1
                 set f ${pn} cp   ;;
   *?.oxa?(oxa) )  print 2
                   set oxa $pn  ;;
esac

# Hopefully clearer code that shows the author's intentions.
case $pn.$db in
     *?.fcp) ;&  #  Fall through
   *?.fcpdb) print "first case"
            set f ${pn} cp
            ;;
     *?.oxa) ;&  #  Fall through
  *?.oxaoxa) print "Second case"
            set oxa $pn
            ;;
         *) # Always allow for the unexpected!
            print "Unknown value [$pn.$db]"
            exit 1
            ;;
esac

exit 0

Note the

     *.oxa) ;&  #  Fall through
  *.oxaoxa) print "Second case"

could be written as

*.oxa|*.oxaoxa)

Using a logical OR, but I am in the habit of splitting them up on their own lines in case in the future the developer maintaining this needs to take a different action on each case. The framework is already set up then.

Also, always allow for the default case which will catch unexpected values passed in!

Depends on what needs to be matched. The * matches the null string too,
so the two patterns * and *? are different (the latter requires at least one character):

$ cat s
case $pn.$db in
   *?.fcp?(db)  ) printf '%s in the first case\n' "$pn.$db matched" ;;
   *.fcp?(db)  ) printf '%s in the second case\n' "$pn.$db matched" ;;
esac
$ pn= db=fcp
$ . s
.fcp matched in the second case
$ pn=a db=fcp
$ . s
a.fcp matched in the first case
1 Like

Yes, it appears the case pattern means its pattern must start with at least 1 character. My example should have the ? too as in the original.
I will update my example. Thanks!

Thanks a lot guys. It really helped me a lot.