Skip to main content

How to Validate Phone Number

• 2 min read
bash validation regex phone number pattern matching

Quick Answer: Validate Phone Number in Bash

To validate US phone numbers, use: [[ "$phone" =~ ^[0-9]{3}-[0-9]{3}-[0-9]{4}$ ]] for format 123-456-7890 or [[ "$phone" =~ ^[0-9]{10}$ ]] for 10 digits only. Different patterns work for different formats.

Quick Comparison: Phone Validation Methods

FormatPatternStrictnessUse Case
10 digits^[0-9]{10}$StrictUS/Canada
dashes^[0-9]{3}-[0-9]{3}-[0-9]{4}$FormattedFormatted input
flexible^[0-9+\-\s()]{10,}$LooseUser-friendly
intl prefix^\\+[0-9]{1,3}-?[0-9]{6,14}$InternationalGlobal numbers

Bottom line: Choose pattern based on expected format; simple 10-digit for basic validation.


Validate phone numbers in Bash using regex patterns. This tutorial covers common phone number formats and practical validation methods.

Phone Number Formats

Phone numbers vary by region. The most common formats are:

  • US/Canada: (123) 456-7890 or 123-456-7890 or 1234567890
  • International: +1-123-456-7890
  • Simple: 123 456 7890 or 1234567890

Method 1: Basic Validation Pattern

#!/bin/bash

phone="555-123-4567"

# US format: XXX-XXX-XXXX
if [[ $phone =~ ^[0-9]{3}-[0-9]{3}-[0-9]{4}$ ]]; then
  echo "Valid phone number"
else
  echo "Invalid format"
fi

Output:

Valid phone number

Validate Multiple Formats

#!/bin/bash

validate_phone() {
  local phone="$1"

  # Match: (123) 456-7890 or 123-456-7890 or 1234567890
  if [[ $phone =~ ^(\([0-9]{3}\)|[0-9]{3}[-.]?)?[0-9]{3}[-.]?[0-9]{4}$ ]]; then
    return 0
  else
    return 1
  fi
}

# Test cases
validate_phone "555-123-4567" && echo "Valid" || echo "Invalid"
validate_phone "(555) 123-4567" && echo "Valid" || echo "Invalid"
validate_phone "5551234567" && echo "Valid" || echo "Invalid"
validate_phone "555.123.4567" && echo "Valid" || echo "Invalid"
validate_phone "invalid" && echo "Valid" || echo "Invalid"

Output:

Valid
Valid
Valid
Valid
Invalid

International Format Validation

#!/bin/bash

validate_intl_phone() {
  local phone="$1"

  # Match: +1-123-456-7890 or +44-20-1234-5678
  if [[ $phone =~ ^\+[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}-[0-9]{4}$ ]]; then
    return 0
  else
    return 1
  fi
}

# Test
validate_intl_phone "+1-555-123-4567" && echo "Valid" || echo "Invalid"
validate_intl_phone "+44-20-1234-5678" && echo "Valid" || echo "Invalid"

Output:

Valid
Valid

Remove Non-Digits and Validate

#!/bin/bash

validate_phone_digits() {
  local phone="$1"

  # Remove all non-digits
  local digits_only="${phone//[^0-9]/}"

  # Must be 10 digits (US format)
  if [[ $digits_only =~ ^[0-9]{10}$ ]]; then
    echo "Valid: $digits_only"
  else
    echo "Invalid: Expected 10 digits, got ${#digits_only}"
  fi
}

validate_phone_digits "(555) 123-4567"
validate_phone_digits "555.123.4567"
validate_phone_digits "12345"

Output:

Valid: 5551234567
Valid: 5551234567
Invalid: Expected 10 digits, got 5

Practical Example: Phone List Validator

#!/bin/bash

# File: validate_phones.sh

validate_phone() {
  local phone="$1"

  # US phone: flexible format
  if [[ $phone =~ ^(\+?1[-.]?)?(\()?[0-9]{3}(\))?[-. ]?[0-9]{3}[-. ]?[0-9]{4}$ ]]; then
    return 0
  else
    return 1
  fi
}

input_file="$1"

if [ ! -f "$input_file" ]; then
  echo "ERROR: File not found"
  exit 1
fi

valid_count=0
invalid_count=0

while IFS= read -r phone; do
  # Skip empty lines
  [ -z "$phone" ] && continue

  if validate_phone "$phone"; then
    echo "✓ VALID: $phone"
    ((valid_count++))
  else
    echo "✗ INVALID: $phone"
    ((invalid_count++))
  fi
done < "$input_file"

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

Test file (phones.txt):

555-123-4567
(555) 123-4567
5551234567
555.123.4567
invalid123
+1-555-123-4567

Run:

$ chmod +x validate_phones.sh
$ ./validate_phones.sh phones.txt

Output:

✓ VALID: 555-123-4567
✓ VALID: (555) 123-4567
✓ VALID: 5551234567
✓ VALID: 555.123.4567
✗ INVALID: invalid123
✓ VALID: +1-555-123-4567

Summary: 5 valid, 1 invalid

Strict North American Format

#!/bin/bash

validate_na_phone() {
  local phone="$1"

  # Strict: exactly 10 digits in any standard format
  local digits="${phone//[^0-9]/}"

  if [[ $digits =~ ^[0-9]{10}$ ]]; then
    # Format for display
    local area="${digits:0:3}"
    local exchange="${digits:3:3}"
    local subscriber="${digits:6:4}"
    echo "($area) $exchange-$subscriber"
    return 0
  else
    return 1
  fi
}

# Test
result=$(validate_na_phone "555-123-4567")
if [ $? -eq 0 ]; then
  echo "Formatted: $result"
fi

Output:

Formatted: (555) 123-4567

Common Mistakes

  1. Forgetting to escape parentheses in regex - they need escaping
  2. Not handling country code variations - +1, +44, etc.
  3. Assuming single format - support multiple separators
  4. No length validation - check total digits first
  5. Case sensitivity - not an issue, but pattern inconsistency is

Key Points

  • Use [[ $var =~ pattern ]] for regex matching
  • Phone formats vary greatly by region
  • Extract digits with ${phone//[^0-9]/} for easy validation
  • Always test with real-world phone numbers
  • Provide helpful error messages to users

Summary

Phone validation requires flexible regex patterns since formats vary. Start with digit extraction for simplicity, then add format-specific patterns as needed. Always validate on the server side, even if you validate client-side.