39

I'm trying to do something like this:

case $level in
    3)
        echo "Level Three"

    2)
        echo "Level Two"

    1)
        echo "Level one"
        ;;
esac

where if $level = 3, it would output

Level Three
Level Two
Level One

while if $level = 1, it would output only

Level One

But when I try it, I get the error syntax error near unexpected token ')' because I didn't include the ;;.

Every other language I know of allows this, is there a way to do it in bash? Some sort of keyword that means "now go on and do the next case as if it matched"?

Damien
  • 748
  • 8
  • 14
Benubird
  • 5,752
  • 10
  • 36
  • 41
  • 2
    If every language you know allows this, you need to learn languages other than C and its imitators. Fallthrough in case statements are a historical design accident in C that somehow survived in more principled languages. – Gilles 'SO- stop being evil' May 10 '13 at 23:27
  • 1
    @GillesSO-stopbeingevil Not true. Fallthrough is a useful feature, and its harmfulness is just a widespread myth. Many programmers complain about _implicit_ fallthrough, saying it's a tragedy that a non-fallthroughing `case` requires an explicit `break`; ridiculous. Moreover, the very few reasons that I did see, because "it's easy to forget the `break`" is plain wrong, and because "I don't like it" is irrational. – Sapphire_Brick Nov 20 '20 at 06:39

1 Answers1

56

You need to use ;& instead of ;; to get a fall-through behavior:

#! /bin/bash
foo() {
    case "$1" in
        3)
            echo "Level Three"
            ;&
        2)
            echo "Level Two"
            ;&
        1)
            echo "Level One"
            ;;
        a)
            echo "Level a"
            ;&
        b)
            echo "Level b"
            ;&
        c)
            echo "Level c"
            ;;
    esac
}
echo 3:
foo 3
echo 2:
foo 2
echo a:
foo a
3:
Level Three
Level Two
Level one
2:
Level Two
Level one
a:
Level a
Level b
Level c

See the Conditional Constructs section of the bash documentation.

The other special marker is ;;&, which:

causes the shell to test the patterns in the next clause, if any, and execute any associated command-list on a successful match.

;; is always final, no further patterns are tested.

#! /bin/bash

foo() {
    case "$1" in
        *3*)
            echo "Level Three"
            ;;&
        *2*)
            echo "Level Two"
            ;;&
        *1*)
            echo "Level One"
            ;;&
    esac
}

echo 12:
foo 12
echo 13:
foo 13
echo 23:
foo 23
12:
Level Two
Level One
13:
Level Three
Level One
23:
Level Three
Level Two
Mat
  • 51,578
  • 10
  • 158
  • 140