Bash Decision Scripts Made Simple (Part 13 / 34)







Bash Decision Scripts Made Simple: 11 Real-World Examples | LinuxTeck


While writing a Bash script with a simple sequential list of commands has some utility, this type of scripting is very limiting. A script truly comes into its own when it can make decisions automatically based on conditions or command results. Bash conditional statements allow scripts to react differently depending on the situation, such as checking free disk space, verifying user accounts, testing file existence, or validating command execution. In this guide, we walk through 11 practical decision-making scripts based on real system administration tasks, all tested on Ubuntu 24.04 and Rocky Linux 9.

Why Shell Script Decision Making Matters in 2025

Without conditionals, your scripts would behave the same regardless of the system state, user input, or file existence. Here is where they actually help:

  • Check if a service is running before restarting it
  • Warn when disk space drops below a threshold automatically
  • Validate user input before processing it
  • Handle errors gracefully instead of crashing the whole script

No conditionals = a fragile script. Add conditionals = a script that thinks for itself.

If you are brand new to shell scripting, start with the basics of Bash scripting first, then come back here to level up your scripts with real decision logic.



#01

How the Bash if Statement Actually Works

The bash if statement checks a condition and runs a block of commands only when that condition is true. The structure looks simple but there are a few rules that trip up beginners every time.

Syntax Rule

Spaces inside brackets are not optional. [ $x -gt 5 ] is correct. [$x -gt 5] will break with a "command not found" error. Always put a space after [ and before ].

Here is the most basic form. The script checks whether a number is greater than 100:

bash
LinuxTeck.com
#!/bin/bash
# Basic if statement example
read -p "Enter a number: " NUM

if [ $NUM -gt 100 ]
then
echo "Number is greater than 100"
fi

echo "Script finished."

OUTPUT
Enter a number: 150
Number is greater than 100
Script finished.

Senior Tip: (( )) for Arithmetic Comparisons

For purely numerical comparisons, Bash supports the (( )) arithmetic expansion syntax which uses C-style operators and is easier to read for developers coming from other languages.

if (( NUM > 100 )); then echo "Greater"; fi

Inside (( )) you can use >, <, ==, !=, >=, <= directly without the -gt, -lt style flags. Variables also do not need the $ prefix inside (( )).

The fi at the end closes the block. Every if needs a matching fi or the script will not even run. Read more about the complete bash if statement guide for more syntax variations.



#02

if-else and if-elif-else: Handling Multiple Outcomes

Most real decisions are not binary. A service can be running, stopped, or not installed at all. The bash if elif else chain handles all three states cleanly. Here is a practical script that checks a system user's existence, something you might run during server provisioning on Ubuntu or Rocky Linux.

bash
LinuxTeck.com
#!/bin/bash
# Check if a system user exists
read -p "Enter username to check: " USERNAME

if id "$USERNAME" &>/dev/null
then
echo "User $USERNAME exists on this system."
elif [ -z "$USERNAME" ]
then
echo "No username entered. Please provide a username."
else
echo "User $USERNAME does not exist. Creating now..."
useradd "$USERNAME"
echo "User $USERNAME has been created."
fi

Tip: Distro Note

On Rocky Linux 9, useradd is available without any extra package install. On Ubuntu 24.04, the same command is pre-installed as well. On both distros, useradd modifies /etc/passwd and requires root or sudo privileges to run. Always execute user-management scripts with sudo or as the root user regardless of distro.

For more on handling user input inside scripts, see the guide on bash read command with user input examples.



#03

[ ] vs [[ ]] — The Modern Bash Best Practice Nobody Talks About

This is the biggest gap in most tutorials. Single brackets [ ] are POSIX-compliant and work in any shell. Double brackets [[ ]] are a Bash-specific extension and are generally safer and more powerful. Almost no article written before 2024 covers this properly.

Feature [ ] Single Bracket [[ ]] Double Bracket
Shell compatibility POSIX (sh, dash, bash) Bash 3.2+ only
Word splitting on variables Yes — can break with spaces No — safe with unquoted vars
Pattern matching Not supported Supported with == and *
Regex matching Not supported Supported with =~
&& and || inside test Use -a (AND) and -o (OR) — deprecated in newer POSIX && and || work natively
Risk of "too many arguments" error Yes, when var has spaces No

Note: Combining Conditions in Single Brackets

Inside [ ], you combine conditions using -a for AND and -o for OR. Example: [ -f file -a -r file ]. These work but are considered less readable and -a / -o are deprecated in newer POSIX standards. Inside [[ ]], use && and || instead, which are cleaner and behave more predictably.

Senior Tip: The Quoting Rule of Thumb

Inside [ ], always quote your variables — "$VAR" — because single brackets perform word splitting. If $VAR contains spaces, the unquoted version breaks the test with a "too many arguments" error.

Inside [[ ]], quoting is less critical because Bash does not perform word splitting inside double brackets. [[ $VAR == "test" ]] is safe even when $VAR has spaces. However quoting is still a good habit in both cases for clarity.

Common Mistake: Unquoted variable with spaces in [ ]

If $FILENAME contains spaces, [ $FILENAME == "test" ] throws a "too many arguments" error.

Fix: Use "$FILENAME" with quotes in single brackets, or switch to [[ $FILENAME == "test" ]] where quoting is optional.


bash
LinuxTeck.com
#!/bin/bash
# Demonstrating [[ ]] advantages over [ ]

FILENAME="my report.txt"

# This would FAIL with [ ] if FILENAME has spaces
if [[ $FILENAME == *.txt ]]
then
echo "File is a text file: $FILENAME"
fi

# Regex matching -- only works with [[ ]]
IP="192.168.1.5"
if [[ $IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]
then
echo "Valid IP address: $IP"
else
echo "Invalid IP format"
fi

OUTPUT
File is a text file: my report.txt
Valid IP address: 192.168.1.5

Bash 5.x Note (2025)

Bash 5.2 (default on Ubuntu 22.04+) and Bash 5.1 (Rocky Linux 9) both fully support [[ ]]. Use bash --version to confirm. If you are still on an older system running Bash 4.x, [[ ]] still works but regex with =~ has minor behavioral differences with certain special characters.



#04

File Test Operators — The Complete Reference

File tests are one of the most used parts of shell script decision making in real sysadmin work. Before writing to a file, touching a directory, or running a backup, your script should always check what it is dealing with. Here is every file test operator you need:

Operator What it tests Returns true when
-e Exists File or directory exists
-f Regular file Path is a file, not a directory
-d Directory Path is a directory
-r Readable File is readable by current user
-w Writable File is writable by current user
-x Executable File has execute permission
-s Non-empty File exists and has size greater than zero
-L Symlink Path is a symbolic link

The following two operators test string length, not file properties. They are commonly used alongside file tests inside the same scripts:

Operator What it tests Returns true when
-z Empty string String length is zero
-n Non-empty string String length is greater than zero

Here is a real script that validates a log file before processing it:

bash
LinuxTeck.com
#!/bin/bash
# Validate a log file before processing
LOGFILE="/var/log/app.log"

if [ ! -e "$LOGFILE" ]
then
echo "ERROR: Log file not found: $LOGFILE"
exit 1
fi

if [ ! -r "$LOGFILE" ]
then
echo "ERROR: No read permission on $LOGFILE"
exit 1
fi

if [ ! -s "$LOGFILE" ]
then
echo "WARNING: Log file is empty, nothing to process."
exit 0
fi

echo "Log file is valid. Starting processing..."
wc -l "$LOGFILE"

This pattern — check, then act — is used in virtually every production backup and automation script. Learn more about the bash exit codes and error handling to combine file tests with proper error reporting.



#05

Exit Code Conditionals — if Command Succeeded

Every Linux command returns an exit code. Zero means success. Anything else means something went wrong. This is one of the most practical forms of bash conditional statements in real sysadmin scripts, and almost no beginner article covers it.

How Exit Codes Work

After any command runs, $? holds its exit code. You can test it directly with if [ $? -eq 0 ], or use the cleaner shorthand if command; then which tests the command's success inline without needing $? at all.

Here is a real-world service status check script that works on both Ubuntu and Rocky Linux:

bash
LinuxTeck.com
#!/bin/bash
# Service status check using exit codes
SERVICE="nginx"

if systemctl is-active --quiet "$SERVICE"
then
echo "$SERVICE is running. No action needed."
else
echo "$SERVICE is not running. Attempting to start..."
systemctl start "$SERVICE"
if [ $? -eq 0 ]
then
echo "$SERVICE started successfully."
else
echo "FAILED to start $SERVICE. Check logs: journalctl -xe"
exit 1
fi
fi

This kind of script is what you would run from a cron job every few minutes to keep critical services alive automatically.



#06

Short-Circuit Operators: && and || as One-Liners

You do not always need a full if block. The && and || operators let you write conditional logic in a single line. They are widely used in real shell scripts but rarely explained in beginner articles.

  • command1 && command2 — run command2 only if command1 succeeds (exit 0)
  • command1 || command2 — run command2 only if command1 fails (non-zero exit)
bash
LinuxTeck.com
#!/bin/bash
# Short-circuit operator examples for sysadmin tasks

# Create a directory only if it does not exist
[ -d /opt/myapp ] || mkdir -p /opt/myapp && echo "Directory ready."

# Ping test before connecting
ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1 && echo "Network is up." || echo "Network unreachable. Check your connection."

# Quick backup with error handling in one line
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak && echo "Backup done." || echo "Backup FAILED."

Tip: Use One-Liners for Quick Guards

One-liners are perfect for pre-checks at the top of a script. For complex multi-step logic, stick to full if-else blocks for readability and easier debugging. Learn more about writing interactive shell scripts that combine both styles effectively.



#07

Real Sysadmin Scripts: Disk, Service, and User Checks

Here are three production-grade decision scripts used by Linux sysadmins daily. These replace the toy examples with actual automation that solves real problems.

Script 1 — Disk Usage Alert (works on Ubuntu and Rocky Linux)

bash
LinuxTeck.com
#!/bin/bash
# Disk usage alert script
THRESHOLD=80
PARTITION="/"

USAGE=$(df -h "$PARTITION" | awk "NR==2 {print $5}" | tr -d "%")

if [ "$USAGE" -ge "$THRESHOLD" ]
then
echo "ALERT: Disk usage on $PARTITION is at ${USAGE}% -- above threshold of ${THRESHOLD}%"
echo "Top 10 largest directories:"
du -sh /* 2>/dev/null | sort -rh | head -10
else
echo "OK: Disk usage on $PARTITION is ${USAGE}% -- within safe limits."
fi

For more disk monitoring examples, see the df command guide with examples and du command usage.

Script 2 — Automatic Linux Server Backup with Conditional Checks

bash
LinuxTeck.com
#!/bin/bash
# Backup script with pre-flight checks
SOURCE="/etc"
DEST="/backup/etc-$(date +%F)"

if [ ! -d "$SOURCE" ]
then
echo "Source directory $SOURCE not found. Exiting."
exit 1
fi

mkdir -p "$DEST" || { echo "Cannot create backup directory."; exit 1; }

cp -r "$SOURCE" "$DEST"

if [ $? -eq 0 ]
then
echo "Backup successful: $SOURCE --> $DEST"
else
echo "Backup FAILED. Check permissions and disk space."
exit 1
fi

See the full automatic Linux backup script guide for a more complete version with rotation and email alerts.



#08

The case Statement — Cleaner Multi-Branch Logic

When you have five or more conditions to check on the same variable, a chain of elif blocks gets messy fast. The case statement is cleaner and easier to read. Think of it as a switch statement for Bash.

bash
LinuxTeck.com
#!/bin/bash
# OS detection and package manager selection
read -p "Enter your Linux distro (ubuntu/rocky/centos/fedora): " DISTRO

case "$DISTRO" in
ubuntu|debian)
echo "Using APT package manager"
PKG_CMD="apt-get install -y"
;;
rocky|centos|rhel)
echo "Using DNF package manager"
PKG_CMD="dnf install -y"
;;
fedora)
echo "Using DNF (Fedora) package manager"
PKG_CMD="dnf install -y"
;;
*)
echo "Unknown distro: $DISTRO. Exiting."
exit 1
;;
esac

echo "Package install command set to: $PKG_CMD"

OUTPUT
Enter your Linux distro (ubuntu/rocky/centos/fedora): rocky
Using DNF package manager
Package install command set to: dnf install -y

The *) wildcard at the end acts as the default catch-all. Always include it in production scripts so unexpected input is handled gracefully. See the full bash case statement guide for more pattern matching examples.



#09

Combining Conditions: && || -a -o Inside Tests

Sometimes one condition is not enough. You need to check two things at once. There are two ways to combine conditions in Bash, and it matters which one you use.

Method Works In Example
-a (AND) [ ] only [ -f file -a -r file ]
-o (OR) [ ] only [ -f file -o -d file ]
&& (AND) [[ ]] preferred [[ -f file && -r file ]]
|| (OR) [[ ]] preferred [[ -f file || -d file ]]
bash
LinuxTeck.com
#!/bin/bash
# Combining conditions in bash conditionals
CONFIG="/etc/myapp/config.conf"

# Check file exists AND is readable
if [[ -f "$CONFIG" && -r "$CONFIG" ]]
then
echo "Config file is accessible. Loading..."
else
echo "Config not found or not readable. Using defaults."
fi

# Check if user is root OR has sudo group
if [[ $EUID -eq 0 || $(groups | grep -c sudo) -gt 0 ]]
then
echo "Sufficient privileges to run this script."
else
echo "Run this script as root or with sudo."
exit 1
fi



#10

Debugging Bash Conditionals with bash -x

When your if statement is not behaving the way you expect, the fastest fix is to run the script with bash -x. This prints every command before it runs, so you can see exactly what value was in each variable when the condition was evaluated.

How to Use bash -x

You can debug in two ways. Either run the script with bash -x ./script.sh, or add set -x at the top of your script to enable tracing from the start. Use set +x to turn it off after a suspicious block so you are not drowned in output.

bash
LinuxTeck.com
#!/bin/bash
# Enable debug tracing for the conditional block only
USER="admin"

set -x # Start tracing here

if [[ "$USER" == "admin" ]]
then
echo "Admin user detected"
fi

set +x # Stop tracing

echo "Rest of script runs normally"

OUTPUT
+ [[ admin == admin ]]
+ echo 'Admin user detected'
Admin user detected
+ set +x
Rest of script runs normally

The + prefix on each line is the trace output. You can see the actual value that was tested. This saves a lot of time compared to adding echo statements everywhere. See the Linux shell scripting cheat sheet for a quick reference of other useful debug flags.



#11

Troubleshooting Bash Conditional Errors

These are the actual error messages that break Bash if-else scripts, and what causes them. This section exists because no other tutorial covers it, and it is exactly where beginners get stuck.

Error 1: "[: too many arguments"

Cause: A variable contains spaces and you used single brackets without quoting it.

Broken: if [ $FILENAME == "test" ] when FILENAME = "my file"

Fix: Quote the variable — if [ "$FILENAME" == "test" ] — or switch to [[ $FILENAME == "test" ]].

Error 2: "unary operator expected"

Cause: The variable is empty or unset, so the test receives nothing where a value is expected.

Broken: if [ $MYVAR -gt 5 ] when MYVAR is empty

Fix: Always quote numeric comparisons — if [ "$MYVAR" -gt 5 ] — or use [[ ]] which handles empty variables safely.

Error 3: "command not found" on [ or [["

Cause: Missing space after the opening bracket. Bash sees [$VAR as one token and looks for a command with that name.

Fix: Always write [ $VAR -eq 0 ] with a space after [ and before ]. Both spaces are required.

Error 4: if block runs when it should not

Cause: You used = (assignment) inside a test instead of == (comparison). In single brackets, = does work as equality but it is easy to misread. In arithmetic contexts, you need -eq not ==.

Fix: For string comparison use == or = inside brackets. For numbers always use -eq, -gt, -lt etc. inside brackets, or use (( )) for arithmetic where == works naturally.

Error 5: "syntax error near unexpected token fi"

Cause: A missing then keyword, or the script was created on Windows with CRLF line endings. Bash on Linux expects LF only.

Fix: Add the missing then, or convert the file with dos2unix script.sh to strip Windows line endings.



FAQ

Frequently Asked Questions

What is the difference between [ ] and [[ ]] in Bash?

Single brackets [ ] are a POSIX standard test command that works in any shell including sh and dash. Double brackets [[ ]] are a Bash-specific keyword that adds pattern matching with *, regex matching with =~, and safer handling of variables with spaces. If you are writing a script that only runs in Bash (check with #!/bin/bash), prefer [[ ]]. If the script needs to run on minimal systems using sh or dash, use [ ] with careful quoting.

How do I use an if statement in a shell script?

Write if [ condition ]; then on one line, or spread it across two lines with if [ condition ] on the first and then on the second. The block ends with fi. The three things beginners forget are: spaces inside the brackets, the then keyword, and the closing fi. Check out the bash if elif else guide for more complete syntax examples with output.

What does $? do in a Bash if statement?

$? holds the exit code of the last command that ran. Zero means the command succeeded. Any non-zero value means it failed. You use it like this: run a command, then immediately check if [ $? -eq 0 ]. One important rule: check $? immediately after the command you care about because the next command you run will overwrite it.

Can I use && and || instead of if-else in Bash?

Yes. command1 && command2 runs command2 only if command1 succeeds. command1 || command2 runs command2 only if command1 fails. These work well for simple one-line guards. For anything with more than two outcomes or where you need multiple commands in each branch, use a full if-else block instead — it is easier to read and debug.

Does bash if-else work the same on Ubuntu and Rocky Linux?

Yes, the syntax is identical on both. Ubuntu 24.04 ships with Bash 5.2 and Rocky Linux 9 ships with Bash 5.1. Both fully support [[ ]], all file test operators, and regex matching. The only practical difference is system-level things inside your scripts, like using apt on Ubuntu versus dnf on Rocky. The conditional logic itself behaves the same. See the RHEL vs Ubuntu server comparison for a deeper look at the differences between these platforms.

Why does my if statement say "command not found" even though it looks correct?

The most common cause is a missing space inside the brackets. Bash requires a space after [ and before ]. Writing [$VAR -gt 5] without spaces makes Bash try to run a command literally called [$VAR, which obviously does not exist. Fix it by writing [ $VAR -gt 5 ] with spaces on both sides. The second common cause is using #!/bin/sh at the top of the file but using [[ ]], which is a Bash-only feature not available in sh.

END

Summary

Mastering bash conditional statements is the step that takes your scripts from simple command lists to real automation tools. This guide covered 11 practical decision scripts spanning basic if-else blocks, the important [[ ]] vs [ ] distinction, file test operators, exit code conditionals, short-circuit one-liners, case statements, combined conditions, debug techniques, and the five most common errors that trip people up. Each example is tested on Ubuntu 24.04 and Rocky Linux 9 and reflects how scripts are actually written in 2025 production environments.

Keep building on these patterns with the Linux bash scripting automation guide.

Related Articles

LinuxTeck — A Complete Linux Learning Blog
Learn step-by-step how to automate Linux tasks with real-world scripts and practical examples.



About Sharon J

Sharon J is a Linux System Administrator with strong expertise in server and system management. She turns real-world experience into practical Linux guides on Linux Teck.

View all posts by Sharon J →

Leave a Reply

Your email address will not be published.

L