how to beep on tail -f event

14

3

I want my PC to make a system beep on every tail event

I have the following command

tail -f development.log | grep "something rare"

is there an easy way like pipeing it to somthing that beeps? like

tail -f development.log | grep "something rare" | beep

if so will the grep output still be shown?

Jakob Cosoroaba

Posted 2009-09-07T12:55:04.137

Reputation: 1 661

there is a beep program with is in the default repo for debian and variants just apt-get install beep but it doesn't work with the piping this way – Jakob Cosoroaba – 2009-09-07T13:51:58.817

Answers

16

Just define beep as following:

beep() { read a || exit; printf "$a\007\n"; beep; }

Then, you can use your command:

tail -f development.log | grep "something rare" | beep

mouviciel

Posted 2009-09-07T12:55:04.137

Reputation: 2 858

2Going off of @nagul's suggestion, here is the invocation that worked for me: tail -f development.log | stdbuf -oL -eL grep "something rare" | beep – GuitarPicker – 2017-01-25T17:08:39.597

Use read -r to avoid clobbering backslashes, and printf '%s\007\n' "$a" to avoid interpreting percent-escapes in the output. I also don’t think the tail call is necessary (and I’m not sure if Bash will optimize it away) – just do while read …; do …; done instead. – Lucas Werkmeister – 2018-08-12T14:32:57.503

If this isn't working for you make sure your system alert volume is turned up. It's possible to have other volumes turned up, making you think the volume is on, but not have system alerts specifically turned up ... which is the channel the beeps will use. – machineghost – 2019-02-12T19:55:21.407

1sorry but this is not working, it doesn't beep or prints anything – Jakob Cosoroaba – 2009-09-07T13:49:58.587

4Although direct output of tail -f is immediate, it gets buffered as soon as it goes through a pipe. You have to wait for enough "something rare" before observing anything. – mouviciel – 2009-09-07T15:00:05.150

You could pass the output through sed or similar (between tail and grep) to with a regexp replace the something rare with itself many times. How many times it needs to be done depends on how much the pipe is buffered. – David Spillett – 2009-09-07T19:57:13.433

6

@David - That is a hit-and-miss approach. If you want to unbuffer the data passed via a pipe, use any of the methods described in these answers: http://stackoverflow.com/questions/1000674/turn-off-buffering-in-pipe

– None – 2009-09-08T16:28:17.127

10

GNU screen has a built-in feature to beep when a given window changes: see the relevant section of the man page.

Headline summary:

$ screen
$ tail -f yourfile.log    # inside the screen session
<C-a> M    # "Window 0 (bash) is now being monitored for all activity."

As pointed out in comments, this will beep on every new log entry, not just those that match "something rare", so this doesn't do quite what the OP asked for. Still a useful trick to know IMHO.

You can get the best of both worlds by opening two screen windows (<C-a> c to open a window, <C-a> <C-a> to toggle between two windows):

  1. monitored, with tail -f yourfile.log | grep 'something rare'
  2. unmonitored, with a plain tail -f yourfile.log

Then you can sit watching the log scroll past in window 2, and you'll get beeped from window 1 when "something rare" occurs.

screen is awesomely versatile - I highly recommend reading up on it.

Sam Stokes

Posted 2009-09-07T12:55:04.137

Reputation: 516

1That wouldn't beep only on "something rare", would it? – None – 2009-09-07T19:06:18.217

1It would if all that was happening in that particular window is tail -f yourfile.log | grep something\ rare rather than just the tail -f logfile – David Spillett – 2009-09-07T19:58:38.157

Oops, I didn't notice that he only wanted a beep on something rare. Edited to reflect this. The grep would work, but then he wouldn't see the rest of the log, only the rare lines - as I understand it he wants to be able to watch the whole log scrolling past, but be alerted on specific events. – Sam Stokes – 2009-09-08T09:38:26.433

1

You can stop the output from being buffered in the grep command. See man grep for details.

You can pipe the grep output into beep.

The following example is from man beep...

   As part of a log-watching pipeline

          tail -f /var/log/xferlog | grep --line-buffered passwd | \
          beep -f 1000 -r 5 -s

There's a lot of good stuff in those manuals. If only we didn't have to read them to find it. ;-)

A reader

Posted 2009-09-07T12:55:04.137

Reputation: 11

1

The watch command has an --beep option, and you can set the polling interval as well, but the standard with 2 sec should be ok

watch --beep 'tail development.log | grep "something rare"'

oanoss

Posted 2009-09-07T12:55:04.137

Reputation: 19

This didn't work for me (despite adding watch --beep and wrapping my tail/grep, I still didn't get a beep). – machineghost – 2018-11-01T22:13:21.150

1Note, watch works by running your parameter/command every (interval) sections then coming the results to the previous run. Thus you'll want to use the normal version of the tail command, instead of using tail -f – RyanWilcox – 2012-08-28T19:14:00.097

1

You could use sed to add the control-G as follows:

tail -f myFile | sed "s/.*/&\x07/"

or just on rare lines, without using grep, as follows:

tail -f myFile | sed -n "/something rare/s/.*/&\x07/p"

which says: on the lines where something rare occurs, substitute everything for the same stuff with control-G tacked on the end, and print (but don't print the non-matching lines). Works great!

Mi5ke

Posted 2009-09-07T12:55:04.137

Reputation: 31

0

Hm, tricky. We could perhaps do something like this?

for i in `find | grep 7171`; do beep; echo $i; done

Or in your case

for i in `tail -f development.log | grep "something rare"`; do beep; echo $i; done

It seems to be doing some buffering though. I'll look if there's a way to turn off this buffering by the for loop.

Apparently, you should be able to adjust pipe's buffering by using ulimit -p but that keeps complaining about Invalid argument to me. I've also found a post that claims you need to recompile kernel to change this limit.

Ivan Vučica

Posted 2009-09-07T12:55:04.137

Reputation: 885

0

In a previous job, I couldn't get a reliable watcher with just command-fu, so I had a wrapper script like the one below, which inspected the file every poll_duration seconds and grepped the new lines for the interested phrase.

#!/bin/bash

file=$1
phrase=$2
poll_duration=$3

typeset -i checked_linecount
typeset -i new_linecount
typeset -i new_lines
let checked_linecount=new_linecount=new_lines=0
echo "Watching file $file for phrase \"$phrase\" every $poll_duration seconds"

while [ 1 ]
do
        let new_linecount=`wc -l $file| awk '{print $1}'`
        if [[ $new_linecount > $checked_linecount ]]; then
                let "new_lines = $new_linecount-$checked_linecount"
                head --lines=$new_linecount "$file" | tail --lines=$new_lines | grep "$phrase" && beep
                let checked_linecount=$new_linecount
        fi
        sleep $poll_duration
done

This was on a Unix machine. On Linux, you can go one better by using its inotify filewatcher interface. If this package (inotify-tools on Ubuntu) is present, replace

sleep $poll_duration 

with

inotifywait -e modify "$file"  1>/dev/null 2>&1

This call blocks till the file is modified. The blocking version is almost as efficient as what you'd get with the tail -f version if pipe could be configured to work without buffering.

Note: The script first does a head --lines=$new_linecount to ensure that lines added to the file after we checked it do not skew the chunk of the file that gets checked in this loop.

user4358

Posted 2009-09-07T12:55:04.137

Reputation: