19

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?

1
  • 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 Sep 7, 2009 at 13:51

7 Answers 7

19

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
7
  • 1
    sorry but this is not working, it doesn't beep or prints anything Sep 7, 2009 at 13:49
  • 4
    Although 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
    Sep 7, 2009 at 15:00
  • 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. Sep 7, 2009 at 19:57
  • 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: stackoverflow.com/questions/1000674/turn-off-buffering-in-pipe
    – user4358
    Sep 8, 2009 at 16:28
  • 2
    Going off of @nagul's suggestion, here is the invocation that worked for me: tail -f development.log | stdbuf -oL -eL grep "something rare" | beep Jan 25, 2017 at 17:08
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.

3
  • 1
    That wouldn't beep only on "something rare", would it?
    – user4358
    Sep 7, 2009 at 19:06
  • 1
    It 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 Sep 7, 2009 at 19:58
  • 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
    Sep 8, 2009 at 9:38
3

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!

1
  • the only one that worked for me, no issues with buffering, no self-abort. kudos for a simple OOB solution with no dependencies May 5, 2023 at 18:19
2

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. ;-)

1

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.

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"'
2
  • 1
    Note, 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
    Aug 28, 2012 at 19:14
  • This didn't work for me (despite adding watch --beep and wrapping my tail/grep, I still didn't get a beep). Nov 1, 2018 at 22:13
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.

You must log in to answer this question.

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