Skip to main content

How to Validate Email in Bash

• 3 min read
bash validation regex email validation

Quick Answer: Validate Email in Bash

To validate an email address in Bash, use regex matching: [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]. This pattern checks for valid username, @ symbol, domain, and TLD. For stricter validation, check if the domain exists with DNS lookup.

Quick Comparison: Email Validation Methods

MethodComplexityReliabilityBest ForSpeed
Regex patternSimpleGoodFormat checkingFastest
Regex + DNSModerateBetterProduction useMedium
Full RFC 5321ComplexExcellentStrict validationSlow
Custom functionModerateGoodReusable codeMedium

Bottom line: Use regex pattern for quick validation; add DNS check for production systems.


Email Validation in Bash

Email validation is a common requirement in scripts - checking user input, processing CSV files with email addresses, or validating configuration. You can use regular expressions to validate email format in Bash.

Simple Email Pattern

A simple but effective email regex pattern that’s fast and reliable:

#!/bin/bash

email="user@example.com"
email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

if [[ "$email" =~ $email_pattern ]]; then
  echo "Valid email: $email"
else
  echo "Invalid email: $email"
fi

This pattern checks for the essential components of an email address. The breakdown is straightforward: username characters, @, domain name, dot separator, and TLD. This works for most practical use cases where you just need to verify the format looks reasonable.

When to Use Simple Regex Validation

Use this method when:

  • You’re validating user input in scripts
  • You need the fastest validation
  • You’re processing email lists for format checking
  • You want a pure Bash solution (no dependencies)
  • You can’t access DNS for server-side validation

Avoid it when:

  • You need to verify the email actually receives mail (use DNS)
  • You’re following strict RFC 5321 compliance
  • The email might have unusual but valid characters
  • You’re in a production system requiring high accuracy

Pattern Explanation

  • [a-zA-Z0-9._%+-]+ - Username with allowed characters (letters, numbers, dot, underscore, percent, plus, hyphen)
  • @ - Literal @ symbol (required separator)
  • [a-zA-Z0-9.-]+ - Domain name
  • \. - Literal dot (not any character)
  • [a-zA-Z]{2,} - At least 2-letter TLD (like com, org, co.uk)

Function for Email Validation

Create a reusable function:

#!/bin/bash

validate_email() {
  local email="$1"
  local pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

  if [[ "$email" =~ $pattern ]]; then
    return 0  # Valid
  else
    return 1  # Invalid
  fi
}

# Usage
if validate_email "john.doe@example.com"; then
  echo "Email is valid"
else
  echo "Email is invalid"
fi

Practical Example: Process Email List

#!/bin/bash

# Validate each email in a file
email_file="emails.txt"
email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

valid_count=0
invalid_count=0

while IFS= read -r email || [[ -n "$email" ]]; do
  # Skip empty lines and comments
  [[ -z "$email" ]] && continue
  [[ "$email" =~ ^# ]] && continue

  if [[ "$email" =~ $email_pattern ]]; then
    echo "✓ $email"
    ((valid_count++))
  else
    echo "✗ $email"
    ((invalid_count++))
  fi
done < "$email_file"

echo ""
echo "Summary: $valid_count valid, $invalid_count invalid"

Method 2: Stricter Pattern (RFC-ish)

For more accurate validation (though not fully RFC 5321 compliant), use a stricter regex pattern that handles more edge cases:

#!/bin/bash

# More strict pattern
strict_pattern='^[a-zA-Z0-9][a-zA-Z0-9.!#$%&'"'"'*+/=?^_`{|}~-]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'

email="user+tag@example.co.uk"

if [[ "$email" =~ $strict_pattern ]]; then
  echo "Valid (strict): $email"
fi

This pattern is more comprehensive and handles special characters like + and - that are valid in email addresses according to RFC 5321. It’s more restrictive about what comes before @ and handles nested domain structures better.

When to Use Stricter Pattern

Use this when:

  • You need to support email addresses with special characters (+, -, etc.)
  • You’re validating emails that may have subdomains
  • You want closer adherence to RFC standards
  • You can afford the slightly slower regex matching

Method 3: Advanced Validation with DNS Checking

Validate beyond just format by checking if the domain actually exists:

#!/bin/bash

validate_email_advanced() {
  local email="$1"
  local domain

  # Basic format check
  if ! [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Invalid format"
    return 1
  fi

  # Extract domain
  domain="${email#*@}"

  # Check if domain resolves (optional - requires dig/nslookup)
  if command -v dig &>/dev/null; then
    if ! dig +short "$domain" | grep -q .; then
      echo "Domain does not appear to exist: $domain"
      return 1
    fi
  fi

  echo "Valid email: $email"
  return 0
}

validate_email_advanced "user@example.com"

This approach combines format checking with DNS validation to ensure the domain actually exists. It’s much more reliable for production systems because it verifies the domain will accept mail.

When to Use Advanced Validation

Use this method when:

  • You’re building a user registration system
  • You need high confidence the email can receive mail
  • You have DNS access available
  • You’re okay with slightly slower validation (DNS lookup takes time)
  • You want to catch typos in domain names (e.g., gmial.com)

Common Invalid Patterns

Test what should NOT match:

#!/bin/bash

email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

# These should NOT match
invalid_emails=(
  "no-at-sign.com"          # No @
  "@example.com"             # No local part
  "user@"                     # No domain
  "user@.com"                # No domain name
  "user@domain"              # No TLD
  "user..name@example.com"   # Double dots (actually allowed in some systems)
  "user @example.com"        # Space
)

for email in "${invalid_emails[@]}"; do
  if [[ ! "$email" =~ $email_pattern ]]; then
    echo "✓ Correctly rejected: $email"
  else
    echo "✗ Incorrectly accepted: $email"
  fi
done

Real-World Example: User Registration

#!/bin/bash

register_user() {
  local email="$1"
  local password="$2"
  local email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

  # Validate email
  if [[ ! "$email" =~ $email_pattern ]]; then
    echo "ERROR: Invalid email address"
    return 1
  fi

  # Validate password (at least 8 characters)
  if [[ ${#password} -lt 8 ]]; then
    echo "ERROR: Password must be at least 8 characters"
    return 1
  fi

  # Check if email already registered
  if grep -q "^$email:" /etc/passwd 2>/dev/null; then
    echo "ERROR: Email already registered"
    return 1
  fi

  echo "✓ Registration validated for: $email"
  # Continue with registration...
  return 0
}

register_user "newuser@example.com" "SecurePass123"

Important Notes

  • Perfect email validation requires checking if the mail server accepts the address
  • DNS validation (checking if domain MX records exist) is more reliable than regex
  • Regex validation is good enough for most use cases
  • The pattern shown is simplified - RFC 5321 allows many special characters
  • Always validate on the backend, not just in frontend scripts
  • Consider that some valid emails don’t follow common patterns (example: a@b.c)

Common Issues

Using quoted pattern:

# WRONG - treats pattern as literal string
if [[ "$email" =~ "$pattern" ]]; then

# CORRECT
if [[ "$email" =~ $pattern ]]; then

Forgetting to escape the dot:

# WRONG - matches any character
pattern='^user@domain.com$'

# CORRECT - matches literal dot
pattern='^user@domain\.com$'

Quick Reference

# Simple validation
email_pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
[[ "$email" =~ $email_pattern ]] && echo "Valid"

# Function for reuse
validate_email() {
  local email="$1"
  [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

# Usage
validate_email "user@example.com" && echo "Valid" || echo "Invalid"

Summary

Email validation in Bash using regex is straightforward for format checking. Use the pattern ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ for reliable validation of common email formats. For production systems, consider additional checks like DNS validation or confirming the server accepts the address. The combination of format validation and DNS lookup provides the best balance of performance and reliability.