Linux Basic — Bash =~ Operator
Bash =~ Operator
Bash has a built-in regex engine inside the [[ ... ]] conditional. Use =~ to test a string against an ERE pattern with zero external processes and direct access to capture groups.
str="user-1234"
if [[ "$str" =~ ^user-([0-9]+)$ ]]; then
echo "matched, id = ${BASH_REMATCH[1]}"
fi
Syntax Rules
[[ "$string" =~ regex_pattern ]]
- Always inside
[[ ]](not[ ]) - The left side is the string — quote it
- The right side is the regex — don’t quote it (since Bash 3.2 with
compat31off, quoting forces literal matching)
# WRONG — quotes around regex make it literal
[[ "$x" =~ "^[0-9]+$" ]] # only matches the literal "^[0-9]+$"
# RIGHT
[[ "$x" =~ ^[0-9]+$ ]]
If your regex has spaces or shell metacharacters, assign it to a variable first:
pat='^(error|warn): (.+)$'
[[ "$line" =~ $pat ]] # unquoted on the right
Capture Groups: BASH_REMATCH
After a successful match, BASH_REMATCH holds the whole match in [0] and each (group) in [1], [2], …
ip="10.20.30.40"
if [[ "$ip" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
echo "octets: ${BASH_REMATCH[1]} ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} ${BASH_REMATCH[4]}"
fi
BASH_REMATCH is overwritten on every =~ — copy what you need immediately.
Common Patterns
Validate input
read -p "Port: " port
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
echo "not a number" >&2; exit 1
fi
(( port >= 1 && port <= 65535 )) || { echo "out of range" >&2; exit 1; }
Branch on type
case_str="$1"
if [[ "$case_str" =~ ^[0-9]+$ ]]; then echo "integer"
elif [[ "$case_str" =~ ^[0-9]+\.[0-9]+$ ]]; then echo "decimal"
elif [[ "$case_str" =~ ^[A-Za-z][A-Za-z0-9_]*$ ]]; then echo "identifier"
else echo "other"
fi
Pull a version from a tag
tag="release-v2.4.1-rc3"
if [[ "$tag" =~ v([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch="${BASH_REMATCH[3]}"
echo "version $major.$minor.$patch"
fi
Why Use =~ Instead of grep?
=~ |
grep -Eq |
|---|---|
| no subprocess | forks grep |
| gives you capture groups | only yes/no (or use grep -oE) |
| Bash-only | portable to any POSIX shell |
| ERE only | ERE (with -E) or BRE |
For scripts that are already Bash and need substring extraction, =~ is faster and cleaner. For portability or when you genuinely need to scan a file, use grep.
Pitfalls
| Pitfall | Fix |
|---|---|
| Quoting the regex | Don’t — or assign to a var first |
Special chars (., +, [) match literally |
Escape: \. \+ \[ |
BASH_REMATCH clobbered by next =~ |
Copy values to local vars right away |
| Not anchoring → false positives | Use ^ and $ |
Inside ( ) subshell → captures lost in parent |
Stay in the same shell |
Next → POSIX Character Classes