Reputation: 451
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
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
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
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 }'
x|c
requirements as OP just to showcase these work regardless of whether you need fixed-strings or regex based matches.Upvotes: 0
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
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
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
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