grep Anchors and Character Classes Basics
grep anchors ^ and $ match the start and end of a line, while [...] matches any one character from a set.
Tighter patterns mean fewer false positives in CI gates. Anchors and character classes are the building blocks that make a grep precise.
What it does
In a grep pattern, ^ matches the start of a line and $ matches the end, so ^ERROR$ matches a line that is exactly "ERROR". [abc] matches any one of a, b, or c; [^abc] matches any character except those; ranges like [0-9] and [A-Za-z] work too. POSIX classes such as [[:digit:]] and [[:space:]] are portable equivalents.
Common usage
# lines that are exactly "PASS"
grep '^PASS$' results.txt
# lines starting with a timestamp
grep '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' app.log
# POSIX class: any line with a digit
grep '[[:digit:]]' versions.txtOptions
| Token | Matches |
|---|---|
| ^ | Start of line |
| $ | End of line |
| [abc] / [^abc] | Any one (or any not) of the set |
| [0-9] / [a-z] | A character range |
| [[:digit:]] [[:space:]] | POSIX character classes |
In CI
Anchor status assertions with ^...$ so a partial line cannot pass the gate. Prefer POSIX classes like [[:digit:]] over [0-9] when locale or portability matters, and remember that in basic regex {n} needs backslashes (\{n\}) while in -E it does not.
Common errors in CI
In basic (default) grep, { } ( ) + ? are literal unless escaped, so ^[0-9]{4} does not mean four digits without -E or \{4\}. CRLF input breaks $ anchoring because the \r sits before the line end; strip it with tr -d "\r". A [a-z] range can behave by locale; set LC_ALL=C for byte-wise, predictable matching in CI.