99

What's the best way of getting only the final match of a regular expression in a file using grep?

Also, is it possible to begin grepping from the end of the file instead of the beginning and stop when it finds the first match?

5 Answers 5

147

You could try

grep pattern file | tail -1 

or

tac file | grep pattern | head -1 

or

tac file | grep -m1 pattern 
5
  • 31
    tac file | grep -m 1 pattern Commented Nov 2, 2010 at 0:54
  • 1
    With the added constraint that I wanted to get the line number (grep -n) in the actual file, I think tac pretty much had to be avoided, unless I wanted to do some subtraction with wc -l. Otherwise tac with grep -m1 makes a lot of sense. Commented Jul 4, 2014 at 18:48
  • 1
    I'd love to see a more performant version than this, since I am trying to search a 20GB file. Commented Sep 4, 2015 at 17:56
  • 1
    @DennisWilliamson 's answer is much better because grep will stop working after the first match. without -m 1, grep will first find all matching patterns in the file, then head will show only the first - much less efficient. Dennis, please consider posting this in a separate answer! Commented May 18, 2017 at 16:33
  • To keep grep colors when piping, use --color=always. Tail works great when grepping multiple files (e.g. grep pattern -r path), but tac option is not recommended for multiple files (probably have high memory consumption). Commented Feb 25, 2020 at 11:35
2

For someone working with huge text files in Unix/Linux/Mac/Cygwin. If you use Windows checkt this out about Linux tools in Windows: https://stackoverflow.com/questions/3519738/what-is-the-best-way-to-use-linux-utilities-under-windows.

One can follow this workflow to have good performance:

  1. compress with gzip
  2. use zindex (on github: https://github.com/mattgodbolt/zindex) to index the file with appropriate key
  3. query the indexed file with zq from the package.

Quote from its github readme:

Creating an index

zindex needs to be told what part of each line constitutes the index. This can be done by a regular expression, by field, or by piping each line through an external program.

By default zindex creates an index of file.gz.zindex when asked to index file.gz.

Example:

create an index on lines matching a numeric regular expression. The capture group indicates the part that's to be indexed, and the options show each line has a unique, numeric index.

$ zindex file.gz --regex 'id:([0-9]+)' --numeric --unique 

Example: create an index on the second field of a CSV file:

$ zindex file.gz --delimiter , --field 2 

Example:

create an index on a JSON field orderId.id in any of the items in the document root's actions array (requires jq). The jq query creates an array of all the orderId.ids, then joins them with a space to ensure each individual line piped to jq creates a single line of output, with multiple matches separated by spaces (which is the default separator).

$ zindex file.gz --pipe "jq --raw-output --unbuffered '[.actions[].orderId.id] | join(\" \")'" 

Querying the index

The zq program is used to query an index. It's given the name of the compressed file and a list of queries. For example:

$ zq file.gz 1023 4443 554 

It's also possible to output by line number, so to print lines 1 and 1000 from a file:

$ zq file.gz --line 1 1000 
2

I am always using cat (but this makes it a little longer way): cat file | grep pattern | tail -1

I would blame my linux admin course teacher at college who love cats :))))

-- You don't have to cat a file first before grepping it. grep pattern file | tail -1 and is more efficient, too.

3
  • 8
    This is just the first part of Cakemox's answer, except worse. Commented Sep 15, 2017 at 23:57
  • 2
    It works, but it does unnecessary steps. For light usage, this solution works fine, but it does not perform well. The reason is because you don't need to cat the file and pipe it to grep. You can have grep search the file directly via grep pattern file (and then use tail to return the last result), as in Cakemox's answer. Commented Jul 26, 2019 at 17:54
  • Is there any reason where cat ./file | grep ... would be in any way better than grep ./file ...? If yes, the description of this special circumstance should be added to the answer, if not, this version should be removed. In that case this answer adds nothing to the already accepted answer. Commented Nov 11, 2024 at 15:06
1

The above solutions only work for one single file, to print the last occurrence for many files (say with suffix .txt), use the following bash script

#!/bin/bash for fn in `ls *.txt` do result=`grep 'pattern' $fn | tail -n 1` echo $result done 

where 'pattern' is what you would like to grep.

1
  • grep 'pattern' $fn | tail -n 1 already prints to stdout. What's the advantage in collecting and echoing the output instead? Commented Nov 11, 2024 at 15:07
1

If you have several files, use inline-for:

for a in *.txt; do grep "pattern" $a /dev/null | tail -n 1; done 

The /dev/null provides a second file so grep will list the filename where the pattern is found.

2
  • It might be useful if $a variable is empty, but that is all. Commented Apr 27, 2022 at 9:46
  • @JohnGreene it's useful even if $a is non-empty. As stated in the answer, the second file argument tricks grep into multifile behaviour where it prints the filename next to the match. This way you get ./file: match instead of match without knowing where it came from. I don't know if there's a better way to trigger this behaviour, but it was certainly useful for my case. Commented Nov 11, 2024 at 15:13

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.