Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using `until` and `/usr/bin/timeout` in a script

I want to perform a command that takes about 1 minute to finish, in a bash script. However, sometimes this command hangs, so I want to use /usr/bin/timeout inside a loop until it works.

If I use timeout 300 mycommand myarg1, it works, but if I use it inside bash in this loop below, it doesn't print anything (not even the typical output that my command prints) and it hangs!:

until timeout 300 mycommand myarg
do
    echo "The command timed out, trying again..."
done

My version of bash:

$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

My version of timeout:

$ timeout --version
timeout (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

(Standard Ubuntu16.04)

like image 430
knocte Avatar asked Jun 21 '17 14:06

knocte


People also ask

How do I put sleep in a bash script?

How to Use the Bash Sleep Command. Sleep is a very versatile command with a very simple syntax. It is as easy as typing sleep N . This will pause your script for N seconds, with N being either a positive integer or a floating point number.

What is timeout in shell script?

timeout is a command-line utility that runs a specified command and terminates it if it is still running after a given period of time. In other words, timeout allows you to run a command with a time limit.

What is until in bash?

The Bash has three types of looping constructs namely for, while, and until. The Until loop is used to iterate over a block of commands until the required condition is false. Syntax: until [ condition ]; do block-of-statements done.


2 Answers

The problem with

(mycommand args) & pid=$!
sleep 1000 && kill -INT $pid

is that it always takes 1000 seconds. Taking a more active (and CPU consuming) approach will shorten that time:

typeset -i i 
i=0
command with arguments &
pid=$!
while ps $pid > /dev/null 2>/dev/null ; do
    i=$i+1
    if [ $i -gt 999 ] ; then
        kill -9 $pid
    fi
    sleep 1
done

Or, if your system is not too busy or the interval is short:

command with arguments &
pid=$! 
echo "kill -9 $pid" | at "now + 15 minutes"
wait

But timeout will also work, of course.

The reason why

until timeout 300 mycommand myarg
do
    echo "The command timed out, trying again..."
done

hangs is that bash will try to evaluate the condition for your until, which may take upto 300 seconds. If timeout 300 mycommand myarg returns a success, the until is never executed. Try for example:

if timeout 30 mycommand myarg ; then
    echo "The timeout of mycommand succeeded"
else
    echo "It failed! What a pitty"
fi 
like image 83
Ljm Dullaart Avatar answered Sep 22 '22 13:09

Ljm Dullaart


This works like a charm.

When command finish:

sh timeoutLoop.sh 4

OUTPUT:

Seconds start now...
3 seconds passed...

When command not finish:

sh timeoutLoop.sh 2

OUTPUT:

Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
Seconds start now...
The command timed out, trying again...
...

Your mycommand = waitSeconds.sh

#!/bin/bash -

echo "Seconds start now..."
sleep $1
echo "$1 seconds passed..."

And the loop bash timeoutLoop.sh

#!/bin/bash -

until timeout $1 waitSeconds.sh 3
do
echo 'The command timed out, trying again...'
done

Try this, and don't forget the header of the script.

like image 43
Qinsi Avatar answered Sep 21 '22 13:09

Qinsi