17

When watching a growing log file with e.g. "less -iS +F service.log" I want to limit the display to lines matching a certain pattern.

I tried something like

less +F service.log | grep <pattern> | less +F

which doesn't work. Also

cat < service.log | grep <pattern> | less +F

doesn't do what I want. It looks like the input is already closed and less doesn't show changes.

How can I limit the display to lines matching a certain pattern?

seven-phases-max
  • 11,765
  • 1
  • 45
  • 57
axelrose
  • 556
  • 5
  • 17

5 Answers5

28

This question is ages old, but I still think it's worth adding a solution. Instead of trying to grep first and then use less, what about using filtering inside less?

In brief:

  1. use less +F on your file
  2. CTRL-C to temporarily break the "following" action
  3. Type & and your pattern to enable filtering
  4. Issue +F to re-enable the "following" action

More details on this answer on the Unix&Linux StackExchange

Jir
  • 2,985
  • 8
  • 44
  • 66
  • 1
    thaaanks !! I found the Linux stackexchange and didn't understand it. Glad to find your step by step explanation !! :) – Sylvain Jun 27 '20 at 13:17
  • 3
    To do this directly from the command line: `less '+&^MF' service.log` where the `^M` is obtained by typing ctrl-v and then enter – Jack Valmadre Nov 19 '20 at 13:38
  • 2
    From bash, without the C-v (quoted-insert): `less $'+&\nF' service.log` – vdm Feb 09 '21 at 20:59
2

I haven't yet worked out how to do this without a temp file, but here is a script that demonstrates a functional grep-filtered less +F (which cleans up its temp file). I call it lessf.

One of the key elements is the --line-buffered argument to grep that permits tail output to continue to flow through the pipeline (the unbuffer command provided by expect provides similar functionality for any program).

#!/bin/sh
LOGFILE=$1
shift
PATTERN=$@
TEMP_DIR=/tmp/lesstmp
TEMP_FILE="$TEMP_DIR/$(basename $LOGFILE)"
[ ! -d $TEMP_DIR ] && mkdir $TEMP_DIR
trap 'rm -rf "$TEMP_DIR"; exit' INT TERM EXIT
( tail -f "$LOGFILE" | grep --line-buffered $PATTERN ) > "$TEMP_FILE" | less +F "$TEMP_FILE"
trap - INT TERM EXIT

Example usage:

lessf /var/log/system.log foobar

lessf /var/log/system.log -v nobar

Araxia
  • 1,136
  • 11
  • 22
  • I'd like to point out how using the pipe between tail|grep and less +F makes sure that Ctrl-C will kill them all in one go. I had something similar but using background jobs, it doesn't work as well. – Spidey Dec 29 '20 at 17:46
1

If you don't mind spawning and tearing down a couple of processes each line, use a read while loop

tail -f filename.log|while read line; do echo $line | grep pattern; done
MPelletier
  • 16,256
  • 15
  • 86
  • 137
joe
  • 11
  • 1
-1
tail -f service.log | grep <pattern>
Chris May
  • 670
  • 3
  • 6
  • 2
    Thanks for pondering my question though this is not exactly what I want. I want to have "less" functionality, i.e. scrolling, searching, hightlighting within the resulting output. Something like "tail -f service.log | grep | less +F" which unfortunalely doesn't do it. – axelrose Nov 19 '09 at 11:28
-1

The solution seemed simply

LESSOPEN='|grep <pattern> %s' less +F service.log

but less doesn't continue to read new lines from the growing log file.

axelrose
  • 556
  • 5
  • 17
  • my original answer from 2009 doesn't seem to be valid anymore, i.e. the simple solution does watch for a growing log file – axelrose May 01 '19 at 15:36