Relying on killing PID may not be reliable if tail -f
exits too soon (e.g. file does not exist).
Another more generic solution, that will also stop the "kill timer" if it is done before the timeout is to use wait
and kill %%
(%%
indicates the most recent job started by bash - using &
).
The construction will look like this (in which sleep 2
is any custom command with parameters etc and the timeout is set to 10 seconds):
# Your command that may take some (or not) time to execute:
sleep 2 &
# Set a timer that kills the last started job
sleep 10 & wait -n 1; kill %%; wait -n 1
This construction will wait until either the timeout is reached, or the custom command is done executing (in this case the sleep 2
will always stop first). Then, respectively the command or timeout will be killed with kill %%
. Then, the last wait -n 1
blocks further execution until the command or timeout is actually killed (this is optional but likely desired).
Note. Earlier commands executed in parallel will not be affected, as is required and expected.
Example
A more practical example with reading a line from a named pipe with timeout, but for which the timeout-flag of read
cannot be used:
# Custom bash function to set timeout in seconds for the most recently started job (parallel execution).
timeout() { sleep $1 & wait -n 1; kill %%; wait -n 1 }
Example 1:
mkfifo /tmp/test
read ln </tmp/test &
timeout 10
Because there is nothing written to /tmp/test
it will timeout after 10 seconds. In order to show that even when the command terminates immediately it will stop the kill timer:
# Example 2:
mkfifo /tmp/test
echo "Hello" >/tmp/test &
ln="$(read ln </tmp/test && echo -e "$ln" & timeout 10)"
echo "Line was read: $ln"
Note with regard to practical use of named pipe. Because read ln
is executed in a subshell, it cannot be accessed in the (parent) script. Therefore, the line is printed with echo
to store it in a (different) variable ln
.