1

How to write a bash script to exit tail -f filename.log if encountered a line in the file matches one of the following lines

Abaqus JOB filename COMPLETED

Abaqus/Analysis exited with errors

I know one can use Ctrl+C to exit tail -f but looking for automated way to do it.

Any assistance is appreciated.

phuclv
  • 2,086

3 Answers3

1
tail -f filename.log | sed '/Abaqus JOB filename COMPLETED/q'

As to "why would someone need this" appeals to the lack of imagination: because it may scroll too fast, and you may miss it. But you still want to check its progress. And you still want to have its latter part on your screen.

  • this doesn't work. Just like tail -f | grep which doesn't work, when sed exits tail -f still continues to run. However it may work in some newer GNU tail, not old GNU tail or busybox tail or BSD tail – phuclv Mar 09 '23 at 15:08
  • @phuclv It runs, until it tries to write something to the dead pipe. A possible improvement would be to explicitly send a PIPE signal to the command on the left from the right-hand-side when the pattern is detected. – Kusalananda Mar 09 '23 at 15:10
  • @Kusalananda no just try it and see, tail -f will only exit since GNU tail 8.28. See make tail -f exit on a broken pipe. It's frustrated because systems I work with use busybox tail or BSD tail. tail -f filename.log | grep -m 1 -q 'Abaqus JOB filename COMPLETED' also doesn't work – phuclv Mar 09 '23 at 15:18
  • @phuclv But my suggested improvement would still work: tail -f file | { grep -q pattern && pkill -P "$$" tail; } (using grep for shorter code). – Kusalananda Mar 09 '23 at 15:25
0
#!/bin/bash

p1="Abaqus/Analysis exited with errors"
p2="Abaqus JOB filename COMPLETED"

if tail /tmp/filename.log | grep -we "$p1" -we "$p2" > /dev/null 2>&1; then
    echo "pattern found"
    # ....do somenthing - what you need to do....
else
    echo "pattern not found"
fi

Endless loop:

while true; do
    if tail /tmp/filename.log | grep -we "$p1" -we "$p2" > /dev/null 2>&1; then 
       echo "pattern found !"
       # ....do something here....
       break
    fi
    sleep 1
done
s3n0
  • 180
  • 1
  • 8
0
sh -c 'echo "$$"; exec tail -f filename.log' | (
  IFS= read -r pid &&
    sed '/^Abaqus JOB filename COMPLETED$/q
         /^Abaqus\/Analysis exited with errors$/q' &&
    kill -s PIPE "$pid"
)

If we only did tail -f file.log | sed ...q, sed would quit upon seeing any of those lines, but then tail would only terminate (killed by a SIGPIPE) if ever it wrote anything to the pipe after that. And if that log file is never updated because the process that logs things to it has died, then that will never happen.

So here instead, we're sending the SIGPIPE (rather than a SIGTERM which with some shells cause an unwanted diagnostic message to be printed) manually. Using sh to pass the pid of tail (which reuses that of sh because of exec) along to the right hand side of the pipe in the beginning.

A perl equivalent:

sh -c 'echo "$$"; exec tail -f filename.log' | perl -lne '
  BEGIN {$pid = <> or die "No pid."}
  print;
  if ($_ eq "Abaqus JOB filename COMPLETED" ||
      $_ eq "Abaqus/Analysis exited with errors") {
    kill "PIPE", $pid;
    last;
  }'