How to Make Grep Print the Lines Below and Above Each Matching Line

How can I make grep print the lines below and above each matching line?

grep's -A 1 option will give you one line after; -B 1 will give you one line before; and -C 1 combines both to give you one line both before and after, -1 does the same.

How to grep for lines above and below a certain pattern

Use grep with the parameters -A and -B to indicate the number a of lines After and Before you want to print around your pattern:

grep -A1 -B1 yourpattern file
  • An stands for n lines "after" the match.
  • Bm stands for m lines "before" the match.

If both numbers are the same, just use -C:

grep -C1 yourpattern file

Test

$ cat file
Foo line
Bar line
Baz line
hello
bye
hello
Foo1 line
Bar line
Baz1 line

Let's grep:

$ grep -A1 -B1 Bar file
Foo line
Bar line
Baz line
--
Foo1 line
Bar line
Baz1 line

To get rid of the group separator, you can use --no-group-separator:

$ grep --no-group-separator -A1 -B1 Bar file
Foo line
Bar line
Baz line
Foo1 line
Bar line
Baz1 line

From man grep:

   -A NUM, --after-context=NUM
Print NUM lines of trailing context after matching lines.
Places a line containing a group separator (--) between
contiguous groups of matches. With the -o or --only-matching
option, this has no effect and a warning is given.

-B NUM, --before-context=NUM
Print NUM lines of leading context before matching lines.
Places a line containing a group separator (--) between
contiguous groups of matches. With the -o or --only-matching
option, this has no effect and a warning is given.

-C NUM, -NUM, --context=NUM
Print NUM lines of output context. Places a line containing a
group separator (--) between contiguous groups of matches. With
the -o or --only-matching option, this has no effect and a
warning is given.

grep: show lines surrounding each match

For BSD or GNU grep you can use -B num to set how many lines before the match and -A num for the number of lines after the match.

grep -B 3 -A 2 foo README.txt

If you want the same number of lines before and after you can use -C num.

grep -C 3 foo README.txt

This will show 3 lines before and 3 lines after.

How do I fetch lines before/after the grep result in bash?

You can use the -B and -A to print lines before and after the match.

grep -i -B 10 'error' data

Will print the 10 lines before the match, including the matching line itself.

How to print both the grep pattern and the resulting matched line on the same line?

For the updated sample input and full-matching requirement and assuming you never have any regexp metacharacters in file1 and that the matching strings in file2 are never at the start or end of the line:

$ awk 'NR==FNR{strs[$0]; next} {for (str in strs) if ($0 ~ ("[^[:alnum:]]"str"[^[:alnum:]]")) print $0, str}' file1 file2
XP_033390445.1_uncharacterized_protein_BU24DRAFT_430534_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_430534
XP_033390442.1_uncharacterized_protein_BU24DRAFT_488391_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_488391
XP_033390437.1_uncharacterized_protein_BU24DRAFT_488386_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_488386
XP_033390400.1_uncharacterized_protein_BU24DRAFT_417707_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_417707
XP_033390397.1_uncharacterized_protein_BU24DRAFT_417704_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_417704
XP_033390371.1_uncharacterized_protein_BU24DRAFT_488335_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_488335
XP_033376581.1_uncharacterized_protein_BU24DRAFT_429509_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_429509
XP_033376580.1_uncharacterized_protein_BU24DRAFT_210092_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_210092
XP_033376578.1_uncharacterized_protein_BU24DRAFT_229465,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_229465
XP_033376577.1_uncharacterized_protein_BU24DRAFT_498094,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_498094
XP_033376576.1_uncharacterized_protein_BU24DRAFT_416051,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_416051
XP_033376575.1_uncharacterized_protein_BU24DRAFT_482795,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_482795

Original answer doing partial matching:

The correct approach is 1 call to awk:

$ awk 'NR==FNR{strs[$0]; next} {for (str in strs) if (index($0,str)) print $0, str}' file1 file2
XP_033376575.1_uncharacterized_protein_BU24DRAFT_482795,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_482795
XP_033376576.1_uncharacterized_protein_BU24DRAFT_416051,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_416051
XP_033376577.1_uncharacterized_protein_BU24DRAFT_498094,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_498094
XP_033376578.1_uncharacterized_protein_BU24DRAFT_229465,_partial_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_229465
XP_033376580.1_uncharacterized_protein_BU24DRAFT_210092_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_210092
XP_033376581.1_uncharacterized_protein_BU24DRAFT_429509_Aaosphaeria_arxii_CBS_175.79 BU24DRAFT_429509

See https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice and https://mywiki.wooledge.org/Quotes for some of the issues with the script in your question.

how to show n lines above and below my grep search?

With -C you get the context around the match:

grep -C4 -i authorization /etc/kubernetes/manifests/kube-apiserver.yaml

From grep man page:

   Context Line Control
-A NUM, --after-context=NUM
Print NUM lines of trailing context after matching lines. Places a line containing a group separator (described under --group-separator) between contiguous groups of matches.
With the -o or --only-matching option, this has no effect and a warning is given.

-B NUM, --before-context=NUM
Print NUM lines of leading context before matching lines. Places a line containing a group separator (described under --group-separator) between contiguous groups of matches.
With the -o or --only-matching option, this has no effect and a warning is given.

-C NUM, -NUM, --context=NUM
Print NUM lines of output context. Places a line containing a group separator (described under --group-separator) between contiguous groups of matches. With the -o or
--only-matching option, this has no effect and a warning is given.

How can I make 'grep' show a single line five lines above the grepped line?

OK, I think this will do what you're looking for. It will look for a pattern, and extract the 5th line before each match.

grep -B5 "pattern" filename | awk -F '\n' 'ln ~ /^$/ { ln = "matched"; print $1 } $1 ~ /^--$/ { ln = "" }'

basically how this works is it takes the first line, prints it, and then waits until it sees ^--$ (the match separator used by grep), and starts again.

Print line after the match in grep

If you want to use grep as the tool for this, you can achieve it by adding another segment to your pipeline:

cmus-remote -Q | grep -A 1 "tag genre" | grep -v "tag genre"

This will fail in cases where the string you're searching for is on two lines in a row. You'll have to define what behaviour you want in that case if we're going to program something sensible for it.

Another possibility would be to use a tool like awk, which allows for greater compexity in the line selection:

cmus-remote -Q | awk '/tag genre/ { getline; print }'

This searches for the string, then gets the next line, then prints it.

Another possibility would be to do this in bash alone:

while read line; do
[[ $line =~ tag\ genre ]] && read line && echo "$line"
done < <(cmus-remote -Q)

This implements the same functionality as the awk script, only using no external tools at all. It's likely slower than the awk script.

grep lines matching a pattern, and the lines before and after the matching until different pattern

With gawk, which supports multi char RS:

gawk 'BEGIN{RS=ORS="End_pattern"}/ef/' file

Output:

Start_pattern
abc
d
ef
ghij
klm
no End_pattern
Start_pattern
abc
def
hij End_pattern

Explanation:

# Split records based on the End_pattern
BEGIN{RS=ORS="End_pattern"}

# Print records that contain the search term
/ef/

Btw, for cosmetic reasons you might want to append a newline at the end out the output:

gawk 'BEGIN{RS=ORS="End_pattern"}/ef/;END{printf "\n"}' file

PS: While the above solution works with gawk only, it is also possible to achieve that with a simple awk script which is compatible to POSIX, meaning it works with any awk:

awk '{b=b$0"\n"}/End_pattern/{if(b~/ef/){printf "%s",b};b=""}' file

Explanation:

# Append the current line plus a newline to b(uffer)
{b=b$0"\n"}

# Once End_pattern is found ...
/End_pattern/{
# Check if the buffer contains the search term
if(b~/ef/){
# Print the buffer when the term was found
printf "%s",b
}
# Clear the buffer
b=""
}

awk '{b=b$0"\n"}/End_pattern/{if(b~/ef/){printf "%s",b};b=""}' file

How do you grep a file and get the next 5 lines

You want:

grep -A 5 '19:55' file

From man grep:

Context Line Control

-A NUM, --after-context=NUM

Print NUM lines of trailing context after matching lines.
Places a line containing a gup separator (described under --group-separator)
between contiguous groups of matches. With the -o or --only-matching
option, this has no effect and a warning is given.

-B NUM, --before-context=NUM

Print NUM lines of leading context before matching lines.
Places a line containing a group separator (described under --group-separator)
between contiguous groups of matches. With the -o or --only-matching
option, this has no effect and a warning is given.

-C NUM, -NUM, --context=NUM

Print NUM lines of output context. Places a line containing a group separator
(described under --group-separator) between contiguous groups of matches.
With the -o or --only-matching option, this has no effect and a warning
is given.

--group-separator=SEP

Use SEP as a group separator. By default SEP is double hyphen (--).

--no-group-separator

Use empty string as a group separator.


Related Topics



Leave a reply



Submit