user3702858
user3702858

Reputation: 451

How to grep the last occurrence of a line pattern

I have a file with contents

x
a
x
b
x
c

I want to grep the last occurrence,

x
c

when I try

sed -n  "/x/,/b/p" file

it lists all the lines, beginning x to c.

Upvotes: 44

Views: 131928

Answers (7)

steffen
steffen

Reputation: 16938

I'm not sure I got your question right, but here are a few ideas:

  • Print last occurence of x (regex):

      grep x file | tail -1
    
  • Alternatively (should be faster because it reads from the end):

      tac file | grep -m1 x
    
  • Print file from first matching line to end:

      awk '/x/{flag = 1}; flag' file
    
  • Print file from last matching line to end (prints all lines in case of no match):

      tac file | awk '!flag; /x/{flag = 1};' | tac
    

Upvotes: 89

zyy
zyy

Reputation: 1574

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.

Upvotes: 0

RARE Kpop Manifesto
RARE Kpop Manifesto

Reputation: 2819

if you wanna do awk in truly hideous one-liner fashion but getting awk to resemble closer to functional programming paradigm syntax without having to keep track when the last occurrence is

mawk/mawk2/gawk 'BEGIN { FS = "=7713[0-9]+="; RS = "^$";

 } END { print ar0[split($(0 * sub(/\n.+$/,"",$NF)), ar0, ORS)] }'

Here i'm employing multiple awk short-hands :

    sub(/[\n.+$/, "", $NF) # trimming all extra rows after pattern

g/sub() returns # of substitutions made, so multiplying that by 0 forces the split() to be splitting $0, the full file, instead.

split() returns # of items in the array (which is another way of saying the position of last element), so even though I've already trimmed out the trailing \n, i still can directly print ar0[split()], knowing that ORS will fill in the missing trailing \n.

That's why this code looks like i'm trying to extract array items before the array itself is defined, but due to flow of logic needed, the array will become defined by the time it reaches print.

Now if you want something simpler, these 2 also work

mawk/gawk 'BEGIN { FS="=7713[0-9]+="; RS = "^$" 

   } END { $NF = substr($NF, 1, index($NF, ORS)); 
           
           FS = ORS; $0 = $0; print $(NF-1) }'

or

mawk/gawk '/=7713[0-9]+=/ { lst = $0 } END { print lst }'
  • I didn't use the same x|c requirements as OP just to showcase these work regardless of whether you need fixed-strings or regex based matches.

Upvotes: 0

Kajukenbo
Kajukenbo

Reputation: 275

POSIX vi (or ex or ed), in case it is useful to someone

Done in Command mode, of course

:set wrapscan

Go to the first line and just search Backwards! 1G?pattern

Slower way, without :set wrapscan

G$?pattern

Explanation:

G go to the last line

Move to the end of that line $

? search Backwards for pattern

The first backwards match will be the same as the last forward match

Either way, you may now delete all lines above current (match)

:1,.-1d

or

kd1G

You could also delete to the beginning of the matched line prior to the line deletions with d0 in case there were multiple matches on the same line.

POSIX awk, as suggested at get last line from grep search on multiple files

awk '(FNR==1)&&s{print s; s=""}/PATTERN/{s=$0}END{if(s) print s}'

Upvotes: 0

potong
potong

Reputation: 58371

This might work for you (GNU sed):

sed 'H;/x/h;$!d;x' file

Saves the last x and what follows in the hold space and prints it out at end-of-file.

Upvotes: 4

elig
elig

Reputation: 3058

grep -A 1 x file | tail -n 2

-A 1 tells grep to print one line after a match line
with tail you get the last two lines.

or in a reversed way:

tac fail | grep -B 1 x -m1 | tac

Note: You should make sure your pattern is "strong" enough so it gets you the right lines. i.e. by enclosing it with ^ at the start and $ at the end.

Upvotes: 9

ray
ray

Reputation: 4267

not sure how to do it using sed, but you can try awk

awk '{a=a"\n"$0; if ($0 == "x"){ a=$0}}  END{print a}' file

Upvotes: 1

Related Questions