Input handling is useful, but decisions are where scripts become dynamic. In Bash, if logic is powered by tests and command exit codes.
Table of Contents
This post walks through practical conditional patterns for production scripts.
Core Control Flow
Test Syntax and Operators
Combining Conditions
Series Navigation
Core control flow
if elif else structure
Bash conditionals follow a simple structure that is easy to scan in larger scripts.
if [[ condition_a ]]; then
echo "branch a"
elif [[ condition_b ]]; then
echo "branch b"
else
echo "fallback branch"
fiCommand exit codes as conditions
A command itself can be the condition.
if grep -q "production" config.env; then
echo "production settings found"
else
echo "production settings missing"
fiThis is one of the cleanest Bash idioms because it avoids storing temporary values.
Test syntax and operators
Test forms and when to use each
test and [ ] are equivalent in many cases, while [[ ]] offers safer string handling and pattern support.
test -f /etc/hosts && echo "exists"
[ -d /tmp ] && echo "tmp exists"
[[ -n "$USER" ]] && echo "user is set"Prefer [[ ]] for script logic unless you need strict POSIX shell compatibility.
File checks
Common file checks:
-ffor regular file-dfor directory-xfor executable-rfor readable file-wfor writable file
if [[ -x "./deploy.sh" ]]; then
./deploy.sh
else
echo "deploy script is not executable"
fiString checks
Use -z for empty strings and -n for non-empty strings.
if [[ -z "$api_token" ]]; then
echo "missing API token"
exit 1
fi
if [[ "$env" == "prod" ]]; then
echo "running production workflow"
fiNumeric checks
Use numeric operators for integer comparisons.
if [[ "$retry_count" -ge 3 ]]; then
echo "max retries reached"
fiUseful numeric operators include -eq, -ne, -lt, -le, -gt, and -ge.
Combining conditions
Use logical operators safely
You can combine expressions directly inside [[ ]].
if [[ -n "$env" && "$env" != "dev" ]]; then
echo "non-dev environment selected"
fi
if [[ "$size_mb" -gt 100 || "$force" == "true" ]]; then
echo "continue with upload"
fiBuild readable conditions
When conditions get complex, split them into named variables for clarity.
has_token=false
is_prod=false
[[ -n "$api_token" ]] && has_token=true
[[ "$env" == "prod" ]] && is_prod=true
if [[ "$has_token" == "true" && "$is_prod" == "true" ]]; then
echo "production deployment can proceed"
fiPractical script example
#!/usr/bin/env bash
env_name="${1:-dev}"
config_file="./config/${env_name}.env"
if [[ ! -f "$config_file" ]]; then
echo "config file not found: $config_file"
exit 1
fi
if [[ "$env_name" == "prod" ]]; then
echo "running production checks"
elif [[ "$env_name" == "stage" ]]; then
echo "running stage checks"
else
echo "running dev checks"
fiNext in this series
Next, we will use case statements to replace long if chains when matching subcommands and pattern-based options.