12

I would like to do something like this where on Friday, the output is for both conditions that match:

#!/bin/bash

#!/bin/bash
NOW=$(date +"%a")
case $NOW in
    Mon)
        echo "Mon";;
    Tue|Wed|Thu|Fri)
        echo "Tue|Wed|Thu|Fri";;
    Fri|Sat|Sun)
        echo "Fri|Sat|Sun";;
    *) ;;
esac

As the code above is written, the only output on Friday would be:

Tue|Wed|Thu|Fri

Desired output on Friday:

Tue|Wed|Thu|Fri
Fri|Sat|Sun

I understand that normally, only the commands corresponding to the first pattern that matches the expression are executed.

Is there a way to execute commands for additional matched patterns?

EDIT: I am not looking for fall-through behavior, but that's also a nice thing to know about. Thanks steeldriver.

  • 1
    Does this help? Can bash case statements cascade? – steeldriver Apr 3 at 0:46
  • Why not just use a series of ifs that's basically all a case statement is, just syntactic sugar to make a bunch of if or ifelse look nicer. – user1794469 Apr 3 at 0:47
  • @steeldriver op doesn't seem to want fall through but rather multiple match – user1794469 Apr 3 at 0:48
  • @user1794469 isn't that exactly what ;;& does? – steeldriver Apr 3 at 0:50
  • @steeldriver it totally is. I wasn't aware that's how that worked in bash. Fall through generally works by simply going to the next statement, not but testing the nest case, in other languages anyway. – user1794469 Apr 3 at 1:31
18

You can use the ;;& conjunction. From man bash:

               Using ;;& in place of ;; causes the shell to test
the next pattern list in the statement, if any, and execute  any
associated  list on a successful match.

Ex. given

$ cat myscript 
#!/bin/bash
NOW=$(date -d "$1" +"%a")
case $NOW in
    Mon)
        echo "Mon";;
    Tue|Wed|Thu|Fri)
        echo "Tue|Wed|Thu|Fri";;&
    Fri|Sat|Sun)
        echo "Fri|Sat|Sun";;
    *) ;;
esac

then

$ ./myscript thursday
Tue|Wed|Thu|Fri

$ ./myscript friday
Tue|Wed|Thu|Fri
Fri|Sat|Sun

$ ./myscript saturday
Fri|Sat|Sun

For more information (including equivalents in other shells) see

| improve this answer | |
  • 6
    Today I learned! – filbranden Apr 3 at 1:20
1

No, the basic structure for a case statement is that only one matching segment gets executed. Except for fall-through which you are rejecting. Also, it only works on some shells, not all, and has a syntax specific for each shell. The Bash syntax for fall-through is ;;&. In ksh or zsh, the only one available ;& doesn't perform the next test, only directly execute the segment (will print both lines for all cases in Tue|Wed|Thu|Fri).

The only option for case is to split it with this two lines:

esac
case $now in

As this (Please!, keep UPPERCASE variables for environment variables):

#!/bin/bash
now=$(date +"%a")
case $now in
    Mon)
        echo "Mon";;
    Tue|Wed|Thu|Fri)
        echo "Tue|Wed|Thu|Fri";;
esac
case $now in
    Fri|Sat|Sun)
        echo "Fri|Sat|Sun";;
    *) ;;
esac
| improve this answer | |
  • That's how I was doing it and the code is not clean or easy to maintain. I prefer the selected answer. i get that it may not be the overall best practice, but in this situation the selected answer results in code that is easier to read and maintain. For this question (i.e., my situation), it's the right answer. – MountainX Apr 5 at 19:46
  • No worries, @MountainX. If the answer helps, perfect, if it is not the best fit for your use case then also fine. Maybe at another time it could be used. – Isaac Apr 6 at 3:30
  • @MountainX I would suggest this answer over the accepted one, because the logic of the code is easier to see and the code is more standard/portable. The more special-case "tricks" you have in your code, the harder it will be for anyone to maintain. Shell is tricky enough as it is without using once-in-a-lifetime syntax. In 27 years of writing shell scripts, I have never had a need for this. – jrw32982 supports Monica Apr 8 at 22:38
  • @jrw32982supportsMonica - good point in general. However, I already stated why I prefer the selected answer. – MountainX Apr 10 at 2:54
0

Another choice: use functions.

$ cat tt
#!/bin/bash

mon()     { echo "  Mon"             ; }
tue_fri() { echo "  Tue|Wed|Thu|Fri" ; }
fri_sun() { echo "  Fri|Sat|Sun"     ; }

now=$(date +"%a")
[[ $1 != "" ]] && now=$1      # use for testing

case $now in
  Mon)         mon              ;;
  Tue|Wed|Thu) tue_fri          ;;
  Fri)         tue_fri; fri_sun ;;
  Sat|Sun)     fri_sun          ;;
  *)           echo "  ERROR"   ;;
esac

and test with

$ for f in Mon Tue Wed Thu Fri Sat Sun bla ;do echo "$f"; tt "$f"; done
Mon
  Mon
Tue
  Tue|Wed|Thu|Fri
Wed
  Tue|Wed|Thu|Fri
Thu
  Tue|Wed|Thu|Fri
Fri
  Tue|Wed|Thu|Fri
  Fri|Sat|Sun
Sat
  Fri|Sat|Sun
Sun
  Fri|Sat|Sun
bla
  ERROR
| improve this answer | |

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.