40

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"?

2
  • 3
    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. Commented May 10, 2013 at 23:27
  • 2
    @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. Commented Nov 20, 2020 at 6:39

1 Answer 1

58

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
6
  • 26
    Portability note: that syntax is not POSIX. It comes from ksh and is available in ksh, bash (since 4.0, 2009) and zsh (since 3.1.2, 1997). ;;& is bash-specific. Commented May 10, 2013 at 13:34
  • 4
    Also note that the zsh equivalent of bash's ;;& is ;| (added in 4.3.3, 2007). Commented May 10, 2013 at 14:31
  • Not to confuse with &|, which is completely unrelated to this. ;) (and that was just a blink emoticon... for now) Commented Apr 20, 2021 at 14:29
  • 1
    Yep, it'd be nice to have POSIX syntax (for dash). Commented Jun 29, 2021 at 12:23
  • 1
    The POSIX way to do it (especially to target BSD shells) would be to use one case (or if) statement per condition. Commented Jan 20, 2023 at 17:16

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.