TechEarl

How to Show Lines Before and After a grep Match (Context)

grep -C 3 'pattern' file prints the matching line plus 3 lines on each side. The three context flags (-A after, -B before, -C both), how the -- group separator works between match blocks, asymmetric context, recursive context search, and the macOS BSD vs GNU differences that bite.

Ishan KarunaratneIshan Karunaratne⏱️ 11 min readUpdated
Use grep -C 3 'pattern' file to print 3 lines before and after each match. The -A, -B, -C context flags, the -- group separator, asymmetric context, recursive search, and BSD vs GNU grep differences.

A bare grep prints only the matching line. Most of the time that is not enough: an error line means little without the stack frame above it, and a config key means little without the block it sits in. grep solves this with three context flags. grep -C 3 'pattern' file prints each matching line plus 3 lines on either side, which is the one command I reach for during log triage.

The three flags are small but each does one thing: -A N shows N lines After the match, -B N shows N lines Before, and -C N shows N lines of Context on both sides. The count argument is mandatory. Forget it and grep reads the next token as the count, which is the single most common mistake with these flags.

Set your values

Try it with your own values

Set your OS, the file to search, and the pattern. Every grep example below updates with your values.

The one-liner

bash· Linux (GNU)
grep -C 3 ':pattern' :search_path

That prints every line containing the pattern, with the 3 lines before and the 3 lines after each one. On PowerShell, Select-String -Context takes a before,after pair, so -Context 3,3 is the equivalent of grep -C 3.

The three context flags

-A, -B, and -C are the whole feature. Each takes a numeric count.

FlagLong form (GNU)Meaning
-A N--after-context=NThe matching line plus N lines after it
-B N--before-context=NN lines before plus the matching line
-C N--context=NN lines before, the match, N lines after

-C N is exactly -A N -B N with the same count on both sides. The short forms work on both GNU and BSD grep. The long forms are GNU only, so a script that needs to run on macOS should stick to -A, -B, -C.

After context, the matching line itself is included in every case. -A 3 gives you 4 lines total per match (the line plus 3), not 3.

Lines after the match

-A is the one I use most for logs, because the interesting detail in a stack trace is usually below the line that names the exception.

bash· Linux (GNU)
grep -A 10 ':pattern' :search_path

Ten lines after the match is usually enough to capture a Java or Python stack trace in one block.

Lines before the match

-B is the inverse. Reach for it when the match is a symptom and the cause is above it: a "connection refused" line whose preceding lines show the failed handshake.

bash· Linux (GNU)
grep -B 5 ':pattern' :search_path

Asymmetric context

-C is symmetric, but you rarely want the same count on both sides. Pass -A and -B together with different counts when the interesting context is lopsided. A config block, for example, usually starts a couple of lines above the key and runs well below it:

bash· Linux (GNU)
grep -A 5 -B 1 ':pattern' :search_path

That prints 1 line before and 5 lines after. When you mix -A, -B, and -C in one invocation, grep does not combine them: -C overrides whatever -A and -B you also passed. So grep -A 5 -B 1 -C 3 behaves as -C 3 and silently discards the -A 5 -B 1. Drop -C entirely when you want asymmetry.

Context plus line numbers

Add -n so each printed line carries its line number. This turns a context block into something you can jump to directly in an editor, and it makes clear where the matching line sits inside the block.

bash· Linux (GNU)
grep -n -C 3 ':pattern' :search_path

With -n, GNU grep prints the matching line with a colon after the number and the context lines with a hyphen after the number. So 42: is a match and 41- is a context line. That punctuation difference is the fastest way to scan a long output and find the actual hits.

The group separator between blocks

When two matches are far apart, grep prints each context block separately and divides them with a line containing two hyphens. That separator line is literally the string written as two ASCII hyphen characters, printed on its own line between non-adjacent groups. If two matches are close enough that their context windows overlap or touch, grep merges them into one block and prints no separator.

GNU grep lets you customize that divider. --group-separator=STRING swaps the two-hyphen line for any string you choose, and --no-group-separator removes it entirely.

bash· Linux (GNU)
grep --no-group-separator -C 2 ':pattern' :search_path

--group-separator and --no-group-separator are GNU-only. BSD grep on macOS always prints the two-hyphen line and gives you no way to suppress it, which matters when a script downstream parses grep output (see the mistakes section below).

Context flags compose with -r, so you can pull a context window out of every file in a tree at once. Add -n to keep the output navigable, since recursive output also carries the filename.

bash· Linux (GNU)
grep -rn -C 3 ':pattern' .

With recursive context, GNU grep prints the group separator both between matches in the same file and between files. Each match line is prefixed file:line: and each context line file-line-, so the colon-versus-hyphen rule still tells you which lines actually matched.

Why context matters for log forensics

The reason these flags exist is that a log line is rarely self-contained. Two cases come up constantly.

A stack trace is a block: the line naming the exception, then a dozen frames below it. Grepping for Exception with no context gives you the headline and throws away the diagnosis. grep -A 20 'Exception' app.log keeps the whole trace.

A config block is also a block: the line you searched for sits inside a stanza that only makes sense with its neighbours. Grepping an nginx config for proxy_pass with -A 3 -B 3 shows you the surrounding location block, not just the one directive.

In both cases the match is a pointer and the context is the payload. That is why -C is the default move for any "what happened around this error" question.

macOS BSD grep vs GNU grep

macOS ships BSD grep. The context flags mostly behave the same, but the separator handling diverges.

FeatureGNU grepBSD grep (macOS default)
-A N, -B N, -C NSupportedSupported
--after-context, --before-context, --context long formsSupportedNot supported
Two-hyphen group separator between blocksPrintedPrinted
--group-separator=STRINGSupportedNot supported
--no-group-separatorSupportedNot supported
Merging overlapping context windowsMerged, no separatorMerged, no separator

If you need to customize or suppress the separator on macOS, brew install grep gives you GNU grep as ggrep. For the short flags -A, -B, -C alone, the system BSD grep is fine and portable.

Common mistakes

1. Forgetting the count argument. grep -A 'pattern' file makes grep read pattern as the count for -A, fail to parse it as a number, and error out or treat your real file as the pattern. The count is mandatory and must come right after the flag: grep -A 3 'pattern' file.

2. Expecting -C to add to -A and -B. When -C appears alongside -A or -B, -C wins and the others are discarded. grep -A 10 -C 2 is just -C 2. For asymmetric context, use -A and -B only and leave -C out.

3. Scripts choking on the separator. The two-hyphen line that grep prints between blocks is not a match and not a context line. A script that does grep -A 2 'x' file | wc -l or pipes context output into a parser will count or mis-handle those separator lines. Strip them with --no-group-separator on GNU grep, or filter the line out with a second grep on BSD.

4. Assuming context lines are matches. Every line in a context block prints, including the surrounding non-matching lines. If you then pipe that into another grep or a counter, you are working with the whole block, not just the hits. Use -n and key off the colon-versus-hyphen punctuation, or count with grep -c separately.

5. Huge counts on huge files. grep -C 5000 'pattern' big.log will happily print thousands of lines per match. If the count is large enough that you want the whole region, you probably want a pager instead.

When NOT to use context flags

Context flags are for pulling a bounded window around scattered matches. Two cases call for a different tool.

When you actually want the whole file or a large contiguous region, do not crank -C up to a huge number. Open the file in a pager (less app.log, then /pattern to search and n to step through matches) so you can scroll freely in both directions without grep deciding the boundaries for you.

When you want structured extraction, not a visual window, reach for awk. If the task is "print every line from the match until a blank line" or "pull field 3 of the line two rows below each match", that is range-pattern and record logic, which awk expresses directly. grep context is a fixed line count; awk handles data-dependent boundaries.

See also

FAQ

TagsgrepCLILinuxmacOSBSDLog AnalysisShell Scripting
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts

Connect to an AWS EC2 instance using plain SSH with a key pair, EC2 Instance Connect, AWS Systems Manager Session Manager, or an EC2 Instance Connect Endpoint for private instances. Default usernames, security group rules, and troubleshooting Permission denied and Connection timed out.

How to SSH into an AWS EC2 Instance

Connect to an EC2 instance four ways: plain SSH with a key pair, EC2 Instance Connect, Session Manager, and EC2 Instance Connect Endpoint. Default usernames, security group rules, and the troubleshooting matrix that fixes Permission denied and Connection timed out.

Using regex in Apache .htaccess with mod_rewrite: RewriteRule and RewriteCond pattern syntax, rewrite flags, and copy-paste rules for HTTPS redirects, www normalization, trailing slashes, 301 redirects, clean URLs, and blocking by user-agent or IP.

How to Use Regex in .htaccess (Apache mod_rewrite)

Use regex in .htaccess with Apache mod_rewrite: how RewriteRule and RewriteCond patterns work, the per-directory quirk that breaks everyone, and copy-paste rules for HTTPS, www, trailing slashes, 301s, clean URLs, and access blocking.

Use find -size +100M to list files larger than 100 megabytes. Unit suffixes (c/k/M/G), +/- sign convention, combine with sort -rn to surface the biggest files on disk, and BSD vs GNU rendering differences.

How to Find Files Larger Than a Size with find -size

find . -size +100M lists every file larger than 100 megabytes. The unit suffixes (c, k, M, G), the +/- sign convention, how to combine with sort to find the biggest files on disk, the BSD vs GNU divergence for printing sizes, and the wc -c trick for byte-exact thresholds.