Case statement in Bash Shell Scripting

Last updated: May 16, 2023 | Timofey Dankevich

1. Introduction

As with any programming language, in Bash, there is a concept of a case statement too that allows one to use it with multiple conditions. The case statement checks an input value until it finds the associated pattern to the input and executes the command linked to that input value. This tutorial provides an introduction to the case statement in Bash with examples.

2. Case statement syntax

The basic syntax of a case statement:

case EXPRESSION in
  PATTERN1)
    COMMANDS1
    ;;
  PATTERN2)
    COMMANDS2
    ;;
  PATTERN3)
    COMMANDS2
    ;;  
  *)
    DEFAULT_COMMANDS
    ;;
esac

It is worth memorizing several important things:

  • The case statement starts with the “case” keyword followed by the EXPRESSION and the “in” keyword. The statement ends with the case keyword backwards - “esac”.
  • It is possible for the case statement to specify plural patterns separated by the “|” operator.
  • A pattern and its associated commands are called clause.
  • The “)” operator is followed by COMMANDS.
  • Each clause must be separated from each other using the “;;”.
  • It is a common practice to use the wildcard asterisk symbol (*) as a final pattern to define the default case. This pattern will always match.
  • If there are no patterns are correspond to input, the return status is zero. Otherwise, the return status is the exit status of the executed commands.

3. if-else statement vs a case statement

The if-else and switch are conditional statements. On the one hand, if-else statement executes the group of statements based on whether it is true or false, on the other hand, a case statement checks the value of a variable and compares it with numerous possible patterns. 

Here is a listing of the points why a case statement should be used:

  • The case statement is more concise and easier to read when dealing with many conditions, while the if-else statement can become cumbersome and challenging to maintain.
  • It matches a single expression against multiple patterns. Each condition is independent and can't be combined with others using logical operators.
  • The case statement supports shell pattern matching. It allows the checking of wildcard and character classes, which can be more versatile and powerful than simple equality checks.
  • A case statement is faster than an if-else statement because the case statement does not evaluate all conditions sequentially.
  • The case statement is more convenient to support them since its syntax is simpler than an if-else statement, especially when there are multiple conditions.

4. Case statement examples

These examples illustrate how the case statement can be utilized to streamline decision-making and enhance the functionality of Bash scripts

4.1. Using single case statement

#!/bin/bash

read -p "Enter a number (1-7): " day_number

case $day_number in
  1)
    echo "Monday"
    ;;
  2)
    echo "Tuesday"
    ;;
  3)
    echo "Wednesday"
    ;;
  4)
    echo "Thursday"
    ;;
  5)
    echo "Friday"
    ;;
  6)
    echo "Saturday"
    ;;
  7)
    echo "Sunday"
    ;;
  *)
    echo "Invalid input"
    ;;
esac

This script is a straightforward example of using a case statement to handle user input and provide appropriate responses. It ensures that only valid inputs are processed, while invalid inputs are acknowledged and flagged.

The user should enter a day number and then the script via case statement evaluates the day_number variable. If there is a matching pattern, the script prints a corresponding name of the day, otherwise wildcard (*) case will be executed and the script prints out “Invalid input”.

using a case statement to handle user input and provide appropriate responses

4.2. Using multiple case statement

In this example, you can see a way of using multiple conditions in the pattern and it will work correctly.

case $letter in
	[aeuioAEUIO])
		echo "It is vowel."
		;;
	[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])
		echo "It is consonant."
		;;
	*)
		echo "Invalid input!"
		;;
esac
example script using multiple case statement

In this case, a pattern works as a regular expression and just it searches for a coincidence between a pattern and an input of the user. That’s why the last input is incorrect since double BB does not match any patterns in the case statement, but it is possible to fix it the following way. 

#!/bin/bash

read -p "Enter any letter (a-zA-Z): " letter

case $letter in
        [aeuioAEUIO]*)
                echo "It is vowel."
                ;;
        [bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]*)
                echo "It is consonant."
                ;;
        *)
                echo "Invalid input!"
                ;;
esac
pattern works as a regular expression

Now, the same input is correct since the “*” symbol means one or more.

4.3. Using fallthrough case statement

Another way of using a case statement is fallthrough - which allows the execution of several commands by the order. It is one of the benefit of a case statement compared to an if-else statement.

The following script is able to print out a month-by-order starting with the number entered by the user.

#!/bin/bash
read -p "Enter a number of month (1-12): " month
case $month in
	1)
		echo "January"
		;&
	2)
		echo "Fabruary"
		;&
	3)
		echo "March"
		;&
	4)
		echo "April"
		;&
	5)
		echo "May"
		;&
	6)
		echo "June"
		;&
	7)
		echo "July"
		;&
	8)
		echo "August"
		;&
	9)
		echo "September"
		;&
	10)
		echo "October"
		;&
	11)
		echo "November"
		;&
	12)
		echo "December"
		;;
	*)
		echo "Invalid input!"
		;;
esac

This script initiates the user to input a number meaning a month, which is then evaluated by a case statement. Each case resembles a month, and the pattern matches, then the script prints the name of the month. The script uses a fallthrough operator, allowing the script to continue through all subsequent cases even after a match. If the user input does not match any cases, a wildcard case outputs a specific error message.

example script for fallthrough case statement

4.4. Using Case statements in for loops

A case statement is a very flexible construction so it is possible to use it in a different case, for instance, in a loop as shown below.

#!/bin/bash
read -p "Enter a name of directory: " dir
content=$(ls "$dir")
echo 
for item in $content;
do
	case $item in
		*.c)
			echo -e $item "is a C source code."
			;;
		*.cpp)
			echo -e $item "is a C++ source code."
			;;
		*.py)
			echo -e $item "is a python source code."
			;;
		*)
			echo -e $item "is something else."
	esac
done

The script waits for input of the user that should enter a directory with source codes of different programming languages. Then the script executes a shell command “ls” that allows to display of the content of the specified directory, it saves in the variable and the script then iterates over each item in the directory using a for loop. For each item, a case statement is used to determine the type of file based on its extension.

The script checks for three types of source code files: C, C++, and Python. If a file with one of these extensions is found, the script prints a message stating the type of source code.

example case statements in for loops

This script can execute more useful actions rather than just printing out a type of file. Let's improve the script - takes a directory name as input and organizes the files within that directory based on their file extensions.

#!/bin/bash

read -p "Enter a name of directory: " dir

content=$(ls "$dir")

echo 

for item in $content;
do
        case $item in
                *.c)
                        mkdir -p $dir/c_src
                        mv $dir/$item $dir/c_src/$item
                        ;;
                *.cpp)
                        mkdir -p $dir/cpp_src
                        mv $dir/$item $dir/cpp_src/$item
                        ;;
                *.py)
                        mkdir -p $dir/py_src
                        mv $dir/$item $dir/py_src/$item
                        ;;
                *)
                        echo -e $item "is something else."
        esac
done

The script allows a user to organize files in a given directory by their file extensions. The script receives the user input for a directory name and reads its content. Then, via a loop, the script traverses through each item of the directory and checks the item’s extension, if the extension is equal to one of the specified patterns, the script creates a new directory and moves the item into the new directory. For any file that does not match the predefined extensions, the script simply prints a message stating that the file is "something else". 

It is a helpful script for the developer since it can sort different source codes by their own directories automatically.

4.5. Regular expressions in case statements

Bash does not support full regular expressions, but there are special glob patterns. Within “[ … ]”, character classes can be specified using the syntax [:class:], where the class is one of the following classes defined in the POSIX standard:

  • alnum: Alphanumeric characters. This includes all digits (0-9) and letters (a-z, A-Z).
  • alpha: Alphabetic characters. This includes all lowercase and uppercase letters (a-z, A-Z).
  • ascii: ASCII characters. This includes all characters with ASCII values ranging from 0 to 127.
  • blank: Blank characters. This includes space and tab.
  • cntrl: Control characters. This includes non-printable characters that control how text is displayed, such as newline (\n), carriage return (\r), tab (\t), etc.
  • digit: Numeric characters. This includes all digits (0-9).
  • graph: Graphic characters. This includes all printable characters except for space.
  • lower: Lowercase alphabetic characters. This includes all lowercase letters (a-z).
  • print: Printable characters. This includes all characters that are graphical or blank.
  • punct: Punctuation characters. This includes characters like commas, periods, semicolons, etc., that are not included in alnum.
  • space: Space characters. This includes space, form feed (\f), newline (\n), carriage return (\r), horizontal tab (\t), and vertical tab (\v).
  • upper: Uppercase alphabetic characters. This includes all uppercase letters (A-Z).
  • word: Word characters. This includes all alphanumeric characters and the underscore (_).
  • xdigit: Hexadecimal digits. This includes all digits (0-9) and the letters a-f and A-F.
#!/bin/bash
read -p "Enter any string: " string
case $string in
	*[[:alpha:]]*)
		echo -e $string "contains letters."
		;;
	*[[:digit:]]*)
		echo -e $string "contains digits."
		;;
	*)
		echo -e "string is empty."
		;;
esac

The script allows us to determine whether the entered input includes letters or digits. It is worth understanding in that case if the string includes both letters and digits, then the first pattern will be executed since this regular expression searches for any coincidence. 

4.6. Nesting Case statements

Nested case statements in Bash allow for advanced decision-making processes in our scripts. They provide a way to organize multiple conditions and potential outputs in a clean, efficient way. As we can nest case statements within other case statements, this allows us to handle complex condition trees.

Let's delve deeper into this concept by using a real-world scenario: mapping months to their respective seasons.

#!/bin/bash

read -p "Enter a name of season [spring, summer, autumn, winter]: " season
read -p "Enter a number of month [1-3]: " month

case $season in
        "spring")
                case $month in
                        1)
                                echo "March"
                                ;;
                        2)
                                echo "April"
                                ;;
                        3)
                                echo "May"
                                ;;
                        *)
                                echo "Invalid month!"
                                ;;
                esac
                ;;
        "summer")
                case $month in
                        1)
                                echo "June"
                                ;;
                        2)
                                echo "July"
                                ;;
                        3)
                                echo "August"
                                ;;
                        *)
                                echo "Invalid month!"
                                ;;
                esac
                ;;
        "autumn")
                case $month in
                        1)
                                echo "September"
                                ;;
                        2)
                                echo "October"
                                ;;
                        3)
                                echo "November"
                                ;;
                        *)
                                echo "Invalid month!"
                                ;;
                esac
                ;;
        "winter")
                case $month in
                        1)
                                echo "December"
                                ;;
                        2)
                                echo "January"
                                ;;
                        3)
                                echo "February"
                                ;;
                        *)
                                echo "Invalid month!"
                                ;;
                esac
                ;;
        *)
                echo "Invalid season!"
                ;;
esac

The script starts by prompting the user to input a season and a month number. The script then enters a case statement that checks the value of the season variable.

Depending on the season input, it enters a sub-case statement that checks the month variable. The month should be a number between 1 and 3 representing the month of the given season. If the user enters an incorrect month number, the script falls into the wildcard (*) case of the sub-case statement, printing 'Invalid month!'. Similarly, if the user enters a season that there is not in the list, the script prints 'Invalid season!'.

example script showing nesting case statements

Nested case statements provide several benefits. They help in organizing code in a clear and logical manner, making it easier to understand and maintain. They also make it easy to add, remove, or modify conditions without affecting the rest of the script.

Moreover, they provide an efficient way to handle multiple conditions. Instead of writing multiple if-else statements, we can nest case statements to create a clean, hierarchical decision structure.

4.7. Case statements in a function

Bash functions provide a way to encapsulate functionality within a script, making it easier to organize and reuse code. When combined with case statements, we can create robust functions that are capable of handling complex conditions and providing varied outputs.

In the following script, there is a function named num_of_month which accepts a string representing a month and outputs the corresponding month number. This is an excellent example of using a case statement within a function.

#!/bin/bash

function num_of_month ()
{
        case $1 in
                "January" | "january")
                        echo $1 "is 1st month."
                        ;;
                "February" | "february")
                        echo $1 "is 2nd month."
                        ;;
                "March" | "march")
                        echo $1 "is 4th month."
                        ;;
                "April" | "april")
                        echo $1 "is 3rd month."
                        ;;
                "May" | "may")
                        echo $1 "is 5th month."
                        ;;
                "June" | "june")
                        echo $1 "is 6th month."
                        ;;
                "July" | "july")
                        echo $1 "is 7th month."
                        ;;
                "August" | "august")
                        echo $1 "is 8th month."
                        ;;
                "September" | "september")
                        echo $1 "is 9th month."
                        ;;
                "October" | "october")
                        echo $1 "is 10th month."
                        ;;
                "November" | "november")
                        echo $1 "is 11th month."
                        ;;
                "December" | "december")
                        echo $1 "is 12th month."
                        ;;
                *)
                        echo "Invalid syntax!"
                        ;;
        esac
}

read -p "Enter a name of month: " month

echo $(num_of_month $month)
example script for case statements in a function

This is a clear, efficient way to handle this kind of decision-making within a function. By encapsulating this logic within a function, you can easily call this code anywhere in your script without having to duplicate it.

Conclusion

In conclusion, the case statement in Bash provides a concise and efficient way to handle multiple conditions and execute corresponding commands. It offers more flexibility and readability compared to if-else statements, supports pattern matching using glob patterns, and can be nested to handle complex decision-making scenarios.

SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment