0

I am searching for a way to execute a command when a keyword appears in a given file, say temp.txt. Currently, I have tried tail -F temp.txt | awk '/keyword/ {echo found}'. The desired goal is for echo found to execute only when a new line is added to temp.txt that contains keyword. I would like to find the simplest solution with basic commands.

I feel like the answer to this linked post is what I want, but it isn't working for me, and I cant figure out why.

Best way to follow a log and execute a command when some text appears in the log

1 Answers1

3

Try:

while :; do awk '/keyword/{print "found"}'; sleep 1; done < temp.txt

or if you want to execute a shell command instead of just printing "found":

while :; do awk '/keyword/{system("echo \047found\047"}'; sleep 1; done < temp.txt

That uses mandatory POSIX commands and will work in any shell on every Unix box. For example:

$ echo foo > temp.txt

$ while :; do awk '/keyword/{print "\n>>> found <<<\n"}'; sleep 1; done < temp.txt & [1] 16520

$ echo keyword >> temp.txt $ >>> found <<<

See https://unix.stackexchange.com/a/629912/133219 for more information on that approach.

If "keyword" doesn't appear often in the file and you care about that loop spinning in the background using CPU cycles, you can get a bit fancier with the sleep value by doubling it up to some max value every time through the loop when keyword isn't found so the code spends far more time sleeping than executing, something like this bash code but can be written for any shell:

secs=1
maxSecs=60
while :; do
    if awk '/keyword/{print "found"; f=1} END{exit !f}'; then
        secs=1
    else
        if (( (2*secs) < maxSecs )); then
            secs=$(( 2*secs ))
        else
            secs=$maxSecs
        fi
    fi
    sleep "$secs"
done < temp.txt

That will run awk once per second while "keyword" is showing up that quickly in your file, and when it's not it'll gradually cycle down to calling awk once per minute until "keyword" starts showing up again and then start calling awk once per second again. Obviously set maxSecs to whatever value you like - 5, 30, 3600, whatever.

Ed Morton
  • 31,617
  • You could use inotifywait to get rid of the sleep. – lumbric Jan 27 '21 at 13:35
  • @lumbric inotifywait is not a mandatory POSIX command, it's not even a POSIX-defined utility, see https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html. It looks like it might be a Linux thing (and the OP is on a Mac, see https://superuser.com/q/371354 for a similar Mac command - thanks google :-)! ) and so wouldn't be portable to all Unix boxes. The OP said they want the simplest solution with basic commands and I interpret "basic" as "mandatory POSIX". – Ed Morton Jan 27 '21 at 14:11
  • yes, you are right. A busy loop is probably good enough in most situations. Still, avoiding the sleep is a better solution if it is possible because it's faster and more efficient. So I guess there is a trade off, but in practice won't make a difference. – lumbric Jan 28 '21 at 14:56
  • 1
    @lumbric Maybe you missed my point - inotifywait is not available on all Unix boxes and is not a "basic" command as I understand the OPs request so there is no trade-off, it's just not a viable solution to this question. – Ed Morton Jan 28 '21 at 14:59