2

How can I view in less from the first instance of some arbitrary string "foo" to the last instance?

This arbitrary string will be on most every line of the log. I don't want to do grep "foo" bar.log | less because it won't be on each line that's relevant.

Let's say the file is

1 Random junk I don't want to see
2 Care about (foo)
3 Care about (foo)
4 Care about
5 Care about (foo)
6 Other random junk I don't want to see

Unfortunately the lines I want to ignore do not follow a nice pattern, otherwise I could use just grep -v 'insert pattern here'.

I am wondering how to get the following into less somehow,

2 Care about (foo)
3 Care about (foo)
4 Care about
5 Care about (foo)

grep "foo" bar.log | less will not work because it ignores line 4, which is one I care about.

Captain Man
  • 1,156
  • 3
  • 12
  • 25
  • you say "don't want to grep" but you tagged sed and grep; are you opposed to calling `sed` before `less`? – Jeff Schaller Mar 17 '16 at 15:23
  • So you want an interactive `grep`, one where you only get shown one result at a time and you can switch between them? – phk Mar 17 '16 at 15:28
  • @Jeff I don't mind at all, I just tagged with `grep` and `sed` because they seemed like the tools that would be able to do this. – Captain Man Mar 17 '16 at 15:31
  • This has been asked before more than once - here's just an example: [How can I “grep” patterns across multiple lines?](http://unix.stackexchange.com/q/112132) so in your case `pcregrep -M 'foo.*(\n|.)*foo' infile` – don_crissti Mar 17 '16 at 15:32
  • @phk no, that's not what I meant, I've updated with an example. – Captain Man Mar 17 '16 at 15:33
  • Ok, then it might be related to: https://stackoverflow.com/questions/20096639/awk-or-sed-to-print-text-between-the-first-occurance-of-a-pattern-and-the-last-o – phk Mar 17 '16 at 15:37

3 Answers3

4

If you have awk you can do:

awk '/foo/{print b$0;b="";x=1;next} x{b=b$0"\n"}' bar.log | less

When a foo appears, it prints buffer (b variable) and current line, and clears the buffer.

Otherwise, but only if foo already appeared (x variable) it buffers current line.

mik
  • 1,332
  • 11
  • 15
1

It takes some careful shell quoting, but you could use the scriptable editor ed for this:

printf '%s\n' "/foo/ka" "??" "'a,.w "'!less' q | ed -s file

This sends four commands to ed:

  1. /foo/ka -- searches (from the beginning of the file) for the pattern foo; at that first match, set a mark named a.
  2. ?? -- repeat the search, but going backwards, wrapping around the end of the file; the important byproduct here is that it sets the current line to that (last) match.
  3. 'a,.w !less -- from the mark named a through the current line (.), write those lines to the shell command (!) less.
  4. q -- quit ed.

You'll need to exit less gracefully (q) in order for ed to exit. This solution assumes that the pattern exists at least once in the file, otherwise the searches will fail and you'll get three ?'s before ed exits.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
0

Are you looking to use grep to exclude matching lines, rather than include them? If so, try: grep -v "Don't care about"

izzy
  • 101
  • 3
  • Unfortunately not all the lines I want to skip have a nice pattern. I made them all say that in the question to make it a little more clear what I asking for. In reality this is about getting the "subsection" of the logs for a specific transaction. Many (but not all) of the lines relevant have the transaction's ID. Some log messages span multiple lines. That's why I wanted to get everything between the first and last instead of just reversing the filter. I will update the question to make this more obvious. – Captain Man Oct 29 '19 at 18:50