|

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 allows grep to read the entire file as a single string.
  • -P: Use Perl-compatible regular expressions (PCRE), which properly handle \n as 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:

  • \n matches the newline between the two lines
  • \$ escapes the dollar signs since $ is special in PCRE
  • The pattern doesn’t require leading/trailing newlines — grep finds 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 -z reads the entire file into memory as a single string. For multi-gigabyte files, this can cause memory issues. Use head or tail to limit input if needed.
  • Null bytes: If your file actually contains null bytes, -z will break on them.
  • PCRE support: Some minimal grep implementations (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.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *