Chapter 21: Shell Scripting: Control Flow (if, else, case)

Chapter Objectives

By the end of this chapter, you will be able to:

  • Understand the concept of exit codes and how they form the basis of conditional logic in shell scripts.
  • Implement conditional branching using if, elif, and else statements to control script execution.
  • Perform string, numeric, and file-based comparisons using the test, [, and [[ constructs.
  • Design and implement multi-way branching logic efficiently using the case statement for pattern matching.
  • Write robust shell scripts for the Raspberry Pi 5 that can make decisions based on system state, user input, or hardware status.
  • Debug and troubleshoot common errors related to syntax and logic in conditional shell scripts.

Introduction

In our journey through embedded Linux, we have learned how to execute commands and string them together to perform simple, linear tasks. However, the true power of an embedded system lies in its ability to react to a changing environment. A weather station must respond differently to a sudden drop in temperature, a security camera must take action only when motion is detected, and a factory robot must halt if a sensor reports an anomaly. This ability to make decisions is the hallmark of intelligent systems, and in the world of shell scripting, it is made possible through conditional logic.

This chapter introduces the fundamental building blocks of decision-making in Bash: the if statement and the case statement. These constructs allow us to move beyond simple, sequential scripts and create programs that can analyze conditions and alter their execution path accordingly. We will explore how a script can check the status of hardware, validate user input, monitor system resources, and take specific actions based on what it finds. For an embedded developer working with the Raspberry Pi 5, mastering conditional logic is not merely an academic exercise; it is the essential skill that enables the creation of automated, responsive, and reliable applications that bridge the gap between software commands and the physical world. By the end of this chapter, you will be equipped to write scripts that do not just execute commands, but make intelligent choices.

Technical Background

At the heart of every decision a computer makes is a simple question: “Is a particular condition true or false?” In many programming languages, this is handled with Boolean true and false values. The shell, however, operates on a different but related principle: the exit code of a command. Every command or program you run in Linux finishes with an integer status code that it reports back to the shell. By convention, an exit code of 0 signifies success, while any non-zero value (from 1 to 255) indicates some form of failure or error. This simple mechanism is the foundation of all conditional logic in shell scripting.

The if statement doesn’t evaluate a Boolean expression directly; instead, it executes a command and examines its exit code. If the exit code is 0 (success), the condition is considered “true,” and the code block following the if is executed. If the exit code is non-zero (failure), the condition is “false,” and the block is skipped.

Consider the grep command, which searches for patterns in text. If grep finds the pattern, it exits with 0. If it doesn’t, it exits with 1. We can use this behavior directly in an if statement:

Bash
if grep -q "root" /etc/passwd
then
  echo "The root user exists in the password file."
fi

Here, grep -q "root" /etc/passwd is the command being tested. The -q (quiet) option suppresses grep‘s normal output; we only care about its exit code. If root is found, the command succeeds (exit code 0), and the echo statement runs. If not, it fails (exit code 1), and the then...fi block is skipped.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%%
graph TD
    subgraph "Shell Conditional Logic"
        direction TB
        A["Start: Execute a Command<br>e.g., <i>grep root /etc/passwd</i>"]
        D{{"Check Command's<br>Exit Code"}}
        A --> D

        D -- "Exit Code is 0 (Success)" --> T[Condition is TRUE<br>Execute 'then' block]
        D -- "Exit Code is Non-Zero (Failure)" --> F[Condition is FALSE<br>Skip 'then' block]

        T --> E[End]
        F --> E[End]
    end

    %% Styling
    style A fill:#1e3a8a,stroke:#1e3a8a,stroke-width:2px,color:#ffffff
    style D fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style T fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style F fill:#ef4444,stroke:#ef4444,stroke-width:1px,color:#ffffff
    style E fill:#10b981,stroke:#10b981,stroke-width:2px,color:#ffffff

The test Command and []

While using any command’s exit code is powerful, we often need to perform more explicit comparisons, such as checking if two numbers are equal or if a file exists. This is the role of the test command. The test command evaluates an expression and exits with a status of 0 if the expression is true and 1 if it is false.

For example, to check if a variable holds a specific value, you could write:

test “$USER” = “pi”

This command will exit with 0 if the $USER variable is equal to “pi”. While perfectly valid, this syntax is not as common as its more popular alias: the single bracket [. The command [ expression ] is a synonym for test expression.

Warning: The spaces around the brackets are mandatory. The [ is not just syntax; it is a command (a link to test, in fact), and like any command, it requires spaces to separate it from its arguments. Forgetting a space, as in [$VAR=5], is a very common syntax error.

Using this improved syntax, we can write more readable conditions.

Types of Comparisons

The test command and its bracketed forms support three main categories of comparisons, each with its own set of operators. It is crucial to use the correct operators for the type of data you are evaluating.

1. Numeric Comparisons

When comparing integers, you must use a specific set of operators. These operators do not work for string comparisons.

Operator Description Example
-eq Equal to [ $val -eq 100 ]
-ne Not equal to [ $val -ne 100 ]
-gt Greater than [ $val -gt 100 ]
-ge Greater than or equal to [ $val -ge 100 ]
-lt Less than [ $val -lt 100 ]
-le Less than or equal to [ $val -le 100 ]

Attempting to use > or < for numeric comparison within single brackets will result in an error, as the shell will interpret them as redirection operators.

2. String Comparisons

For comparing textual data, a different set of operators is used.

Operator Description Example
= or == Equal to [ “$STR” == “hello” ]
!= Not equal to [ “$STR” != “hello” ]
-z String is null (has zero length) [ -z “$STR” ]
-n String is not null (has non-zero length) [ -n “$STR” ]
< Less than (in lexicographical order) [[ “$STR” < “b” ]]
> Greater than (in lexicographical order) [[ “$STR” > “a” ]]

Tip: Always enclose your variables in double quotes (e.g., "$STR") within test conditions. If a variable is empty or contains spaces, omitting the quotes can lead to the test command receiving an unexpected number of arguments, causing a syntax error. For example, if $VAR is empty, [ $VAR = "a" ] becomes [ = "a" ], which is invalid. [ "$VAR" = "a" ] becomes [ "" = "a" ], which is valid and evaluates correctly.

3. File Attribute Checks

A common task in embedded systems is to check the status of files, such as device nodes in /dev or configuration files in /etc. The test command provides a rich set of operators for this purpose.

Operator Description Example
-e File exists (of any type) [ -e /dev/ttyS0 ]
-f File exists and is a regular file [ -f /boot/config.txt ]
-d File exists and is a directory [ -d /etc/ ]
-r File exists and is readable [ -r /etc/shadow ]
-w File exists and is writable [ -w /tmp/data ]
-x File exists and is executable [ -x /bin/bash ]
-s File exists and has a size greater than 0 [ -s /var/log/syslog ]

These file operators are indispensable for writing scripts that safely interact with the filesystem, ensuring a file exists before trying to read it or that a directory is present before writing to it.

Branching with if-elif-else

An if statement on its own is useful, but its power grows when you add branching. The else and elif (short for “else if”) clauses allow you to build a decision tree, executing different blocks of code for different conditions.

The full structure looks like this:

Bash
if [ condition1 ]
then
  # Code to run if condition1 is true (exit code 0)
elif [ condition2 ]
then
  # Code to run if condition1 is false and condition2 is true
else
  # Code to run if all preceding conditions are false
fi

This structure allows a script to navigate a series of questions. Imagine a script that checks the Raspberry Pi’s CPU temperature. It first asks, “Is the temperature in the critical range?” If so, it takes immediate action. If not, it proceeds to the elif to ask, “Is the temperature in the warning range?” If so, it logs a warning. If that’s also not true, the else block acts as a default, reporting that the temperature is normal. This cascading logic is fundamental to creating responsive and robust systems.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%%
graph TD
    subgraph "if-elif-else Decision Flow"
        A[Start] --> C1{{"if (condition1 is true)?"}}
        C1 -- "Yes" --> P1["Execute Block 1"] --> E[End]
        C1 -- "No" --> C2{{"elif (condition2 is true)?"}}
        C2 -- "Yes" --> P2["Execute Block 2"] --> E
        C2 -- "No" --> P3["<b>else</b><br>Execute Block 3"] --> E
    end

    %% Styling
    style A fill:#1e3a8a,stroke:#1e3a8a,stroke-width:2px,color:#ffffff
    style C1 fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style C2 fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style P1 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style P2 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style P3 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style E fill:#10b981,stroke:#10b981,stroke-width:2px,color:#ffffff

The Modern Alternative: [[]]

Bash introduced an enhanced version of the [ command: the double-bracket [[ ... ]]. While [ is a standard command bound by shell parsing rules, [[ is a keyword with its own special parsing rules, which resolves several quirks and adds new features.

Key advantages of [[ ... ]] include:

  1. No Word Splitting or Globbing: Inside [[, variables don’t need to be quoted to prevent errors with spaces or empty values. The expression [[ $VAR = "some string" ]] is safe even if $VAR is empty or contains spaces.
  2. Regular Expression Matching: It introduces the =~ operator for matching against extended regular expressions, a powerful tool for string validation.
  3. C-Style Logical Operators: You can use && (AND) and || (OR) directly inside the brackets, rather than using the -a and -o operators of test, which can be brittle.

Example using &&:

Bash
# Old style with [
if [ -f "$FILE" -a -r "$FILE" ]; then ...

# New, safer style with [[
if [[ -f "$FILE" && -r "$FILE" ]]; then ...

For new scripts written specifically for Bash, using [[ ... ]] is generally recommended for its improved safety and extended features. However, if you are writing scripts that must be portable to other shells (like sh or dash), you should stick with the POSIX-standard [.

Feature [ … ] (POSIX Standard) [[ … ]] (Bash Keyword)
Variable Quoting Required. [ “$VAR” ] is mandatory to prevent errors with empty or space-filled variables. Not Required. [[ $VAR ]] is safe from word splitting and globbing. Quoting is still good practice.
Logical Operators Uses -a (AND) and -o (OR), which are brittle. Uses C-style && and || directly inside the brackets for more intuitive logic.
Pattern Matching No built-in pattern matching. Requires case or external tools. Supports wildcard globbing and the powerful =~ operator for regular expression matching.
Portability High. Works in any POSIX-compliant shell (sh, dash, ksh, bash). Low. Specific to Bash and some other modern shells (zsh, ksh). Will cause errors in /bin/sh.

Multi-Way Branching with the case Statement

When you have a long chain of if-elif-elif... statements all checking the same variable against different values, the code can become clumsy and hard to read. The case statement provides a much cleaner and more efficient solution for this scenario. It compares a single variable or value against a list of patterns and executes the code block associated with the first matching pattern.

The structure is as follows:

Bash
case "$VARIABLE" in
  pattern1)
    # Commands for pattern1
    ;;
  pattern2|pattern3)
    # Commands for when variable matches pattern2 OR pattern3
    ;;
  *.txt)
    # Commands for any value ending in .txt
    ;;
  *)
    # Default commands (matches anything)
    ;;
esac

The case statement is a perfect analogy for a switchboard. A single input value ($VARIABLE) is plugged in, and the case statement routes it to the correct destination based on a matching pattern. Each block of commands must be terminated with ;;, which prevents the execution from “falling through” to the next pattern. The *) pattern is a catch-all, similar to an else clause, that executes if no other patterns match.

One of the most powerful features of case is its use of shell wildcards in patterns. The asterisk (*) matches any sequence of characters, the question mark (?) matches any single character, and character sets ([abc]) can be used. This makes it exceptionally good at tasks like parsing command-line arguments, where you might want to handle options like --start, --stop, or --status.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%%
graph TD
    subgraph "case Statement Switchboard"
        A[Start: Input Variable<br>e.g., $COMMAND] --> S{{"case $VARIABLE in"}}

        S --> P1("pattern1)") --> B1["Execute<br>Commands 1"] --> T(";;")
        S --> P2("pattern2 | pattern3)") --> B2["Execute<br>Commands 2"] --> T2(";;")
        S --> P3("*.txt)") --> B3["Execute<br>Commands 3"] --> T3(";;")
        S --> P4("*)<br>(Default)") --> B4["Execute<br>Default Commands"] --> T4(";;")

        T --> E[esac<br>End]
        T2 --> E
        T3 --> E
        T4 --> E
    end

    %% Styling
    style A fill:#1e3a8a,stroke:#1e3a8a,stroke-width:2px,color:#ffffff
    style S fill:#8b5cf6,stroke:#8b5cf6,stroke-width:1px,color:#ffffff
    style P1 fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style P2 fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style P3 fill:#f59e0b,stroke:#f59e0b,stroke-width:1px,color:#ffffff
    style P4 fill:#eab308,stroke:#eab308,stroke-width:1px,color:#1f2937
    style B1 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style B2 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style B3 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style B4 fill:#0d9488,stroke:#0d9488,stroke-width:1px,color:#ffffff
    style E fill:#10b981,stroke:#10b981,stroke-width:2px,color:#ffffff

Practical Examples

Let’s apply these concepts to practical scenarios you might encounter while developing on a Raspberry Pi 5. These examples will read system information, control hardware, and manage services.

Example 1: System Health Monitor

A common task for an embedded device is to monitor its own health. This script uses an if-elif-else structure to check the CPU temperature and report its status. The Raspberry Pi 5 exposes its CPU temperature through a file in the /sys filesystem.

File Structure and Code

Create a file named health_check.sh.

Bash
#!/bin/bash

# health_check.sh
# A simple script to monitor the CPU temperature of a Raspberry Pi 5.

# The temperature is stored in this file in millidegrees Celsius.
# E.g., a value of 54321 means 54.321°C.
TEMP_FILE="/sys/class/thermal/thermal_zone0/temp"

# Define temperature thresholds in millidegrees Celsius.
# 80°C is a typical throttling threshold. 60°C is a warm/warning level.
CRITICAL_TEMP=80000
WARN_TEMP=60000

# First, check if the temperature file exists and is readable.
if [[ ! -r "$TEMP_FILE" ]]; then
  echo "Error: Cannot read temperature file at $TEMP_FILE."
  echo "Please ensure you are running on a Raspberry Pi and have permissions."
  exit 1
fi

# Read the raw value from the file.
# The `cat` command outputs the content of the file.
# We use command substitution `$(...)` to capture that output into a variable.
current_temp_raw=$(cat "$TEMP_FILE")

# Check if the value read is a valid number.
# We use a regular expression to ensure it contains only digits.
if ! [[ "$current_temp_raw" =~ ^[0-9]+$ ]]; then
    echo "Error: Invalid temperature value read: '$current_temp_raw'"
    exit 1
fi

# Now, use our conditional logic to evaluate the temperature.
echo "--- System Health Report ---"
echo "Current raw temperature value: $current_temp_raw"

if [[ "$current_temp_raw" -ge "$CRITICAL_TEMP" ]]; then
  # This block runs if the temperature is >= 80000.
  echo "Status: CRITICAL! CPU temperature is above 80°C."
  echo "Action: System may be throttling. Check cooling and system load."
elif [[ "$current_temp_raw" -ge "$WARN_TEMP" ]]; then
  # This block runs if the first condition was false, but temp is >= 60000.
  echo "Status: WARNING. CPU temperature is above 60°C."
  echo "System is running warm. Monitor for further increases."
else
  # This block runs if both of the above conditions were false.
  echo "Status: OK. CPU temperature is within normal range."
fi

echo "--------------------------"

Build and Execution Steps

  1. Save the code above into a file named health_check.sh.
  2. Make the script executable using the chmod command: chmod +x health_check.sh
  3. Run the script from your terminal: ./health_check.sh

Expected Output

The output will vary based on your Pi’s current temperature.

If the Pi is idle and cool:

Plaintext
--- System Health Report ---
Current raw temperature value: 45123
Status: OK. CPU temperature is within normal range.
--------------------------

If the Pi is under load:

Plaintext
--- System Health Report ---
Current raw temperature value: 62580
Status: WARNING. CPU temperature is above 60°C.
System is running warm. Monitor for further increases.
--------------------------

This example demonstrates file checks (-r), numeric comparisons (-ge), and a clear if-elif-else decision path, providing a practical template for any monitoring task.

Example 2: GPIO LED Controller with case

This example shows how to use a case statement to parse command-line arguments to control an LED connected to a GPIO pin. It provides a clean interface for turning the LED on, off, or checking its status.

Hardware Integration

You will need:

  • 1x LED (any color)
  • 1x 330Ω resistor
  • Jumper wires

Connect the components as follows:

  1. Connect the anode (longer leg) of the LED to GPIO 21 (Pin 40) on the Raspberry Pi 5.
  2. Connect the cathode (shorter leg) of the LED to one end of the 330Ω resistor.
  3. Connect the other end of the resistor to a Ground (GND) pin (e.g., Pin 39).

Warning: Always use a current-limiting resistor when connecting an LED to a GPIO pin to prevent damage to both the LED and the Raspberry Pi.

File Structure and Code

Create a file named led_control.sh. We will use the modern gpiod tools (gpioset, gpioget) which are the standard for GPIO control, replacing the older, deprecated /sys/class/gpio interface.

Bash
#!/bin/bash

# led_control.sh
# Controls an LED on GPIO 21 using a case statement for command parsing.

# Raspberry Pi 5 uses gpiochip4 for pins 0-27. GPIO 21 is on this chip.
GPIO_CHIP="gpiochip4"
GPIO_LINE=21 # Using GPIO pin 21

# The first argument to the script is our command ($1).
COMMAND=$1

# Check if a command was provided.
if [[ -z "$COMMAND" ]]; then
  echo "Usage: $0 {on|off|status}"
  echo "  on:     Turn the LED on."
  echo "  off:    Turn the LED off."
  echo "  status: Check the current state of the LED."
  exit 1
fi

# Use a case statement to handle the command.
case "$COMMAND" in
  on)
    echo "Turning LED on (GPIO $GPIO_LINE)..."
    # gpioset <chip> <line>=<value>
    # 1 means high/on.
    gpioset "$GPIO_CHIP" "$GPIO_LINE"=1
    echo "Done."
    ;; # End of this case

  off)
    echo "Turning LED off (GPIO $GPIO_LINE)..."
    # 0 means low/off.
    gpioset "$GPIO_CHIP" "$GPIO_LINE"=0
    echo "Done."
    ;; # End of this case

  status)
    echo "Checking LED status (GPIO $GPIO_LINE)..."
    # gpioget <chip> <line>
    # It returns 0 or 1.
    state=$(gpioget "$GPIO_CHIP" "$GPIO_LINE")
    if [[ "$state" -eq 1 ]]; then
      echo "LED is currently ON."
    else
      echo "LED is currently OFF."
    fi
    ;; # End of this case

  *) # Catch-all for any other argument
    echo "Error: Invalid command '$COMMAND'."
    echo "Usage: $0 {on|off|status}"
    exit 1
    ;;
esac

exit 0

Build and Execution Steps

1. Ensure the gpiod tools are installed. They are standard on Raspberry Pi OS.

Bash
sudo apt-get update && sudo apt-get install gpiod

2. Save the code as led_control.sh and make it executable:

Bash
chmod +x led_control.sh

3. Run the script with different commands:

Bash
# Turn the LED on
./led_control.sh on

# Check its status
./led_control.sh status

# Turn the LED off
./led_control.sh off

# Test an invalid command
./led_control.sh blink

Expected Output

Plaintext
$ ./led_control.sh on
Turning LED on (GPIO 21)...
Done.

$ ./led_control.sh status
Checking LED status (GPIO 21)...
LED is currently ON.

$ ./led_control.sh off
Turning LED off (GPIO 21)...
Done.

$ ./led_control.sh blink
Error: Invalid command 'blink'.
Usage: ./led_control.sh {on|off|status}

This example elegantly demonstrates how a case statement can create a clean command-line interface, a common pattern in embedded scripts for testing and diagnostics.

Common Mistakes & Troubleshooting

When writing conditional logic in shell scripts, small syntax mistakes can lead to frustrating errors. Here are some of the most common pitfalls and how to avoid them.

Mistake / Issue Symptom(s) Troubleshooting / Solution
Missing Spaces in [ … ] Error message like [: missing `]’ or command not found. The [ is a command and requires spaces around it and its arguments.
Mistake: if [$VAR=5]
Solution: if [ “$VAR” = 5 ]
Unquoted Variables Error message like [: too many arguments when a variable is empty or contains spaces. Always double-quote variables inside tests to treat them as a single entity.
Mistake: if [ $VAR = “a b” ]
Solution: if [ “$VAR” = “a b” ]
Incorrect Comparison Operators Unexpected logical results. For example, [ 10 > 5 ] evaluates to false because it’s a string comparison. Use -eq, -gt, -lt, … for numeric comparisons. Use ==, !=, >, < for string comparisons (note: > and < should be used inside [[ … ]]).
Forgetting ;; in case Code “falls through” and executes commands from the next case pattern unexpectedly. Each block of commands for a pattern in a case statement must end with a double semicolon (;;) to prevent fall-through.
Assignment vs. Comparison A script might behave strangely without syntax errors. Using a single = in some contexts can be ambiguous. While = works for string comparison, using == inside [[ … ]] is clearer and aligns with other languages, preventing confusion with variable assignment.

Exercises

  1. Basic User Check: Write a script named user_check.sh. It should check if the current user running the script is root. If it is, it should print a warning message: “Warning: Running as root is not recommended.” Otherwise, it should print “Running as a standard user. Good.” (Hint: The current user is stored in the $USER variable).
  2. File or Directory Identifier: Create a script type_check.sh that takes one argument: a path to a file or directory. The script should use if-elif-else to check if the path exists. If it does, it should then determine if it’s a regular file or a directory and print an appropriate message (“X is a regular file.” or “X is a directory.”). If the path does not exist, it should print an error message. (Hint: Use -e, -f, and -d).
  3. Raspberry Pi Model Identifier: Write a script pi_model.sh that uses grep and a case statement to identify the Raspberry Pi model. Read the “Model” line from /proc/cpuinfo. The script should print a specific message for “Raspberry Pi 5 Model B” and have a default message like “Unknown or older Raspberry Pi model detected” for any other value. (Hint: Use grep -i "Model" /proc/cpuinfo and command substitution).
  4. Advanced LED Controller: Extend the led_control.sh example. Add a new command blink to the case statement. This command should turn the LED on, wait for 0.5 seconds (sleep 0.5), turn it off, wait another 0.5 seconds, and repeat this cycle five times. (Hint: You will need a for loop inside the blink case).
  5. Simple Service Manager: Write an interactive script service_menu.sh. It should check if the ssh service is running using systemctl is-active --quiet ssh. Based on the exit code, it should present a menu using a case statement. If the service is active, the menu should offer to stop or restart it. If it’s inactive, the menu should offer to start it. The script should then execute the chosen command using sudo systemctl .... Include an option to exit the script.

Summary

  • Conditional Logic is Based on Exit Codes: In shell scripting, a command that exits with a status code of 0 is considered “true” (success), while any non-zero exit code is “false” (failure).
  • The if Statement Tests Exit Codes: The if statement executes a command and, based on its exit code, conditionally runs a block of code.
  • test, [, and [[ are for Comparisons: These commands are used to perform numeric, string, and file-based comparisons, forming the core of most conditional expressions.
  • Quoting Variables is Crucial: Always enclose variables in double quotes ("$VAR") inside conditional expressions to prevent errors from empty values or spaces.
  • if-elif-else Creates Decision Trees: This structure allows for complex, cascading logic where multiple conditions are tested in sequence.
  • case Provides Clean Multi-Way Branching: The case statement is a more readable and efficient alternative to long if-elif chains, especially for parsing user input or matching patterns.
  • [[...]] is the Modern Standard: For Bash scripts, the [[...]] construct offers safer parsing and more features (like regex matching) than the traditional [...].

Mastering these conditional constructs elevates your scripts from simple command sequences to dynamic programs capable of responding intelligently to the diverse conditions of an embedded environment.

Further Reading

  1. Bash Reference Manual (GNU): The official documentation for the Bash shell. The section on Conditional Constructs is the definitive source.
  2. Advanced Bash-Scripting Guide: An in-depth, classic resource covering a vast range of scripting topics, including detailed explanations of tests and comparisons.
  3. Raspberry Pi Documentation – GPIO: Official documentation on using GPIO on the Raspberry Pi, including information on modern gpiod utilities.
  4. Bash case statement (GeeksforGeeks): A well-structured tutorial focusing specifically on the syntax and usage of the case statement.
  5. Greg’s Wiki – BashFAQ: A highly respected wiki that clarifies many common points of confusion in Bash scripting, including the differences between [ and [[.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top