Mahyar
Mahyar

Reputation: 1121

Terminate tail command after timeout

I'm capturing stdout (log) in a file using tail -f file_name to save a specific string with grep and sed (to exit the tail) :

tail -f log.txt | sed /'INFO'/q |  grep 'INFO' > info_file.txt

This works fine, but I want to terminate the command in case it does not find the pattern (INFO) in the log file after some time

I want something like this (which does not work) to exit the script after a timeout (60sec):

tail -f log.txt | sed /'INFO'/q |  grep 'INFO' | read -t 60

Any suggestions?

Upvotes: 1

Views: 2713

Answers (3)

agc
agc

Reputation: 8406

The timeout command, (part of the Debian/Ubuntu "coreutils" package), seems suitable:

timeout 1m tail -f log.txt | grep 'INFO' 

Upvotes: 3

Charles Duffy
Charles Duffy

Reputation: 295272

Since you only want to capture one line:

#!/bin/bash
IFS= read -r -t 60 line < <(tail -f log.txt | awk '/INFO/ { print; exit; }')
printf '%s\n' "$line" >info_file.txt

For a more general case, where you want to capture more than one line, the following uses no external commands other than tail:

#!/usr/bin/env bash
end_time=$(( SECONDS + 60 ))
while (( SECONDS < end_time )); do
  IFS= read -t 1 -r line && [[ $line = *INFO* ]] && printf '%s\n' "$line"
done < <(tail -f log.txt)

A few notes:

  • SECONDS is a built-in variable in bash which, when read, will retrieve the time in seconds since the shell was started. (It loses this behavior after being the target of any assignment -- avoiding such mishaps is part of why POSIX variable-naming conventions reserving names with lowercase characters for application use are valuable).
  • (( )) creates an arithmetic context; all content within is treated as integer math.
  • <( ) is a process substitution; it evaluates to the name of a file-like object (named pipe, /dev/fd reference, or similar) which, when read from, will contain output from the command contained therein. See BashFAQ #24 for a discussion of why this is more suitable than piping to read.

Upvotes: 3

Fred
Fred

Reputation: 6995

This seems to work for me...

read -t 60 < <(tail -f log.txt | sed /'INFO'/q | grep 'INFO')

Upvotes: 3

Related Questions