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.

6
  • 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, 2016 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, 2016 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. Mar 17, 2016 at 15:31
  • This has been asked before more than once - here's just an example: How can I “grep” patterns across multiple lines? so in your case pcregrep -M 'foo.*(\n|.)*foo' infile Mar 17, 2016 at 15:32
  • @phk no, that's not what I meant, I've updated with an example. Mar 17, 2016 at 15:33

3 Answers 3

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.

2
  • Both this and @Stephane 's solution work. How do these stop on the last foo? Mar 17, 2016 at 16:36
  • 1
    @CaptainMan only when foo appears, the lines since last foo are printed.
    – mik
    Mar 17, 2016 at 16:43
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.

0

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

1
  • 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. Oct 29, 2019 at 18:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .