Reading POSIX specification for case conditional construct, I see:

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

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

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


$ for shell in /bin/*sh; do
    printf '=%-18s=\n' "$shell"
    "$shell" ./test.sh .
=/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?

  • When a standard is at variance with every implementation you can think of, it is the specification that is wrong (it happens) or you've misinterpreted it. Sure, that's a semantic argument of "wrong", but practically, adhering to the spec just won't work. – msw Dec 22 '15 at 5:53
  • Let me add a note: /opt/schily/bin/osh is preferred over /usr/5bin/sh as it also aims to present a portable version of the OpenSolaris Bourne Shell but includes less bugs that have been introduced from making it portable. – schily Dec 22 '15 at 10:22
  • @schily: Yes, I know. You have noticed me sometimes ago. The order in my test is meaningless. – cuonglm Dec 22 '15 at 10:24
  • BTW: fot those that did not read about the difference before: /opt/schily/bin/osh and /opt/schily/bin/sh are compiled from the same sources but osh only includes the portability changes and some bugfixes for hard problems while sh is including a lot of additional code that adds features and that make it behave very close to a POSIX shell. See: schilytools.sourceforge.net/bosh.html for a list of new features and features that are still missing for POSIX compliance. – schily Dec 22 '15 at 10:29
  • i always do ${sh##*/} so i dont have to wind up talking to anybody about my paths. – mikeserv Dec 22 '15 at 10:33

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:

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.

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


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.

  • 1
    Ah, the grammar make sense that the empty compound-list is allowed. But how about the empty case list? I don't see it in grammar. Your if example did not seem to be similar with the empty case. The if body had at least $x for the shell to parse. While the case body had nothing. – cuonglm Dec 22 '15 at 6:26
  • @cuonglm - what do you mean? its left out for all of them. and the if expands $x while parsing just as case expands the patterns - each in order and stopping at the first match in the same way. a case will never expand any patterns beyond a match because the test is passed and the expansion part of the parse is done at that point. – mikeserv Dec 22 '15 at 6:35
  • I mean the parsing occur before expansion. Like when you do if $x; then ; fi you got syntax error, case $x in esac work well. – cuonglm Dec 22 '15 at 6:37
  • @cuonglm - that's not how it works w/ case. not really - it needs the delimiters - but each pattern / list pair is parsed and expanded together. once a match is found all of the rest is ignored up to esac - as long as very special shell chars like ( and ) dont interfere with its recognizing esac. so if you had list 3 pages long and you matched the first pattern most of the time, you'd have a fast test anyway. – mikeserv Dec 22 '15 at 6:40
  • Right, I know it but I still confuse about the way shell parse. It's interesting that case $x in esac work in all my shell test above, except ksh93. – cuonglm Dec 22 '15 at 6:47

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.