Text Processing for Network Engineers Part 2: grep and Searching Configs and Logs at Scale
Part 1 covered the pattern language. This part is grep itself, past the handful of flags most people stop at.
The flags everyone knows, briefly
-i (case-insensitive), -v (invert match), -c (count matches instead of printing them), -n (line numbers), -r/-R (recursive). All essential, all covered elsewhere. The flags that actually change how you work with configs and logs are the ones below.
Context: -A, -B, -C
A single matching line is rarely the whole story in a device config. If you grep for an IP address, you almost always want to see which interface it’s configured on, and that line is above the match:
grep -B2 '10.10.10.1' running-config.txt
-B2 prints two lines before each match, -A2 prints two lines after, -C2 prints two lines on both sides. For pulling a whole interface stanza out of an IOS-style config when you only know one line inside it, -A with a generous count and a bit of luck is often faster than writing a proper multi-line parser:
grep -A8 'interface GigabitEthernet0/1' running-config.txt
This is context, not structure, grep doesn’t know where the stanza ends, it just prints N lines and stops. For anything where you need the actual block boundaries (start at interface, stop at the next ! or the next interface), that’s a job for sed or awk (Parts 3 and 4), not grep’s fixed-count context flags. Know where the line is: grep’s job. Know where the block ends: not grep’s job.
Extraction: -o
By default grep prints the whole matching line. -o prints only the matched text, one match per output line. This turns grep from “find the line” into “extract the value”:
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' interfaces.conf
Run against a config file, that prints every IP-shaped string, one per line, with no surrounding context, ready to pipe into sort -u for a deduplicated list, or into wc -l for a count. Combine it with a capture-adjacent pattern and it becomes a crude field extractor:
grep -oE 'vlan[0-9]+' switchport-configs.txt | sort -u
That’s every distinct VLAN interface name referenced across a config, with no manual scanning. -o is the flag that turns grep from a search tool into the first stage of a data pipeline, which matters once Part 4’s awk enters the picture: grep narrows, awk shapes.
-P: PCRE mode and lookarounds
grep -P unlocks Perl-compatible regex, which means lookaheads and lookbehinds: match a position based on what’s next to the pattern without including that neighbor in the match itself.
A genuinely useful one: find every ACL line that is missing a trailing log keyword, without matching lines where log legitimately appears elsewhere:
grep -P 'permit(?!.*\blog\b)' acl-audit.txt
(?!.*\blog\b) is a negative lookahead, “not followed anywhere on this line by the word log.” That’s a genuinely different capability from grep -v, which excludes whole lines matching a pattern. Here we want lines that do match permit but don’t also contain log, which needs the two conditions combined on the same line, and that’s a lookahead’s job, not an invert flag’s.
-P is GNU grep only (not available on BSD/macOS grep, and not inside sed or awk at all), so treat it as a power tool for interactive investigation, not something to bake into a portable script that has to run on a vendor appliance’s shell.
Recursive search across a fleet of config backups
This is where grep becomes an audit tool rather than a search box. Assume a directory of nightly config pulls, one file per device, named by hostname:
configs/
core-sw01.cfg
core-sw02.cfg
branch-fw12.cfg
branch-fw13.cfg
...
A RADIUS server gets decommissioned. Before you pull the plug, you need every device still pointing at it:
grep -rl '10.50.0.15' configs/
-r walks the directory, -l prints only the filenames that contain a match, not the matching lines themselves, which is exactly what you want for a “which boxes are affected” list rather than a “show me every occurrence” dump. Flip -l to -L and you get the inverse: every file that does not reference that server, useful for confirming the migration actually finished everywhere.
grep -rc 'aaa group server radius OLD-RADIUS' configs/
-c recursively gives you a per-file count, filename:N, so you can see at a glance which devices have multiple stale references (a device with the group defined and referenced under three separate line-vty stanzas needs more cleanup than one with a single dangling reference).
Restrict the walk with --include when the directory has mixed file types:
grep -rl --include='*.cfg' '10.50.0.15' configs/
Fixed strings: -F
If the thing you’re searching for has regex metacharacters in it that you don’t want interpreted, like an IP address with literal dots, or a bracketed string, -F (or the fgrep alias) treats the pattern as a literal string, not a regex. It’s a small flag that avoids a specific class of self-inflicted bug: dots in an IP address are regex “any character” unless escaped, so an un-escaped grep '10.50.0.15' will also match 10x50x0x15 or 1050015 with any single character standing in for each dot. It rarely matters for something as constrained as an IP address, but for arbitrary strings with brackets, parens, or asterisks in them, -F is the difference between “search for this text” and “search for this regex that happens to look like this text.”
The pattern across all of this: grep answers “where,” not “what’s inside.” It finds lines, files, or matched substrings. Reshaping what it finds into a report, a table, or a calculation is the next tool’s job.