223

I am looking for the correct syntax of the switch statement with fallthrough cases in Bash (ideally case-insensitive). In PHP I would program it like:

switch($c) {
    case 1:
        do_this();
        break;
     case 2:
     case 3:
        do_what_you_are_supposed_to_do();
        break;
     default:
        do_nothing(); 
}

I want the same in Bash:

case "$C" in
    "1")
        do_this()
        ;;
    "2")
    "3")
        do_what_you_are_supposed_to_do()
        ;;
    *)
        do_nothing();
        ;; 
esac

This somehow doesn't work: function do_what_you_are_supposed_to_do() should be fired when $C is 2 OR 3.

codeforester
  • 39,467
  • 16
  • 112
  • 140
Mischka
  • 2,239
  • 2
  • 14
  • 3
  • 5
    Don't use call functions with parens!!! Since you can define a function in bash using either `function fname { echo "Inside fname"; return 0; }` or `fname() { echo "inside fname"; return 0; }` placing parens on a function call can look like it's a function defintion. Functions should be called like any other command line program such as `mv`, `cp`, `rsync`, `ls`, `cd`, etc... In this case we call fname like so: `fname $ARGS`. – Charles D Pantoga Dec 17 '15 at 17:36
  • 1
    `do_nothing()` shall be a SKIP statement? Use `:`. – sjas Jul 21 '16 at 12:01
  • Also see [How to use Shellcheck](https://github.com/koalaman/shellcheck), [How to debug a bash script?](https://unix.stackexchange.com/q/155551/56041) (U&L.SE), [How to debug a bash script?](https://stackoverflow.com/q/951336/608639) (SO), [How to debug bash script?](https://askubuntu.com/q/21136) (AskU), [Debugging Bash scripts](http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html), etc. – jww Aug 31 '18 at 12:15

5 Answers5

355

Use a vertical bar (|) for "or".

case "$C" in
"1")
    do_this()
    ;;
"2" | "3")
    do_what_you_are_supposed_to_do()
    ;;
*)
    do_nothing()
    ;;
esac

Bash Reference Manual: Conditional Constructs. case

Abdull
  • 26,371
  • 26
  • 130
  • 172
geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • 34
    Or in this simple case a character class `[23])` – SiegeX Apr 06 '11 at 06:50
  • 5
    @Mischka - I note you haven't accepted this answer, is that because it doesn't answer the fallthrough part of the question? Fallthrough logic is useful where special processing should occur before `do_what_you_are_supposed_to_do()`, collapsing both "2" and "3" into a single case fails to address this. I'm unsure if editing the question to make this clearer is reasonable, since it's obvious that many people have found this answer helpful. – Tyson Apr 23 '18 at 04:03
  • 2
    @Tyson The OP hasn't accepted the answer, since it is an unregistered user. As to the "fall-through" logic, as normally understood by programmers, the question body demonstrates a collapsing of condition, _not_ fall-through logic. (Notice the use of `break` in the php code.) Editing the question, or its title, at this date would invalidate many of the answers which _do_ provide fall-through logic, and probably shouldn't be done. Fall-through wasn't in the title until several edits by other users, but it's too late to take it back out now. – Chindraba Feb 08 '19 at 05:30
120

Recent bash versions allow fall-through by using ;& in stead of ;;: they also allow resuming the case checks by using ;;& there.

for n in 4 14 24 34
do
  echo -n "$n = "
  case "$n" in
   3? )
     echo -n thirty-
     ;;&   #resume (to find ?4 later )
   "24" )
     echo -n twenty-
     ;&   #fallthru
   "4" | [13]4)
     echo -n four 
     ;;&  # resume ( to find teen where needed )
   "14" )
     echo -n teen
  esac
  echo 
done

sample output

4 = four
14 = fourteen
24 = twenty-four
34 = thirty-four
Jasen
  • 11,837
  • 2
  • 30
  • 48
  • 2
    This logic in this example was difficult to follow. Also, I don't think this example really demonstrates the difference between `;&` and `;;&`. I changed the `"24"` to `;;& # resume` and got the same results, so I'm still wondering when you would use `;&` fallthrough. – wisbucky Feb 07 '20 at 00:53
  • 2
    it's a simple example for a complex thing - I changed `?4` to `[13]4` to make it more obvious, and to make your change a breaking change – Jasen Feb 07 '20 at 01:02
  • Unfortunately not supported on macos' bash :( – v01pe Oct 16 '20 at 15:35
  • @JerryGreen well what can I say, here's my output: ```test.sh: line 7: syntax error near unexpected token `&' test.sh: line 7: ` ;;& #resume (to find ?4 later )'``` Here's my `bash --version`: GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19) – v01pe Nov 08 '20 at 14:11
  • 1
    @v01pe OH SHIT SORRY. I'm switching like 10 times per day between macos and windows (with linux-like `MINGW64` shell), and actually I didn't even realize that I used this script on windows LOL. Just tried it on my macbook with the same exact (preinstalled) bash version: I confirm, it doesn't work :\ On windows though it shows `GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)`, so it seems it's a feature of newer Bash version :\ Thx for the notice btw! (I deleted my previous comment due to being misinfo) – Jerry Green Nov 09 '20 at 05:40
  • @JerryGreen No worries, I can imagine the CLI confusion– I remember always using the wrong shortcuts etc. when switching between win/mac a lot :) – v01pe Nov 09 '20 at 10:53
  • Guys, Bash 4 was released in **2009**. 9, not 19. Not exactly a "new" version. MacOS is sticking with 3.2 from **2006** for a legal/policy reason, to block GPLv3 license. Use homebrew to update your shell to something that is not older than iPhone 1... – MestreLion Apr 20 '21 at 14:34
  • @wisbucky: actually `;&` is the one that behaves like in C, PHP, etc: it falls-through the next statement without re-testing the condition. – MestreLion Apr 20 '21 at 14:38
30
  • Do not use () behind function names in bash unless you like to define them.
  • use [23] in case to match 2 or 3
  • static string cases should be enclosed by '' instead of ""

If enclosed in "", the interpreter (needlessly) tries to expand possible variables in the value before matching.

case "$C" in
'1')
    do_this
    ;;
[23])
    do_what_you_are_supposed_to_do
    ;;
*)
    do_nothing
    ;;
esac

For case insensitive matching, you can use character classes (like [23]):

case "$C" in

# will match C='Abra' and C='abra'
[Aa]'bra')
    do_mysterious_things
    ;;

# will match all letter cases at any char like `abra`, `ABRA` or `AbRa`
[Aa][Bb][Rr][Aa])
    do_wild_mysterious_things
    ;;

esac

But abra didn't hit anytime because it will be matched by the first case.

If needed, you can omit ;; in the first case to continue testing for matches in following cases too. (;; jumps to esac)

Zearin
  • 1,474
  • 2
  • 17
  • 36
Sprinterfreak
  • 504
  • 4
  • 10
17

Try this:

case $VAR in
normal)
    echo "This doesn't do fallthrough"
    ;;
special)
    echo -n "This does "
    ;&
fallthrough)
    echo "fall-through"
    ;;
esac
heemayl
  • 39,294
  • 7
  • 70
  • 76
René Steetskamp
  • 1,053
  • 9
  • 12
15

If the values are integer then you can use [2-3] or you can use [5,7,8] for non continuous values.

#!/bin/bash
while [ $# -gt 0 ];
do
    case $1 in
    1)
        echo "one"
        ;;
    [2-3])
        echo "two or three"
        ;;
    [4-6])
        echo "four to six"
        ;;
    [7,9])
        echo "seven or nine"
        ;;
    *)
        echo "others"
        ;;
    esac
    shift
done

If the values are string then you can use |.

#!/bin/bash
while [ $# -gt 0 ];
do
    case $1 in
    "one")
        echo "one"
        ;;
    "two" | "three")
        echo "two or three"
        ;;
    *)
        echo "others"
        ;;
    esac
    shift
done
rashok
  • 12,790
  • 16
  • 88
  • 100
  • what is the `shift` at the end for? – Milkncookiez Nov 10 '19 at 13:43
  • 2
    `shift` removes first argument on CLI arguments list. Basically on every iteration of this loop always `$1` is used to get each argument from CLI arguments list with the help of `shift`. – rashok Nov 10 '19 at 14:25