Grep 2 Lines using `grep` Command in Linux
Matching consecutive lines with grep
grep is primarily line-based, which makes matching patterns that span multiple lines seem impossible at first. However, using the right options, you can search for consecutive lines as a single block.
The problem
Let’s say you need to verify that ~/.bashrc contains these two consecutive lines (not at the beginning of the file):
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$PATH
Both lines must exist and be consecutive — neither before nor after any other lines between them.
Why standard grep doesn’t work
Standard grep processes input line-by-line, treating newlines as line separators. This means the default behavior can’t match across line boundaries. You need to change how grep interprets the input.
The solution
Two options work together:
-z: Treat input as null-byte-terminated instead of newline-terminated. This allowsgrepto read the entire file as a single string.-P: Use Perl-compatible regular expressions (PCRE), which properly handle\nas a literal newline character in the pattern.
Here’s the command:
grep -Pz 'export GOPATH=\$HOME/go\nexport PATH=\$GOPATH/bin:\$PATH' ~/.bashrc
Breaking this down:
\nmatches the newline between the two lines\$escapes the dollar signs since$is special in PCRE- The pattern doesn’t require leading/trailing newlines —
grepfinds it anywhere in the file
To check whether the pattern exists and return a zero exit code:
grep -Pzq 'export GOPATH=\$HOME/go\nexport PATH=\$GOPATH/bin:\$PATH' ~/.bashrc && echo "Found" || echo "Not found"
The -q flag suppresses output and is useful in scripts.
Practical examples
Check for a specific comment followed by a function in a shell script:
grep -Pz '# Initialize database\nfunction init_db' setup.sh
Find two consecutive error patterns in a log file:
grep -Pz 'Connection timeout\nFailed to reconnect' app.log
Match lines with variable content between them:
grep -Pz 'server:\s+\{[^}]*"enabled":\s*true' config.json
The pattern [^}]* matches any characters except the closing brace, allowing flexibility in what appears between the opening brace and the “enabled” field.
Important caveats
- Large files: Using
-zreads the entire file into memory as a single string. For multi-gigabyte files, this can cause memory issues. Useheadortailto limit input if needed. - Null bytes: If your file actually contains null bytes,
-zwill break on them. - PCRE support: Some minimal
grepimplementations (like busybox grep) don’t support-P. GNU grep does.
Check your grep version:
grep --version
Alternative approaches
For complex multiline matching, consider pcregrep (part of the PCRE toolkit), which handles this more naturally:
pcregrep -M 'export GOPATH=\$HOME/go\nexport PATH=\$GOPATH/bin:\$PATH' ~/.bashrc
Or use awk for more control:
awk '/export GOPATH=\$HOME\/go/ { getline; if (/export PATH=\$GOPATH\/bin:\$PATH/) print "Found" }' ~/.bashrc
The awk approach reads the next line after matching the first pattern and checks if it matches the second. This avoids loading the entire file and works on systems with older grep versions.