How to find last occurrence of pattern and print all lines following the last occurance [duplicate]
How to find last occurrence of pattern and print all lines following the last occurance [duplicate]
This question already has an answer here:
I have a file which contains multiple occurrences of pattern "====". These patterns are followed by texts. I'd like to search for the last occurrence of the "====" pattern and print all the lines after the pattern. Any ideas on how to do it in bash? Combination of grep, awk, sed or tail, etc..?
grep
awk
sed
tail
Sample file:
====
First run of script
End of first Script
====
2nd run of script
End of 2nd script
====
3rd run of script
End of 3rd script
Output of the command should look like below.
3rd run of script
End of 3rd script
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
No problem at all; cross-site duplicates aren't a thing, and a question being duplicate doesn't mean it's bad.
– l0b0
Sep 9 '18 at 4:22
Welcome , You can vote up or accept the answer which solve your problem instead of editing your question.
– GAD3R
Sep 9 '18 at 11:20
5 Answers
5
Based on SO duplicate:
$ sed -n '/====/h;//!H;$!d;x;//p' sample.txt
====
3rd run of script
End of 3rd script
Output of the command should look like below.
3rd run of script
End of 3rd script
I started writing an explanation, but I don't actually understand some of the commands so I'll just refer to info sed.
info sed
(Let's see if I got this right)
/====/h sets the hold buffer to the current line if it matches ====; //!H adds it to the hold buffer if it doesn't match ====. Those combine to clear the hold on a ==== and to store any lines in there. Then $!d deletes the current line (it's already stored in the hold) and jumps to the next, unless this was the last line. The x and p only run on the last line: x loads the hold buffer to the active buffer, and //p prints it if it contains a ====. (The // pattern means to use the previous pattern, so it's just a shorthand for /====/.)– ilkkachu
Sep 9 '18 at 12:19
/====/h
====
//!H
====
====
$!d
x
p
x
//p
====
//
/====/
The final condition on the
p means that if ==== isn't seen, this won't print anything. But this also will print the ==== line itself.– ilkkachu
Sep 9 '18 at 12:21
p
====
====
Software tools method, more efficient for big files since it only reads the end of the file, then stops when it finds the last match:
tac sample.txt | grep -F -m1 -B 999999 '====' | head -n -1 | tac
Note: increase or reduce 999999 as needed, just so it's longer than any possible match. See also *Glenn Jackman's answer with variants for awk and sed which avoid the need for 99999. For systems with low resources, the grep method is the most efficient of the three variants.
999999
awk
sed
99999
grep
save the text in file.txt and run this command:
file.txt
awk 'p; /====/ $p' file.txt | tail -3
output:
3rd run of script
End of 3rd script
Your
awk command doesn't do anything... You may as well run only tail -3 (which is obsolete - you should use tail with -n). Either way, this only works with the OP input sample, that is it only prints the last 3 lines, regardless. I guess the 3 people who mechanically upvoted here don't care about that.– don_crissti
Sep 9 '18 at 11:46
awk
tail -3
tail
-n
With
p unset, the first p is a falsy condition, and doesn't do anything. The second is the same as /====/ $0 when p gets coerced to an integer, but what in the world is the concatenation of a regex and a field (or string) supposed to be? It does seem to act like a true condition, though, so it looks like that's the same as running awk 1. Or cat.– ilkkachu
Sep 9 '18 at 11:53
p
p
/====/ $0
p
awk 1
cat
Same concept as agc's answer, but doesn't require you to guess how many lines there might be:
with GNU sed
tac file | sed '/====/Q' | tac
or awk
tac file | awk '/====/ exit 1' | tac
The idea of the double tac is to invert the question: How to print all lines preceding the first occurrence of pattern. That is a much simpler problem.
tac
In awk, something like this:
awk
awk '/====/ t = ""; next t = t $0 "n"; END printf "%s", t; ' < sample.txt
It stores the input lines in t, and clears t when it sees ====. What ever is in there is printed at the end.
t
t
====
Note that
====
/^====$/
====
====
sed 1d
Another version that only starts storing input after the ==== separator is seen, effectively producing an empty output if the input doesn't contain the separator:
====
awk '/====/ any = 1; t = ""; next any t = t $0 "n"; END printf "%s", t; ' < sample.txt
Thank you @l0b0 for posting the link. Sorry for the duplicate question. I had done many searches for a possible solution and the link you provided matches my exact needs.
– user612223
Sep 9 '18 at 4:13