1. Overview

Searching for text in files is one of the most frequent use cases while working in Linux. Moreover, the default path of searching is usually from start to end. However, we may want to search a file from end to beginning for some use cases.

One such use case is when we want to search for the last occurrence of a pattern in a large text file. Naturally, performing a reverse search from bottom to top will be much faster in such a scenario.

In this tutorial, we’ll explore multiple approaches to search from the end of a file to the beginning.

2. Scenario Setup

Let’s start by taking a look at the temperature.txt file that contains temperature data over multiple dates:

$ cat temperature.txt
2023/12/01 -4°C
2022/12/02  2°C
2022/12/03  3°C
2022/12/04 -1°C
2022/12/05 -3°C
2022/12/06 -3°C
2022/12/07  3°C
2022/12/08  5°C

Further, our objective is to search for the most recent date when the temperature dropped below 0°C.

We’ll reuse this text file to solve our use case through different approaches. So, let’s get started.

3. Using the tail and grep Commands

In this section, we’ll use the grep command to solve our use case. Additionally, we’ll use the tail command to support grep.

Let’s start by searching for negative temperatures throughout the entire file by using the grep command:

$ grep '-' temperature.txt
2023/12/01 -4°C
2022/12/04 -1°C
2022/12/05 -3°C
2022/12/06 -3°C

Since we only want the most recent date on which the temperature was negative, we can use the tail command to get the last result:

$ grep '-' temperature.txt | tail -1
2022/12/06 -3°C

Great! Our approach gives us the correct result by giving us the first match from the end of the file. However, we must note that the direction of search in this approach is still from start to end.

4. Using the tac and grep Commands

In this section, we’ll get our search results from the end of the file while keeping the search direction from the end to the beginning of the file. For this purpose, we’ll use the tac command with a few helper commands, such as grep and head.

4.1. With head

First, let’s start by using the tac and grep commands to search through all the records in the temperature.txt file that have negative temperatures:

$ tac temperature.txt | grep '-'
2022/12/06 -3°C
2022/12/05 -3°C
2022/12/04 -1°C
2023/12/01 -4°C

We must note the effect of using tac wherein the order of file content is reversed.

Now, we can use the head command to restrict the output to the first result:

$ tac temperature.txt | grep '-' | head -1
2022/12/06 -3°C

Perfect! It works as expected.

4.2. With the -m1 Option

In our use case, we’re interested in a single search result from the end of the file. However, our solution had an intermediate step where it searched across all the results and then limited the output to a single result.

So, let’s optimize our solution by using the -m option to stop the grep command from searching any further once we’ve got our result:

$ tac temperature.txt | grep -m1 '-'
2022/12/06 -3°C

Fantastic! Our code is not only concise but also more performant now.

5. Using the awk Command

Awk is one of the most popular text-processing utilities in Linux. So, let’s see how we can use it to solve our use case.

5.1. With tail or tac

Let’s begin by using the awk command to search for the negative temperatures in the temperature.txt file:

$ awk '/-/{print}' temperature.txt
2023/12/01 -4°C
2022/12/04 -1°C
2022/12/05 -3°C
2022/12/06 -3°C

We must note that we’ve used the print command to show the entire record matching the negative temperature search pattern.

Now, let’s use the tail command to restrict the output to the last matching record:

$ awk '/-/{print}' temperature.txt | tail -1
2022/12/06 -3°C

The result looks correct.

Alternatively, we can first reverse the input using tac, then pass the reversed input to awk:

$ tac temperature.txt | awk '/-/{print; exit}'
2022/12/06 -3°C

It’s worth noting that we called the exit command after printing the first match to skip processing further lines.

5.2. With the END Block

Awk is a self-sufficient text-processing language. So, we don’t need the tail command in conjunction with awk to solve our use case.

Let’s rewrite our awk script by adding the END block so that the print statement executes precisely once at the end of the file:

$ awk '/-/ {
    last_match=$0;
} 
END {
    print last_match
}' temperature.txt
2022/12/06 -3°C

We’ve used the last_match variable to keep track of the last match of the search pattern. Eventually, it holds the value of the first match from the end of the file.

Lastly, it’s worth noting that our search direction is from beginning to end. So, it’s functionally correct but not a performant solution.

6. Using the sed Command

sed is another well-known and powerful text manipulation utility in Linux. In this section, we’ll use sed to solve our use case with multiple strategies.

6.1. With tail

Let’s write a one-liner sed command to search through all the negative temperatures in the temperature.txt file:

$ sed -n -e '/-/p' temperature.txt
2023/12/01 -4°C
2022/12/04 -1°C
2022/12/05 -3°C
2022/12/06 -3°C

By default, sed will output all the lines from the pattern space. So, we’ve used the -n option to suppress the unnecessary lines from the file. Further, we used the p command for the lines that match the pattern of negative temperature.

Like earlier, we can go ahead and use the tail command to restrict the output to a single result:

$ sed -n -e '/-/p' temperature.txt | tail -1
2022/12/06 -3°C

Our approach works fine.

6.2. With d Command

In our last approach, we had to use the -n option to intentionally suppress those lines that don’t have a negative temperature. Alternatively, we can use the d command to delete such lines and rely upon sed‘s default behavior to print the pattern space.

$ sed -e '/-/!d' temperature.txt
2023/12/01 -4°C
2022/12/04 -1°C
2022/12/05 -3°C
2022/12/06 -3°C

With this approach, the result looks the same.

Now, we can get the last record from this output by piping our sed script to another sed command that uses the p command to display the last line:

$ sed -e '/-/!d' temperature.txt | sed -n -e '$p'
2022/12/06 -3°C

It’s interesting to note that  ‘$’ represents the last line in the pattern space.

Alternatively, we can rewrite the second sed one-liner using the d command:

$ sed -e '/-/!d' temperature.txt | sed -e '$!d'
2022/12/06 -3°C

Interestingly, we didn’t have to use the -n option in this rewrite approach. Further, we used $! to delete (d) all lines that don’t represent the last line.

6.3. With Hold Space

So far, our approaches to solving the use case with sed kept the search direction from start to end. Now, let’s explore another approach that uses the hold space to reverse the content of the file:

$ sed -e '1!G;h;$!d;' temperature.txt
2022/12/08  5°C
2022/12/07  3°C
2022/12/06 -3°C
2022/12/05 -3°C
2022/12/04 -1°C
2022/12/03  3°C
2022/12/02  2°C
2023/12/01 -4°C

Now, let’s reuse learnings from our previous approaches to restrict the output to those records that contain negative temperatures only:

$ sed -e '1!G;h;$!d;' temperature.txt | sed -e '/-/!d'
2022/12/06 -3°C
2022/12/05 -3°C
2022/12/04 -1°C
2023/12/01 -4°C

Further, as we did earlier, we can limit the output to a single record:

$ sed -e '1!G;h;$!d;' temperature.txt \
| sed -e '/-/!d' \
| sed -n -e '1p'
2022/12/06 -3°C

Lastly, we can rewrite the last sed command to quit (q) as soon as we get at least one result:

$ sed -e '1!G;h;$!d;' temperature.txt \
| sed -e '/-/!d' \
| sed -e 'q'
2022/12/06 -3°C

Great! It looks like we’ve got this one right. However, we must note that the search direction is from start to end and is not recommended for large files.

7. Using the nl, sort, and cut Commands

Let’s start by reversing the contents of the file using the combination of nl, sort, and cut commands to reverse the contents of the file:

$ nl temperature.txt | sort -nr | cut -f 2-
2022/12/08  5°C
2022/12/07  3°C
2022/12/06 -3°C
2022/12/05 -3°C
2022/12/04 -1°C
2022/12/03  3°C
2022/12/02  2°C
2023/12/01 -4°C

Now, we can use the grep -m command to restrict our search to a single result:

$ nl temperature.txt | sort -nr | cut -f 2- | grep -m1 '-'
2022/12/06 -3°C

The output looks correct.

8. Using the Vim Editor

Quite often, our file might already be open in our editor. In this section, let’s learn how to solve our use case while working in the Vim Editor.

Firstly, we must note that we can do a reverse search in the normal mode using the ‘?’ command:

?{pattern}<Enter>

Next, let’s simulate our scenario by opening the file using the vim command:

$ vim temperature.txt

Continuing inside the Vim editor, we can perform the reverse search by pressing “?-” in the normal mode. Then, our cursor moves to the first match from the end of the file.

If we want to examine the line carefully, we can create a new buffer in Vim and yank that line to the new buffer. Of course, we can save the matched line in a file if required.

Finally, let’s demo how these steps are done in Vim:

 

9. Conclusion

In this article, we learned how to search from the end of a file to the beginning. Furthermore, we solved the use case with different utilities, such as grep, tac, sed, awk, and so on.

Lastly, we learned how to reverse search a pattern in a file open in the Vim editor.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.