1. Overview

The sleep command in Linux may seem very basic at first since its function is to delay the execution of scripts or commands for a specified amount of time. However, when incorporated into more complex scripts or used in combination with other commands, sleep can become a useful tool for managing the timing and synchronization of processes.

sleep is already included by default in all standard Linux distributions as part of the GNU Core Utilities package, so we don’t need to install it separately.

In this tutorial, we’ll explore several scenarios where we can use sleep to improve script execution and automate task management.

2. Basic Usage of sleep

The sleep command accepts time durations as arguments, indicated by suffixes such as s for seconds, m for minutes, h for hours, and d for days. sleep treats numbers without suffixes as seconds, and if we specify multiple time parameters, they are summed:

$ sleep 1m 30s

In this basic example, sleep runs for 1 minute and 30 seconds, then exits with exit code 0. During this time, it does nothing, doesn’t load CPU or RAM, and doesn’t return any output when it ends.

The manual also mentions the inf or infinity option to sleep indefinitely.

2.1. while Loops

In basic Bash loops like while and for, sleep ensures that we perform tasks with intentional delays.

For example, let’s consider a scenario where we need to check if a host is reachable every minute:

#!/bin/bash

while true
do
    ping -c 1 -W 5 baeldung.com &> /dev/null && \
    echo "OK: Host reachable" || \
    echo "WARNING: Host NOT reachable"

    echo "Check complete, waiting for one minute..."
    sleep 1m
done

This script uses a while loop to ping baeldung.com once a minute, with a timeout of 5 seconds, and prints the appropriate output.

2.2. for Loops

If we need to process a list of files with a pause between each file to avoid high resource usage, we can use a for loop:

#!/bin/bash

search_string="Critical Error"
log_directory="/var/log"

echo "Starting to analyze log files for '$search_string' in $log_directory..."

for file in $log_directory/*.log
do
    echo "Analyzing file $file for occurrences of '$search_string'..."

    # Perform the search and count how many times the string appears in the file
    match_count=$(grep -c "$search_string" "$file")
    
    if [ "$match_count" -ne 0 ]; then
        echo "Found $match_count occurrences of '$search_string' in $file."
    else
        echo "No occurrences found in $file."
    fi
    
    echo "Analysis of $file completed, sleeping for 30 seconds..."
    sleep 30s  # Pause to reduce CPU and disk load
done

echo "Log file analysis completed."

This script searches for a specific error string in all .log files in the /var/log directory, prints the results, and then waits 30 seconds to reduce CPU and disk usage. This sleep is useful when the files to analyze are very large, when other processes are consuming resources, or when we’re using a machine with limited hardware.

2.3. Using and Interrupting sleep infinity

First, sleep inf and sleep infinity are the same command. It creates an infinite sleep duration within a script:

#!/bin/bash
echo "Script will now sleep indefinitely until interrupted."
sleep inf

We can run such a script in the foreground and kill it with CTRL+C, or we can run it in the background and kill it knowing its PID (process ID):

$ ./example.sh   # Foreground execution
Script will now sleep indefinitely until interrupted.
^C

$ ./example.sh & # Background execution
[1] 24901
$ Script will now sleep indefinitely until interrupted.
$ kill 24901

In practical scenarios, sleep infinity is often used to keep a Docker container running indefinitely, which is especially useful during development and testing phases.

2.4. Trapping SIGINT or SIGTERM When Sleeping

Let’s say we want to use sleep inf to create a script that waits to perform certain actions when it receives a signal such as SIGINT or SIGTERM. This may seem intuitive, but it’s not, because trap doesn’t interrupt sleep. Let’s give it a try, saving this script as trapSignals.sh:

#!/bin/bash
trap 'echo "SIGINT trapped"; exit 0' SIGINT
trap 'echo "SIGTERM trapped"; exit 0' SIGTERM
sleep inf

Let’s run it in the background and then use kill to send signals:

$ ./trapSignals.sh &
[1] 10645
$ kill -s SIGINT 10645
$ kill -s SIGTERM 10645
$ ps -A | grep trapSignals.sh
  10645 pts/0    00:00:00 trapSignals.sh
$ kill -s SIGKILL 10645
$ ps -A | grep trapSignals.sh
[1]+  Killed                  ./trapSignals.sh

This example shows that SIGINT and SIGTERM do nothing because trap is waiting for sleep to end. So we had to send SIGKILL to kill the script.

We can read the official Signals and Error Handling documentation to understand that the only workaround for exiting sleep inf via trap is to use wait in this way:

#!/bin/bash
trap 'echo "SIGINT trapped"; exit 0' SIGINT
trap 'echo "SIGTERM trapped"; exit 0' SIGTERM
sleep inf &
wait $! # Waits for the completion of the sleep command executed in background

Now let’s see if it works:

$ ./trapSignals.sh &
[1] 12523
$ kill -s SIGINT 12523
SIGINT trapped

$ ./trapSignals.sh &
[2] 12530
[1]   Done                    ./trapSignals.sh
$ kill -s SIGTERM 12530
SIGTERM trapped

$ ps -A | grep trapSignals.sh
[2]+  Done                    ./trapSignals.sh

This time it worked as planned. Beyond this minimal example, we can ask trap to execute the Bash functions we want.

3. Real-World Applications

Now, let’s look at examples to illustrate how we can use sleep strategically to meet specific needs, from server maintenance to system monitoring, and demonstrate its utility beyond mere delay tactics.

3.1. Email Notification of Available Updates

This script checks for available updates on a Debian-based system every four hours and notifies the system administrator via email if there are available updates. It requires root privileges and msmtp already installed and configured. Of course, we need to replace [email protected] with our actual email address:

#!/bin/bash
echo "Checking for system updates every 4 hours..."

while true; do
    # Updates the package list
    apt update

    # Checks if there are upgradable packages
    updates=$(apt list --upgradable | wc -l)

    # If there are more than 1 line of output, updates are available
    if [ "$updates" -gt 1 ]; then
        echo "There are updates available. Sending an email notification."
        # Prepares the email message
        echo -e "Subject: Update Available\n\nThere are updates available for your system. Please review and apply them." | \
        msmtp [email protected]
    else
        echo "Your system is up to date."
    fi

    # Delays the next update check by 4 hours
    sleep 4h
done

This script ensures that the system periodically checks for updates without manual monitoring.

3.2. VPN Autoreconnect

The Network Manager of modern Linux distributions doesn’t have an automatic VPN reconnection system yet. As a workaround, we can use a repeated check every second. The CPU and RAM load is negligible:

#!/bin/bash

# Configuration
vpnName="insert-here-vpn-name"

# Last message variable
last_message=""

# Function to log messages with a timestamp, only if the message has changed
log_message() {
    if [[ "$1" != "$last_message" ]]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
        last_message=$1
    fi
}

# Main loop
while true; do
    # Check VPN status
    if nmcli con show --active | grep -q "$vpnName"; then
        log_message "VPN $vpnName is connected."
    else
        log_message "VPN $vpnName disconnected, attempting to reconnect..."
        if nmcli con up id "$vpnName"; then
            log_message "Reconnected to VPN $vpnName successfully."
        else
            log_message "Failed to reconnect to VPN $vpnName."
        fi
    fi

    # Wait before checking again
    sleep 1
done

Of course, let’s not forget to replace insert-here-vpn-name with the name of the VPN connection as set up in the Network Manager.

3.3. Dynamic sleep Adjustment Based on System Load

In environments where workloads can vary dramatically, using static sleep intervals may not always be optimal. Dynamically adjusting sleep durations based on the current system load, as measured by uptime, can result in more efficient operations.

This script is designed for systems with single-core CPUs, where a load average of 1.5 is already significantly high, indicating that the system is 50% overloaded:

#!/bin/bash
# Script to adaptively manage task frequency based on system load
while true; do
    # Extract the load average for the last 5 minutes robustly
    load=$(uptime | awk -F'load average: ' '{print $2}' | awk -F', ' '{print $2}')
    
    # Convert comma to decimal if needed (for locales that use commas)
    load=$(echo $load | tr ',' '.')

    if (( $(echo "$load > 1.5" | bc) )); then
        echo "High load detected. Increasing delay..."
        sleep_time=10m
    else
        sleep_time=5m
    fi
    ./perform_task.sh
    echo "Task completed. Next run in $sleep_time."
    sleep $sleep_time
done

Of course, we need to replace ./perform_task.sh with the command or script we want to run.

4. Alternative Tools

The sleep command is a simple method for inserting delays, but for more structured scheduling, Linux offers tools tailored to different scheduling needs:

  • at → precise timing
  • batch → load-dependent execution
  • cron → periodic tasks
  • systemd → sophisticated event-based scheduling

The at command schedules a one-time task to run at a specific time in the future. It’s ideal for tasks that need to be run at a specific time, but not repeated:

$ echo "Running at command" | at 17:01

The batch command executes tasks when system load levels permit, optimizing server performance:

$ echo "Running batch command" | batch

The cron command is used to schedule tasks to run at regular intervals. For example, this crontab entry would cause the system to print Hello from cron every minute:

* * * * * echo "Hello from cron"

systemd timers provide a robust and flexible alternative for scheduling tasks. They function similarly to a cron job, but offer more flexibility and integration with the system’s startup and maintenance processes. Anyway, we won’t go into them because the discussion would be long.

5. Conclusion

In this article, we’ve learned about the various uses and benefits of the Linux sleep command. From simple task delays in scripts to efficient management of system resources, sleep proves to be an invaluable tool for any system administrator or script writer.

In addition, the practical examples we provided illustrate how sleep can be integrated into real-world scenarios to improve performance and functionality. From ensuring that system updates are handled smoothly to automatically managing VPN connections, the use of sleep goes beyond mere pausing and serves as a key component in automated and scheduled operations.

Our examples serve not only to demonstrate the utility of sleep, but also to inspire creative ways to incorporate it into our scripts.