How to Validate Email in Bash
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
| Method | Complexity | Reliability | Best For | Speed |
|---|---|---|---|---|
| Regex pattern | Simple | Good | Format checking | Fastest |
| Regex + DNS | Moderate | Better | Production use | Medium |
| Full RFC 5321 | Complex | Excellent | Strict validation | Slow |
| Custom function | Moderate | Good | Reusable code | Medium |
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.