RSS Feed Share on Twitter

All Shell Scripting Tips

19 Jan 2018

A 'sleep' substitute which gives regular updates

The sleep command can be quite useful in an interactive shell. If you want to check something in 5 minutes time, or even just want a timer for a while, a simple "sleep 5m ; curl http://localhost/test.php" or just "sleep 5m ; echo Ready" can be handy terminal tricks.

However, once you've set it off, it's not easy to tell how long it's been running for.

This shell script takes a second parameter, which allows you to specify how often you want to get updates on the sleep process. You can also add a message to display each time it checks in.

Here, we ask it to sleep for 20 seconds, and give an update every 9 seconds. Every 9 seconds, until the 20 seconds are up, it says "Waiting for the ghost in the shell...", because that is the message we asked it to display:

sleepy example

(Note: The original Unix sleep only took an integer number of seconds, eg "sleep 300" to sleep for 5 minutes (300 seconds). Most current sleep implementations can understand "10s", or "20m" or "2h" as "10 seconds," "20 minutes," and "2 hours" respectively).

How it works

The script is shown below. The script spawns two child processes which are run in the background; the first is the main "sleep" command. The "$!" variable stores the Process ID (PID) of the last backgrounded process. The script stores this in $SLEEP_PID. It then starts an infinite loop ("while :" will loop forever), which also runs in the background (note the ampersand (&) after the "done" - this causes the whole loop to be a background process).

It makes a note of the Loop's PID so that the loop can be killed as soon as the script ends (even if you terminate the script abruptly).

The "wait $SLEEP_PID" causes the whole script to block until the sleep has finished; the two background processes keep on running.

The loop will display a message (including the current time), and sleep until its next message is due. It will keep doing this forever, or until it is killed.

The "trap 'kill -9 $LOOP_PID' 0" will kill the Loop if the script is terminated for any reason. Otherwise, the loop might keep writing to your terminal forever!

Once the main sleep has finished, the "wait $SLEEP_PID" is signalled by the operating system, and the script continues to the next command, which is to echo a final "Done" message to tell you that it has finished.

The code

Download sleepy.sh
#!/bin/bash
if [ "$#" -eq "0" ] || [ "$1" == "-?" ]; then
  echo "Usage: `basename $0` sleeptime frequency [message]"
  echo "Sleep for , notify every "
  exit 2
fi
SLEEPTIME=$1
FREQUENCY=$2
shift 2
MESSAGE=$@

# Start the main sleep process
sleep "$SLEEPTIME" &
# Note the PID of the sleep process
SLEEP_PID=$!

while :
do
  echo "`date` $MESSAGE"
  sleep $FREQUENCY
done &
# The while loop runs in its own subshell, and
# has its own PID. Make a note of the loop's PID:
LOOP_PID=$!

# Kill the loop if we exit for any reason
trap 'kill -9 $LOOP_PID' 0

# Wait for the main sleep to finish
wait $SLEEP_PID

# Finished.
echo "`date` $MESSAGE : Done."

Getting the timing right

Note in the example above, that the script started at 27 seconds past the minute, and finished 20 seconds later, at 47 seconds past the minute, even though it was reporting in 9 second intervals; 27+9=36, then 45, then 2 seconds later, the 20 seconds have elapsed. Because the script is put together in the way it is, the "wait $SLEEP_PID" is triggered immediately, and the loop is killed via the trap which was set for it. The trap is needed in case the script is interrupted, but it also neatly kills the loop for us on a normal completion, so there is no need to kill the loop even on a clean exit.

Tying it all up

At the top of the article, it was suggested to use this in tandem with another command; here is an example of this in use. After the sleep has finished, it does a curl to inspect the HTTP headers from www.shellscript.sh:

another sleepy example

 

 


You can buy the content of this tutorial as a PDF to download to all of your devices!

Contact

You can mail me with this form. If you expect a reply, please ensure that the address you specify is valid. Don't forget to include the simple addition question at the end of the form, to prove that you are a real person!