1

Say I have this as input:

..pattern1...this
..pattern1...1
..pattern1...2
...

Or

..pattern1...0
..pattern2...0
..pattern1...1
..pattern2...1
..pattern2...2
..pattern1...this
...

I.e. list of lines containing only two types of patterns (strings). I'm trying to obtain the first line of pattern1 after the last line of pattern2. If there is no pattern2 then the first line of pattern1. Example output for both cases would be:

 ..pattern1...this

I can do either one of this cases separately with sed (like with this answer) but I wasn't able to get a command which would do both ad the same time.

Anyone can jump in and lend an hand? Thanks!

JoeSlav
  • 4,479
  • 4
  • 31
  • 50
  • 1
    Never use the word `pattern` on it's own as it's highly ambiguous - when you say `pattern` do you mean `string` or `regexp` or something else? Do you want full-line matches or full-word matches or partial matches on either or something else? What should the output be given your posted sample input? Please [edit] your question to address all of that. – Ed Morton Jun 04 '19 at 14:25
  • 1
    Thanks. tried to clear it up. In my particular case i'm fine with strings – JoeSlav Jun 04 '19 at 14:28
  • `fine with` or `require`? If you're `fine with strings` then that implies you're `fine with regexps` too and the code would be briefer for regexp than string matching. – Ed Morton Jun 04 '19 at 14:29
  • Well, indeed as you say, a solution with regex it's absolutely fine. In my particular case i'm lucky enough to have to match something easy like abc_123 which works fine with both regex or strings. Thanks. – JoeSlav Jun 04 '19 at 14:32
  • and if `abc_1234` exists in your input then it's also considered as matching `abc_123`, right? – Ed Morton Jun 04 '19 at 14:35
  • What if there is no `pattern1` after the last occurrence of `pattern2`? – anishsane Jun 04 '19 at 17:06
  • @anishsane: empty answer is acceptable – JoeSlav Jun 04 '19 at 17:08

2 Answers2

1

Given one interpretation of your question this might be what you need:

If pattern means regexp and you're OK with partial rather than full matches then:

tac file | awk '/pattern1/{line=$0; f=1} /pattern2/{exit} END{if (f) print line}'

If you need full regexp matches then add start/end anchors (/^pattern1$/) and compare to a specific field (e.g. $4 ~ /^pattern1$/) instead of the whole line if you need word instead of line matches.

If you need string matches instead of regexp then use index() or == against $0 or $N (where N is your target field number) as appropriate.

If pattern1 and pattern2 are shell variables and given a whole bunch of assumptions about what a "pattern" might be:

tac file | awk -v re1="$pattern1" -v re2="$pattern2" '$0 ~ re1{line=$0; f=1} $0 ~ re2{exit} END{if (f) print line}'
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • Works for example n.2 but doesn't work with example n.1 where there is no pattern2 – JoeSlav Jun 04 '19 at 14:43
  • now works, thanks! why the final tac? should be only 1 line so there should be no need? – JoeSlav Jun 04 '19 at 14:47
  • 1
    Force of habit. Your problem is just a reduced version of the common problem of finding the last N occurrences of something solved idiomatically by `tac | cmd | tac`. I'll remove that final `tac`. – Ed Morton Jun 04 '19 at 14:48
  • 1
    Thank you! by the way, how can i modify the pattern1 and pattern2 in case they are bash variables like $pattern1, $pattern2 ? Thanks! – JoeSlav Jun 04 '19 at 14:57
  • I added an example of that to the end of my answer. – Ed Morton Jun 04 '19 at 15:01
1
awk '/pattern1/ && !line{line=$0} /pattern2/{line=""} END{print line}' file
  1. When pattern1 is found, set the line variable to entire line.
  2. When we find pattern1 again, ignore it. (since line will already be set.)
  3. When pattern2 is found, reset line variable, so that the next occurance of pattern1 is respected.

And Ed Morton's answer has already mentioned how to handle, if you want exact line match or if you want to take pattern from a shell variable. Adding my answer only to suggest that use of tac can be avoided.

anishsane
  • 20,270
  • 5
  • 40
  • 73