Shell – Does POSIX sh allow empty compound-list in first condition and empty condition in case conditional construct

posixshell

Reading POSIX specification for case conditional construct, I see:

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

As I understand, there will be at least one condition in body of case, and the compound-list corresponded with that condition must be existed.

I wrote a quick test:

$ cat test.sh
case $1 in
esac

case $1 in
  .) ;;
  *) echo 1
esac

then:

$ for shell in /bin/*sh; do
    printf '=%-18s=\n' "$shell"
    "$shell" ./test.sh .
  done
=/bin/ash          =
=/bin/bash         =
=/bin/dash         =
=/bin/heirloom-sh  =
=/bin/ksh          =
=/bin/lksh         =
=/bin/mksh         =
=/bin/pdksh        =
=/bin/posh         =
=/bin/schily-osh   =
=/bin/schily-sh    =
=/bin/sh           =
=/bin/yash         =
=/bin/zsh          =

(/bin/heirloom-sh is the Bourne shell from heirloom tool chest, /bin/schily-sh and /bin/schily-osh are the Schily Bourne shell)

It surprised me! All my known shells accepted the syntax. And also:

case $1 in esac

work in all shells above but ksh (That's ksh93u+ in my system), although it worked in ksh88 as confirmed by schily.

So is it allowed by POSIX or I missed something?

Best Answer

Here are the grammar rules, if it helps. I'm not great at reading it, but it sure looks allowed - for every pattern + list case there's also a pattern + break case. That third one would seem to indicate you might even go totally patternless. The link is here.


case_clause      : Case WORD linebreak in linebreak case_list    Esac
                 | Case WORD linebreak in linebreak case_list_ns Esac
                 | Case WORD linebreak in linebreak              Esac
                 ;
case_list_ns     : case_list case_item_ns
                 |           case_item_ns
                 ;
case_list        : case_list case_item
                 |           case_item
                 ;
case_item_ns     :     pattern ')'               linebreak
                 |     pattern ')' compound_list linebreak
                 | '(' pattern ')'               linebreak
                 | '(' pattern ')' compound_list linebreak
                 ;
case_item        :     pattern ')' linebreak     DSEMI linebreak
                 |     pattern ')' compound_list DSEMI linebreak
                 | '(' pattern ')' linebreak     DSEMI linebreak
                 | '(' pattern ')' compound_list DSEMI linebreak

Anyway - it makes sense to me that it should work. The following works:

x=
if $x; then $x; else echo this doesnt happen; fi

...because the commands are not empty when parsed and the shell has something to do. I always associate a pattern with an attached list directly. And in fact, they are pretty well conjoined.

x=0
for z in a b c d e f g
do    case $z in [abcd]) ;; $((x+=1))) ;; esac
done; echo "$x"

3

The spec is pretty clear about about the order of expansions and pattern - list associations. I always just naturally conjoin the two and consider them as more or less a single command. And so because the shell does something, it can check that box off. It probably has something to do with the way a C switch case works.

Related Question