2

I am trying to figure out a command which will enable me to read a log file in real time and execute a command when the string matches? I am using logkeys and trying to make it when I type a word it immediately triggers a command. This script works, but only when I press enter (start a newline) does it execute, and it seems anything I have found online also requires the press of the enter key to work. Is there a way to get around this somehow?

#/bin/bash
echo Waiting...
string='test'
tail /path/to/logfile -n0 -f | while read line; do
        if [[ $line =~ $string ]]; then
                echo "hello"
        fi
done
user unknown
  • 35,537
  • 11
  • 75
  • 121

1 Answers1

0

I've played with buffering settings to no avail, so my conclusion is that read waits for a newline before it finishes. If you instead did read -n1, read would read exactly one character, which isn't quite what we want either, because then $line would always be just that one char.

Unfortunately, grep appears to have the same behavior (even with buffering options changed), even with grep -o:

$ tail logfile -f -n0 | grep -o test &
[1] 25524
$ echo -n test >> logfile
$ echo -n test >> logfile
$ echo test >> logfile
test
test
test

I think the general solution would be to roll our own "ring buffer grep" search tool that reads character per character into a ring buffer.

Here's my perl version of that, hope it helps. (Save as: ringgrep.pl)

#!/usr/bin/perl -w

use strict;

if (!$ARGV[0]) {
    print "Usage: $0 needle\n";
    exit 1;
}
my $needle = $ARGV[0];
my $buffer_len = length($needle);
my @buffer = (0) x $buffer_len;
my $i = 0;
my $input;

while(sysread(STDIN, $input, 1)) {
    $buffer[$i] = $input;
    my $string = join("", @buffer);
    $string = (($i+1)%$buffer_len == 0 ? "" : substr($string, $i-$buffer_len+1)) . substr($string, 0, $i+1);
    # print "string is: $string\n";
    if ($string =~ /$needle/) {
        print "got test!\n";
        @buffer = (0) x $buffer_len;
        $i = 0
    } else {
        $i = (++$i) % $buffer_len
    }
}

Usage:

$ chmod +x ringgrep.pl
$ tail logfile -n0 -f | ./ringgrep.pl "this is a test" &
[1] 25756
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "this is a test" >> logfile
got test!
$ echo -n "this is a test" >> logfile
got test!
$ echo -n "this is a test" >> logfile
got test!
$ (echo -n t; echo -n h; echo -n i; echo -n s; echo -n ' '; echo -n i; echo -n s; echo -n ' '; echo -n a; echo -n ' '; echo -n t; echo -n e; echo -n s; echo -n t) >> logfile
got test!
sneep
  • 1,828
  • 14
  • 19
  • 1
    It's of course entirely possible to write this in bash, but maybe there's a better way to do this anyway. – sneep Mar 25 '18 at 06:08