Section 1 of 10

1 Before you start

Before you begin, take a few minutes to check what you already know and how confident you feel. You will see the same questions again at the end of the module — this helps both you and us measure what you have learned. Click an option for every question and confidence rating, then click Next to continue.

Pre-test

What ends each branch of a case statement?

Pre-test

What does the special variable $? hold?

Pre-test

By convention, what exit code means success?

Pre-test

What does cmd1 && cmd2 do?

Pre-test

Which line is the catchall (default) branch of a case statement?

Pre-test

What does cmd1 || cmd2 do?

Pre-test

Which keyword ends a case statement?

Pre-test

In a case statement, which pattern matches any value ending in .csv?

Pre-confidence

I can write a case statement instead of a long if/elif chain.

Not at all confident
Fully confident
Pre-confidence

I can read the exit code of the previous command and react to it.

Not at all confident
Fully confident
Pre-confidence

I can use && and || to chain commands based on success or failure.

Not at all confident
Fully confident
Section 2 of 10

2 Introduction

As your scripts grow, you will often need them to make decisions and react to different situations. To do this elegantly, you need tools in your toolbelt. In this lesson, we will uncover the following:

  • case ... esac for clean multi-way branching.
  • Every command has an exit code (0 = success, anything else = failure).
  • Reading the exit code with $?.
  • Shortcut operators && (run only on success) and || (run only on failure).
Section 3 of 10

3 case - When You Branch on One Value

When you need your script to check a single variable against a long list of possible values, writing a massive chain of if and elif statements gets messy fast. The case statement is designed specifically for this scenario. It is much easier to read, and it even supports wildcard patterns!

Take a look at how clean this looks when sorting files by their extension:

case "$extension" in
    csv|tsv)
        echo "looks like tabular data"
        ;;
    json)
        echo "structured data"
        ;;
    pdf|png|jpg)
        echo "report or figure"
        ;;
    *)
        echo "unknown extension"
        ;;
esac

This looks strange at first glance, but here’s the exact breakdown

case "$variable" in ... esac : This is the outer wrapper. Fun fact: esac is literally just "case" spelled backwards! It tells bash that the block is finished.

PATTERN) : Each branch starts with the pattern you are looking for, followed by a closing parenthesis ).

The | Operator: You can check for multiple patterns at once by separating them with a pipe | (which means "OR").

;; (Double Semicolon): This is the most common beginner trap! You must end the commands in each branch with a double semicolon to tell bash to stop reading and exit the case block.

*) (The Catchall): The asterisk is a wildcard that matches literally anything. You always put this at the very bottom to act as your else statement—it catches anything that didn't match the options above.

A case statement compares one value against patterns from top to bottom, runs the first branch that matches, and falls through to *) when nothing else fits.
A case statement compares one value against patterns from top to bottom, runs the first branch that matches, and falls through to *) when nothing else fits.
  • Try it yourself!
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
dx=hypertension
case "$dx" in
    hypertension) echo "code I10" ;;
    diabetes) echo "code E11" ;;
    *) echo "code unknown" ;;
esac

case statements don't have to be massive blocks of text. You can format them on a single line for quick, clean translations.

Section 4 of 10

4 Exit Codes - How Commands Report Success and Failure

By default, bash scripts are like runaway trains: if a command fails, the script doesn't stop. It just plows right into the next command.

If your "load data" step fails, your "calculate BMI" step will run on an empty file, and you might accidentally ship a blank report to your colleagues without ever realizing it. To stop the runaway train, we need to understand exit codes.

Every time a command finishes running, it leaves behind a hidden, invisible integer called an exit code. Think of it as a status report:

  • 0 = Perfect Success. The command did exactly what it was supposed to do.
  • 1 to 255 = Failure. Something went wrong. The specific number depends on the command, but any number higher than zero means trouble.
  • You can see the exit code of the most recent command with $?.
$ ls /etc > /dev/null
$ echo $?
0
$ ls /not_a_real_path > /dev/null 2>&1
$ echo $?
2
Every command reports whether it worked through an exit code, 0 for success and any other number for failure, which you read with $?.
Every command reports whether it worked through an exit code, 0 for success and any other number for failure, which you read with $?.

The $? variable is incredibly volatile. It updates after every single command. If you need to remember an exit code to use it later, you must save it to a new variable immediately!

Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ grep -c F cohort_2026.csv > /dev/null
$ echo $?
$ grep -c ZZZ cohort_2026.csv > /dev/null
$ echo $?
grep -c hypertension diagnoses.csv > /dev/null
rc=$?
if [[ $rc -ne 0 ]]
then
    echo "grep failed with code $rc" >&2
fi
Section 4.1 of 10

4.1 Setting your own exit code with exit

Just like standard commands report back to you, your script needs to report back to the system when it finishes running. You can set your script's exit code using the exit command.

If your script finishes its work perfectly, end it with exit 0. If your script hits a fatal error, you should print an error message and end it with exit 1 to halt the runaway train.

if [[ ! -f cohort_2026.csv ]]
then
    echo "cohort_2026.csv is missing" >&2
    exit 1
fi
A finished script reports its result to the shell as an exit code, where 0 signals success and any non-zero value signals failure.
A finished script reports its result to the shell as an exit code, where 0 signals success and any non-zero value signals failure.
Section 5 of 10

5 && and || - Short-Circuit Operators

Once you understand exit codes, the && and || operators finally make sense. They look at the invisible exit code of the command on their LEFT, and use it to decide whether or not to run the command on their RIGHT.

Section 5.1 of 10

5.1 && (AND THEN) - run only on success

The command on the right only runs if the left side exits with a 0.

$ mkdir output && cd output
# only cds into output if mkdir succeeded
Section 5.2 of 10

5.2 || (OR ELSE) - run only on FAILURE

The command on the right only runs if the left side exits with a 1-255. It acts as a safety net or fallback.

$ mkdir output 2>/dev/null || echo "could not make output"
# prints message only if mkdir failed
Try it out

Try this snippet in the Bash Scratchpad on the right.

Try this snippet
$ ls cohort_2026.csv && echo "cohort is here"
$ ls cohort_2025.csv || echo "no 2025 cohort"
&& runs the next command only after the previous one succeeds, while || runs the next command only after it fails, both decided by the exit status the previous command leaves behind.
&& runs the next command only after the previous one succeeds, while || runs the next command only after it fails, both decided by the exit status the previous command leaves behind.
Section 6 of 10

6 Check your understanding

You have reached the end of the module. Try the same questions again — your answers here, paired with your pre-test answers, are how we measure what the module taught you. Answer every question and confidence rating, then click Submit and see results to view your score.

Post-test

What ends each branch of a case statement?

Post-test

What does the special variable $? hold?

Post-test

By convention, what exit code means success?

Post-test

What does cmd1 && cmd2 do?

Post-test

Which line is the catchall (default) branch of a case statement?

Post-test

What does cmd1 || cmd2 do?

Post-test

Which keyword ends a case statement?

Post-test

In a case statement, which pattern matches any value ending in .csv?

Post-confidence

I can write a case statement instead of a long if/elif chain.

Not at all confident
Fully confident
Post-confidence

I can read the exit code of the previous command and react to it.

Not at all confident
Fully confident
Post-confidence

I can use && and || to chain commands based on success or failure.

Not at all confident
Fully confident
Section 7 of 10

7 Your results

Here is how your post-test answers compare with your pre-test answers. The pre/post pairing is the most reliable way to see what this module actually taught you.

Your score

Submit the post-test to see your results.

Muddiest point

What is the one thing from this module that is still unclear to you?

Rate this module

Overall, how would you rate this module?

How likely are you to recommend this module to a peer? (0 = not at all, 10 = extremely likely)