Skip to content
AgileLogics
5 min read

Regular Expressions in Bash

Prerequisites: File and string tests, Searching text with grep

Inside [[ ]], the =~ operator matches a string against an extended regular expression, and captures matching groups into the BASH_REMATCH array — no external tools needed.

Validating and extracting

re='^([a-z0-9._%+-]+)@([a-z0-9.-]+\.[a-z]{2,})$'

for email in "ada@example.com" "not-an-email" "linus@kernel.org"; do
  if [[ $email =~ $re ]]; then
    echo "$email → user: ${BASH_REMATCH[1]}, domain: ${BASH_REMATCH[2]}"
  else
    echo "$email → invalid"
  fi
done
ada@example.com → user: ada, domain: example.com
not-an-email → invalid
linus@kernel.org → user: linus, domain: kernel.org
  • BASH_REMATCH[0] is the whole match; [1], [2], ... are the groups in parentheses.
  • Store the regex in a variable and use it unquoted (=~ $re). Quoting the right side makes Bash treat it as plain text, not a pattern.

Parsing structured strings

version="v2.14.3-beta"
if [[ $version =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(.+))?$ ]]; then
  major=${BASH_REMATCH[1]}
  minor=${BASH_REMATCH[2]}
  patch=${BASH_REMATCH[3]}
  tag=${BASH_REMATCH[5]:-none}
  echo "major=$major minor=$minor patch=$patch tag=$tag"
  echo "next minor: v$major.$(( minor + 1 )).0"
fi
major=2 minor=14 patch=3 tag=beta
next minor: v2.15.0

Log lines

line='192.168.1.20 - - [22/Apr/2025:09:12:30] "GET /api/orders HTTP/1.1" 500 1234'
re='^([0-9.]+) .*"([A-Z]+) ([^ ]+) [^"]*" ([0-9]{3})'
if [[ $line =~ $re ]]; then
  printf 'ip=%s method=%s path=%s status=%s\n' "${BASH_REMATCH[@]:1:4}"
fi
ip=192.168.1.20 method=GET path=/api/orders status=500

Build and test patterns in the Regex Tester — Bash uses POSIX extended regular expressions, which don't support shortcuts like \d (use [0-9]).

When to use what

  • [[ =~ ]] — checking or parsing one string inside a script.
  • grep -E / sed -E / awk — processing many lines of a file, where they're much faster than a Bash loop.