Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pattern-based branching

The case command performs pattern matching on a value and executes commands for the first matching pattern. This is useful for branching logic based on specific values or patterns.

Case command basics

A case command begins with the case reserved word, followed by the value to match. After the in reserved word, each branch specifies a pattern in parentheses, followed by a block of commands. Each block ends with ;;, and the command ends with esac.

For example, this command matches the value of foo and runs the corresponding commands:

$ case foo in
> (foo)
>     echo "Matched foo"
>     ;;
> (bar)
>     echo "Matched bar"
>     ;;
> esac
Matched foo

Patterns

Patterns can use wildcards and bracket expressions for flexible matching. For example, to match any string starting with f:

$ case foo in
> (f*)
>     echo "Starts with f"
>     ;;
> (b*)
>     echo "Starts with b"
>     ;;
> esac
Starts with f

To match multiple patterns, separate them with a pipe |:

$ case foo in
> (foo|bar)
>     echo "Matched foo or bar"
>     ;;
> esac
Matched foo or bar

Word expansion

Both the value and patterns undergo word expansion:

$ value="Hello" pattern="[Hh]*"
$ case $value in
> ($pattern)
>     echo "Matched pattern"
>     ;;
> esac
Matched pattern

The value is always expanded first. Patterns are expanded only when the shell needs to match them. Once a pattern matches, remaining patterns are not expanded.

Quote special characters in values or patterns to avoid unwanted expansion or matching:

$ case ? in
> ('?')
>     echo "Matched a single question mark"
>     ;;
> (?)
>     echo "Matched any single character"
>     ;;
> esac
Matched a single question mark

Continuing to the next branch

Instead of ;;, use ;& to continue execution with the next branch, regardless of whether its pattern matches. This allows multiple branches to run in sequence:

$ case foo in
> (foo)
>     echo "Matched foo"
>     ;&
> (bar)
>     echo "Matched bar, or continued from foo"
>     ;;
> (baz)
>     echo "Matched baz"
>     ;;
> esac
Matched foo
Matched bar, or continued from foo

Use ;;& or ;| to continue pattern matching in subsequent branches, so commands in multiple matching branches can run:

$ case foo in
> (foo)
>     echo "Matched foo"
>     ;;&
> (bar)
>     echo "Matched bar"
>     ;;
> (f*)
>     echo "Matched any string starting with f"
>     ;;
> esac
Matched foo
Matched any string starting with f

The ;;& and ;| terminators are extensions to POSIX. yash-rs supports both, but other shells may support only one or neither.

Miscellaneous

If no branch matches, or there are no branches, the shell skips the case command without error.

Use * as a catch-all pattern:

$ case foo in
> (bar)
>     echo "Matched bar"
>     ;;
> (*)
>     echo "Matched anything else"
>     ;;
> esac
Matched anything else

Use '' or "" as an empty value or pattern:

$ case "" in
> ('')
>     echo "Matched empty string"
>     ;;
> esac
Matched empty string

The opening parenthesis ( can be omitted if the first pattern is not literally esac, but parentheses are recommended for clarity:

$ case foo in
> foo)
>     echo "Matched foo"
>     ;;
> esac
Matched foo

Branches can have empty command blocks:

$ case bar in
> (foo)
>     ;;
> (bar)
>     echo "Matched bar"
>     ;;
> esac
Matched bar

The ;; terminator can be omitted for the last branch:

$ case foo in
> (foo)
>     echo "Matched foo"
>     ;;
> (bar)
>     echo "Matched bar"
> esac
Matched foo

Exit status

The exit status of case is that of the last command executed in the last executed branch. If the last executed branch has no commands, or no pattern matches, the exit status is 0.

Formal syntax

The formal syntax of the case command, in Extended Backus-Naur Form (EBNF):

case_command := "case", word, { newline }, "in", { newline },
                { branch }, [ last_branch ], "esac";
newline      := "\n";
branch       := pattern_list, branch_body, terminator, { newline };
last_branch  := pattern_list, branch_body;
pattern_list := "(", word, { "|" , word }, ")"
              | (word - "esac"), { "|" , word }, ")";
branch_body  := { newline }, [ list, [ newline, branch_body ] ];
terminator   := ";;" | ";&" | ";;&" | ";|";