grep and Regular Expressions
One objective covering both grep's options and regular expressions. The trap worth the guide is that grep has two dialects: in basic expressions `{`, `+` and `?` are literal characters and need escaping, and in extended expressions they are operators. The same pattern silently means different things.
Essential Tools Guide 6 of 67 Beginner
- OSRHEL 10.0 (Coughlan)
- Kernel6.12.0-55.9.1.el10_0
- dnf4.20.0
- Flatpak1.16.0
- TimeAbout 13 min
- Reviewed23 August 2026
Written against the versions above. `egrep` still works on RHEL 10 but prints a deprecation warning on some builds and is no longer the documented spelling. Use `grep -E`.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| RHCSA-A01 | 192.168.0.31 | RHEL 10.0 (Coughlan) | Practice node (graded) - spare /dev/sda | 2 Core | 4 GB | 50 GB + 15 GB |
Before you start
- A shell.
- Two scratch files under
/tmp, removed at the end.
-
Finding lines, counting them, and inverting
root:x:0:0:Super User:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologinNote the second line -
grep rootmatched it because/rootcontains the string. grep matches anywhere on the line unless you tell it otherwise.The options that earn their keep:
| Option | Does | |---|---| |
-n| prefix the line number | |-c| count matching lines, print no text | |-v| invert: lines that do NOT match | |-i| ignore case | |-w| match whole words only |grep -c ""counting every line andgrep -vc nologincounting the rest is the pattern worth remembering:-vplus-canswers "how many are not" in one command.bash Example session grep root /etc/passwdroot:x:0:0:Super User:/root:/bin/bashoperator:x:11:0:operator:/root:/usr/sbin/nologingrep -n bash /etc/passwd | head -41:root:x:0:0:Super User:/root:/bin/bash39:sysadmin:x:1000:1000:System Admin:/home/sysadmin:/bin/bashgrep -c "" /etc/passwd; echo "--- lines that are NOT nologin:"; grep -vc nologin /etc/passwd41--- lines that are NOT nologin:5grep -i RooT /etc/passwd | head -2root:x:0:0:Super User:/root:/bin/bashoperator:x:11:0:operator:/root:/usr/sbin/nologinExpected resultMatches anywhere on the line, counted and inverted.
Success conditionYou can answer "how many" and "which not" without a pipeline.
-
Whole words, and context
mail:x:8:12:mail:/var/spool/mail:/sbin/nologinversus:
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin systemd-oom:x:...-wrequires a word boundary either side, somailno longer matches insidespool/mailon other lines. On/etc/passwd, searching a short username without-wmatches every path that happens to contain it.-B1 -A1prints one line either side of the match, and-C1does both. On a log file that is usually what you want: the error line alone rarely explains itself, and the line before it often does.bash Example session grep -w mail /etc/passwdmail:x:8:12:mail:/var/spool/mail:/usr/sbin/nologingrep mail /etc/passwdmail:x:8:12:mail:/var/spool/mail:/usr/sbin/nologingrep -B1 -A1 "^sync" /etc/passwdlp:x:4:7:lp:/var/spool/lpd:/usr/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdownExpected resultA narrowed match, then surrounding context.
Success conditionYou can stop a short search term matching half the file.
-
Anchors and character classes
root rpc^anchors to the start of the line,$to the end:sync root sysadminbash$finds accounts with a login shell, which is a genuinely useful query and a common exam task.Brackets are a set of alternatives for one character:
adm bin chronyand a leading
^inside the brackets negates the set:bin daemon adm lpThat is two different meanings for
^in one syntax: outside brackets it anchors, inside them it negates.[^r]is "any character that is not r", and^[^r]is "lines whose first character is not r".bash Example session grep "^r" /etc/passwd | cut -d: -f1rootrtkitrpcrpcusergrep "bash$" /etc/passwd | cut -d: -f1rootsysadmingrep "^[abc]" /etc/passwd | cut -d: -f1binadmavahicleviscolordchronygrep "^[^r]" /etc/passwd | cut -d: -f1 | head -4bindaemonadmlpExpected resultAnchored matches, a character set, and a negated set.
Success conditionYou can anchor a pattern and express "one of these".
-
The dialect trap
Three lines -
aa,aaa,aaaa- and a pattern asking for three a's:grep "a\{3\}" aaa aaaaBackslashed braces, and it works. Now the same thing written the way it looks like it should be:
grep "a{3}" exit=1 - basic grep read the braces literallyNo matches, and no error. Plain
grepuses Basic Regular Expressions, in which{,},+and?are ordinary characters. It searched for a literala{3}and correctly found nothing.That silence is what makes this expensive: a pattern that is wrong does not complain, it just returns nothing, and you conclude the data is absent.
-Eswitches to Extended Regular Expressions, where they are operators:grep -E "a{3}" aaa aaaaSame with
?:grep -E "colou?r" -> color, colour grep "colou\?r" -> color, colourBoth work; one needs the backslash and one does not. Use
grep -Eand write the pattern the normal way.bash Example session printf 'aa\naaa\naaaa\n' > /tmp/g.txt; grep "a\{3\}" /tmp/g.txtaaaaaaagrep "a{3}" /tmp/g.txt; echo "exit=$? - basic grep read the braces literally"exit=1 - basic grep read the braces literallygrep -E "a{3}" /tmp/g.txtaaaaaaaprintf 'color\ncolour\n' > /tmp/g2.txt; grep -E "colou?r" /tmp/g2.txt; echo "--- and in basic grep:"; grep "colou\?r" /tmp/g2.txtcolorcolour--- and in basic grep:colorcolourExpected resultThe same quantifier silently ignored, then honoured.
Success conditionYou will reach for
-Ebefore debugging a pattern that returns nothing. -
Searching trees, and extracting rather than matching
/etc/ssh/sshd_config:#PermitRootLogin prohibit-password-rrecurses a directory, and prefixes each hit with its file. On a task like "find which config sets X", that is the whole answer in one command.-lprints only the filenames, which is what you want when the matching line is long or there are many:/etc/dnf/dnf.conf /etc/resolv.conf-oprints only the matched part rather than the whole line:root bin daemon adm lpgrep -oE "^[a-z_]+"extracts every username withoutcut,awkor a field separator. Between-o,-land-c, most "list the X that Y" questions are one grep rather than a pipeline.bash Example session sudo grep -r "PermitRootLogin" /etc/ssh/ 2>/dev/null | head -4/etc/ssh/sshd_config:#PermitRootLogin prohibit-password/etc/ssh/sshd_config:# the setting of "PermitRootLogin prohibit-password".grep -oE "^[a-z_]+" /etc/passwd | head -5rootbindaemonadmlpgrep -rl "nameserver" /etc/ 2>/dev/null | head -3/etc/resolv.conf/etc/services/etc/selinux/targeted/contexts/files/file_contextsExpected resultA recursive search, extracted matches, and a filename-only list.
Success conditionYou can find which file sets a setting, and pull values out of it.
Troubleshooting
A pattern with
+,?or{}returns nothing.Why: Basic grep treats them as literal characters.
Fix:
grep -E, or backslash them.A short search term matches far too much.
Why: grep matches anywhere on the line.
Fix:
-wfor whole words, or anchor with^and$.grep -rfloods the terminal with permission errors.Why: Directories your user cannot read.
Fix:
2>/dev/null, or run it with sudo.The shell expanded the pattern before grep saw it.
Why: Unquoted
*,?or[are globs.Fix:Single-quote every pattern.