Case statement fallthrough?

Viewed 58784

In popular imperative languages, switch statements generally "fall through" to the next level once a case statement has been matched.

Example:

int a = 2;
switch(a)
{
   case 1:
      print "quick ";
   case 2: 
      print "brown ";
   case 3: 
      print "fox ";
      break;
   case 4:
      print "jumped ";
}

would print "brown fox".

However the same code in bash

A=2
case $A in
2)
  echo "QUICK"
  ;&
2)
  echo "BROWN"
  ;&
3)
  echo "FOX"
  ;&
4)
  echo "JUMPED"
  ;&
esac

only prints "BROWN"

How do I make the case statement in bash "fall through" to the remaining conditions like the first example?

(edit: Bash version 3.2.25, the ;& statement (from wiki) results in a syntax error)

running:

test.sh:

#!/bin/bash
A=2
case $A in
1)
  echo "QUICK"
  ;&
2)
  echo "BROWN"
  ;&
3)
  echo "FOX"
  ;&
esac

Gives:

./test.sh: line 6: syntax error near unexpected token ;' ./test.sh:
line 6:
;&'

5 Answers

Using ;& is not very portable, as it requires bash (not ash, dash, or any other minimal sh) and it requires at least bash 4.0 or newer (not available on all systems, e.g. macOS 10.14.6 still only offers bash 3.2.57).

A work around that I consider much nicer to read than a lot of if's is loop and modify the case var:

#!/bin/sh

A=2
A_BAK=$A
while [ -n "$A" ]; do
    case $A in
        1)
            echo "QUICK"
            A=2
            ;;

        2)
            echo "BROWN"
            A=3
            ;;

        3)
            echo "FOX"
            A=4
            ;;

        4)
            echo "JUMPED"
            A=""
            ;;
    esac
done
A=$A_BAK

Here's a proof of concept: https://www.onlinegdb.com/0ngLPXXn8

Use a sequence of case statements with pattern alternation:

#!/bin/sh

a="2"
case "$a" in
    "1")
        echo "QUICK"
        ;;
esac
case "$a" in
    "1" | "2")
        echo "BROWN"
        ;;
esac
case "$a" in
    "1" | "2" | "3")
        echo "FOX"
        ;;
esac
case "$a" in
    "4")
        echo "JUMPED"
        ;;
esac

Benefits:

  • Follows the format of the original question
  • Works with any POSIX-compliant sh, including old versions of Bash
  • Unordered string values of a such as "apple", "pear", "banana", "tree" will work
  • Control flow is straightforward without backward branches

Disadvantages:

  • More characters are needed since there are four case statements instead of just one

Note that lowercase a is better than uppercase A for non-environment variables and that $a as well as the case patterns should be quoted for the general case where a might have spaces in it. Quoting can be removed for simple cases.

bash switch with fallthrough, implemented with function and flag

#! /bin/sh

switch_fallthrough() {
  [ $# = 0 ] && { echo no value >&2; return; }
  local f= # fall through
  [ "$1" = 1 ] && { echo quick; f=1; }
  [ $f ] || [ "$1" = 2 ] && { echo brown; f=1; }
  [ $f ] || [ "$1" = 3 ] && { echo fox; return; }
  [ $f ] || [ "$1" = 4 ] && echo jumped
  return 1 # error = no case did match
}

switch_fallthrough "2"
# brown
# fox
Related