1. Overview
In this tutorial, let’s look at how we can use grep to get information around a matched string.
2. Sample Text File
The grep filter searches a file for a particular pattern of characters and displays all lines that contain that pattern. We’ll use the grep command to search a string in a text file and then print the surrounding lines. Let’s create a file by redirecting the output of the ip addr show command to a file (ipinfo.txt):
$ ip addr show > ipinfo.txt
$ cat ipinfo.txt
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3. Displaying Lines Before a Match
While the grep command offers many options, some of the most interesting flags are A, B, and C. These options allow us to get the context around a matched string. The -A flag represents after and prints a specified number of lines after a matched string. The -B flag stands for before, and it prints the lines before a match. Lastly, we’ve got option -C which prints the lines both before and after the matched string.
Let’s run grep with the -B option:
$ grep '/24' -B2 ipinfo.txt
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
When grep finds a match, it returns the surrounding set of lines that we’ve specified.
4. Displaying Lines After a Match
We use the -A flag to display lines after a match and specify the number of lines we need after that match:
$ grep '/24' -A2 ipinfo.txt
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
We can even combine these commands to get a more effective output:
$ grep '/24' -A2 -B2 ipinfo.txt
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
5. Using the -C Option
Finally, we can use -C to print lines before and after a match. Let’s use it to achieve the same result as the previous example:
$ grep '/24' -C2 ipinfo.txt
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
6. Displaying the Entire Paragraph
In this section, we’ll learn how to use the grep and awk commands to search a string in a file and print the entire paragraph.
6.1. Scenario Setup
Let’s modify our existing ipinfo.txt sample file by adding an empty line between the two records:
$ cat ipinfo_v2.txt
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
valid_lft forever preferred_lft forever
We’ll use ipinfo_v2.txt as the input file for this scenario as its content is now in a paragraph format.
6.2. Using grep
In this scenario, each paragraph within our input file contains a fixed number of lines. So, if we know that our search string would belong to a specific line of the paragraph, then we can use an appropriate value with the -A and -B options and display the entire paragraph.
Let’s say we want to search the string “*enp0s3:*” which is likely to exist only on the first line of the paragraph. So, we can use -A5 with the grep command to show the remaining lines of the paragraph:
$ grep -A5 enp0s3: ipinfo_v2.txt
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
valid_lft forever preferred_lft forever
Great! It looks like we’ve got this one right.
6.3. Using awk as an Alternative
Although we could solve our use case with the grep command independently, the earlier approach won’t work if the paragraph size varies. So, in this section, we’ll learn a generic solution for our use case using awk as an alternative command-line utility.
Firstly, we must understand that awk is a better choice for this use case because it inherently supports input record separators. As a result, it becomes quite convenient to capture complete paragraphs in a single record.
Next, let’s see how we can use the awk command to read the ipinfo_v2.txt file such that each record is a single paragraph:
$ awk -vRS= 'END {print NR}' ipinfo_v2.txt
2
We must note that *with RS=, we’ve set the input record separator as blank lines*. Further, we read the entire file and displayed the last record number in the END block using the NR variable.
Finally, let’s go ahead and use this approach to search for the string enp0s3 and display the entire paragraph:
$ awk -vRS= '/enp0s3:/{print $0"\n"}' ipinfo_v2.txt
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:ea:a5:61 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic noprefixroute enp0s3
valid_lft 6826sec preferred_lft 6826sec
inet6 fe80::129e:c85e:f067:e115/64 scope link noprefixroute
valid_lft forever preferred_lft forever
It looks like we’ve got this one right. Additionally, let’s note that we’ve concatenated $0 with a newline character (\n) so that each paragraph that contains the search string is separated from other paragraphs with a blank line.
7. Conclusion
In this tutorial, we saw how the grep command makes our work easy, as we can filter and specify the number of lines around the match. We’ve discussed how to specify the number of lines before and after a match. We mainly use the A, B, and C options to get the lines around a match, and we can also use any or all of them in a single search.
We also learned how to use the grep and awk commands for solving the use case where we want to print the entire paragraph containing a search string.