Bash Conditionals for DevOps
In the first article we wrote linear scripts — commands that run from top to bottom. In real operations, scripts often need to make decisions: skip work if a service is already running, raise an alert if disk usage crosses a threshold, or require confirmation before deploying to production.
Bash conditionals are what make a script react to the current system state. This article walks through if/elif/else, comparison operators, file checks, and case…esac — with DevOps examples you can reuse immediately.
if / elif / else
Basic syntax:
| |
A few notes:
- In modern Bash, prefer
[[ ... ]]over[ ... ].[[ ]]is Bash’s conditional expression syntax. It supports extra features such as pattern matching and is safer when variables contain spaces. - Every
ifblock must end withfi. If you forget it,bash -n script.shwill report a syntax error. - Indent the commands inside the block, commonly with 2 spaces, to keep the script readable.
A tiny example — check whether the script is running as root:
| |
EUID is a Bash built-in variable that stores the effective user ID. The root user always has EUID=0.
Numeric comparisons
When comparing integers, use operators such as -eq, -ne, and -gt.
| Operator | Meaning |
|---|---|
-eq | equal |
-ne | not equal |
-gt | greater than |
-ge | greater than or equal |
-lt | less than |
-le | less than or equal |
Example — check disk usage for /:
| |
The script returns exit codes 0/1/2 for OK/WARN/CRIT. This is a familiar convention in monitoring systems such as Nagios/Icinga, and it is also easy to use from cron or CI/CD pipelines.
Tip: You can use
(( ... ))for arithmetic expressions, for exampleif (( DISK_USAGE_PERCENT >= 80 )); then. Inside(( )), you do not need$before variable names and can use familiar operators such as>=,<=, and==.
String comparisons
For strings, use a different set of operators:
| Operator | Meaning |
|---|---|
= / == | equal |
!= | not equal |
-z STR | string is empty |
-n STR | string is not empty |
< / > | lexical ordering, only inside [[ ]] |
Example — gate an environment before deployment:
| |
Important note: always quote variables ("${ENVIRONMENT}"). If you leave $ENVIRONMENT unquoted and the variable is empty, [[ $ENVIRONMENT == "production" ]] still works, but similar scripts using [ ... ] may fail with syntax errors.
File and directory checks
Bash provides built-in operators for checking file state:
| Operator | Meaning |
|---|---|
-e | exists, file or directory |
-f | exists and is a regular file |
-d | exists and is a directory |
-r | readable |
-w | writable |
-x | executable |
-s | file exists and has size > 0 |
-L | is a symbolic link |
Example — ensure the log directory exists and the config file is valid before starting an app:
| |
The ! at the beginning of an expression is negation — [[ ! -f ... ]] reads as “if the file does not exist”.
Combining multiple conditions (AND / OR)
There are two common ways to combine conditions.
Option 1 — combine inside one [[ ]] with && and ||:
| |
Option 2 — chain two separate test commands with shell-level && / ||:
| |
In automation scripts, option 1 is easier to read when the conditions belong to the same decision. Option 2 is useful when you want “run this command, and only if it succeeds, run the next one”, for example:
| |
If mkdir fails, tar will not run.
case…esac — when if/elif gets too long
When you need to match a variable against many possible values, case is usually shorter and easier to read than a long if/elif chain.
Syntax:
| |
Example — route service operations from one script:
| |
case supports glob-style patterns, so you can write dev*), *.log), [0-9]*), and similar branches. This is convenient when branching by environment name or file type.
DevOps practice: check_service.sh
Now combine the concepts above into a practical script that checks a service and disk usage.
Create check_service.sh:
| |
Try it:
| |
Notable points:
- Use
systemctl is-active --quietfor status checks — this is cleaner than parsing command output withgrep. - Return meaningful exit codes (
0/1/2) so cron, alertmanager, or CI/CD pipelines can understand severity. - The
case 1 in $(( ... )) )pattern is a small trick: an arithmetic expression returns1when the condition is true and0when it is false, so the branch matching1runs. If you find this hard to read, useif/elif/elseinstead for clarity.
Common mistakes
- Missing spaces in
[[ ]]: Bash requires spaces around[[,]], and operators.[[$A == "x"]]is a syntax error. - Using
=for integers:[[ "${COUNT}" = 1 ]]performs a string comparison. For numbers, use-eqor move the expression into(( )). - Not quoting variables: If a variable is empty, Bash may parse the expression incorrectly in
[ ](POSIX test).[[ ]]is safer, but keeping the habit of"${VAR}"is still recommended. - Forgetting
;;incase: Eachcasebranch must end with;;. Without it, Bash continues parsing into the next branch after a match. - Confusing
&&/||with pipelines:cmd1 && cmd2runscmd2only whencmd1succeeds with exit code0. Do not confuse this with the pipe|, which passes stdout from one command to another.
Implementation notes
- When applying this to your own project, think about exit codes before writing the logic: when should the script return
0, and when should it return non-zero? This directly affects cron jobs and pipelines. - Best practices:
- Prefer
[[ ]]over[ ]in Bash-only scripts. - Always include a default
*)branch incaseto catch invalid values and print usage. - When a condition grows beyond 4–5
if/elifbranches, consider switching tocaseor extracting logic into a function. - Put threshold variables near the top of the file or accept them from CLI arguments — do not scatter hardcoded values throughout the script.
- Prefer
- Troubleshooting:
- Script enters the wrong branch? → Run it with
bash -x script.shto inspect expanded variable values. - Seeing
unary operator expected? → This is often caused by an empty variable in[ -f $FILE ]. Use[[ -f "${FILE}" ]]instead. casedoes not match? → Check whether your pattern needs quoting. Incase, entries afterinare patterns (globs), not plain strings.
- Script enters the wrong branch? → Run it with
🎯 Conclusion
Conditionals are the first step that make your Bash scripts “think” — reacting to state instead of running blindly. With if/elif/else, case…esac, numeric comparisons, string checks, and file tests, you now have enough tools to write practical operations scripts such as health checks, deployment gates, and service dispatchers.
In the next article, we will cover loops in Bash with for, while, and until, including automation examples for iterating over server lists and reading log files line by line. 🚀
References
- GNU Bash Manual — Conditional Constructs — Official documentation for
if,case, andselect. - GNU Bash Manual — Bash Conditional Expressions — Complete list of file, string, and numeric operators used by
[[ ]]andtest. - GNU Bash Manual — Shell Arithmetic — Syntax for
(( ... ))arithmetic expressions. - ShellCheck — A linting tool that catches quoting and comparison issues before scripts reach production.
