← Back to DevBytes

Advanced Zsh: Conditionals Techniques

Advanced Zsh: Conditionals Techniques

What Are Conditionals in Zsh?

Conditionals are the backbone of any scripting language, allowing your shell to make decisions based on the state of variables, files, command exit statuses, or the results of arithmetic and string comparisons. In Zsh, conditionals go far beyond the basic if [ ... ] syntax inherited from Bourne shell. Zsh offers powerful enhancements such as the double-bracket [[ ... ]] construct, arithmetic conditionals (( ... )), pattern matching with extended globbing, and a rich set of operators for string, numeric, and file tests. Understanding these advanced techniques is essential for writing robust, efficient, and readable Zsh scripts.

Why Advanced Conditionals Matter

Advanced conditionals in Zsh matter for several reasons:

How to Use Advanced Conditionals

The Classic if / then / elif / else Structure

At its core, an if block evaluates a command or a test expression. The exit status (0 for success, non-zero for failure) determines which branch executes. Here’s a basic example:

if command -v git >/dev/null; then
    echo "Git is installed"
else
    echo "Git not found"
fi

You can chain conditions with elif:

value=42
if (( value < 0 )); then
    echo "Negative"
elif (( value == 0 )); then
    echo "Zero"
else
    echo "Positive"
fi

The [[ ]] Keyword – Enhanced Test Command

The double-bracket [[ ... ]] is a Zsh built-in that provides a superset of the traditional [ ] test. It avoids word-splitting, allows pattern matching, regex matching, and logical operators without escaping issues. Use it for string comparisons, file tests, and combined conditions.

String Comparisons:

name="Zsh"
if [[ $name == "Zsh" ]]; then
    echo "Correct shell"
fi

# Inequality
if [[ $name != "bash" ]]; then
    echo "Not bash"
fi

Pattern Matching:

file="report_2025.csv"
if [[ $file == *.csv ]]; then
    echo "CSV file"
fi

# Use extended globs (setopt extendedglob)
setopt extendedglob
if [[ $file == report_<0-9>*.csv ]]; then
    echo "Report file with digits"
fi

Regex Matching:

email="user@example.com"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid email format"
else
    echo "Invalid email"
fi

File Tests: Use operators like -e (exists), -f (regular file), -d (directory), -r (readable), etc.

if [[ -f /etc/passwd ]]; then
    echo "Password file exists"
fi

if [[ -d ~/Documents && -w ~/Documents ]]; then
    echo "Documents directory exists and is writable"
fi

Logical Operators inside [[ ]]: Use && and || directly without escaping.

if [[ -n $VAR && $VAR -gt 10 ]]; then
    echo "VAR is set and greater than 10"
fi

if [[ -z $VAR || $VAR == "default" ]]; then
    echo "VAR is empty or equals 'default'"
fi

Arithmetic Conditionals with (( ))

The (( ... )) construct evaluates arithmetic expressions. If the result is non-zero, the exit status is 0 (true); if zero, exit status is 1 (false). This is the cleanest way to compare numbers in Zsh.

count=5
if (( count > 3 )); then
    echo "More than 3"
fi

# Complex expression
if (( count % 2 == 0 )); then
    echo "Even"
else
    echo "Odd"
fi

# Combined with assignment
max=100
if (( (count * 2 + 10) < max )); then
    echo "Within limit"
fi

You can also use (( ... )) directly in a while loop or as a standalone command:

i=0
while (( i < 5 )); do
    echo "Iteration $i"
    (( i++ ))
done

Ternary-Like Conditionals: Using && and || for Simple Branches

Zsh does not have a built-in ternary operator, but you can emulate one with logical AND/OR. This is useful for quick, inline conditionals:

# Simple "if then else" inline
[[ -f $file ]] && echo "File exists" || echo "File missing"

# Assign a value conditionally
name=${other_name:-"default"}
# More complex: use a subshell or function
result=$(( [[ $x -

πŸš€ Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started β€” $23.99/mo
← Back to all articles