Використання команди `sleep` у Bash-скриптах на Linux
Команда `sleep` у Linux призупиняє виконання скрипту на точно визначений проміжок часу — вказаний у секундах, хвилинах, годинах або днях — використовуючи синтаксис `sleep [NUMBER][SUFFIX]`. Це один із найважливіших примітивів у Bash-скриптингу, що дозволяє реалізовувати обмеження частоти запитів, логіку повторних спроб, синхронізацію процесів і автоматизацію за розкладом без використання зовнішніх планувальників.
На відміну від cron або `at`, `sleep` працює повністю в контексті власного процесу скрипту, що робить його правильним інструментом, коли затримка має бути відносною до завершення попередньої команди, а не абсолютного астрономічного часу.
Синтаксис і довідник одиниць часу
“`bash
sleep NUMBER[SUFFIX]
“`
| Суфікс | Одиниця | Приклад | Еквівалент у секундах |
|---|
| ——– | ——— | —————- | ———————– |
|---|
| `s` | Секунди | `sleep 30s` | 30 |
|---|
| `m` | Хвилини | `sleep 5m` | 300 |
|---|
| `h` | Години | `sleep 2h` | 7200 |
|---|
| `d` | Дні | `sleep 1d` | 86400 |
|---|
| (немає) | Секунди | `sleep 10` | 10 |
|---|
Суфікс є необов’язковим. Якщо його не вказано, одиницею за замовчуванням є секунди. У GNU/Linux системах (GNU coreutils) `sleep` також приймає значення з плаваючою комою та кілька аргументів — можливість, відсутня в реалізаціях BSD і macOS, якщо GNU coreutils не встановлено через Homebrew.
“`bash
GNU coreutils: both of these are valid
sleep 1.5
sleep 1m 30s # Equivalent to 90 seconds
“`
Важлива примітка щодо переносимості: POSIX вимагає лише цілих секунд без суфікса. Якщо ваш скрипт має працювати на Alpine Linux (BusyBox), macOS або AIX, обмежтеся `sleep INTEGER` та уникайте об’єднання кількох аргументів.
Основні випадки використання в Bash-скриптах
1. Послідовна затримка між командами
Найпростіше застосування — вставка паузи між двома операціями, де друга команда не повинна починатися, доки реальна умова не встигне стабілізуватися:
“`bash
#!/bin/bash
echo "Restarting nginx…"
systemctl restart nginx
sleep 3
systemctl status nginx
“`
Пауза в 3 секунди тут враховує асинхронну послідовність запуску менеджера служб. Без неї `status` може повернути застарілий стан, зафіксований до повної ініціалізації процесу.
2. Цикл опитування з експоненційним відступом
Наївний цикл повторних спроб із фіксованим інтервалом марнує ресурси та може збільшити навантаження на проблемний сервіс нижче за потоком. Правильний шаблон — це експоненційний відступ із джитером:
“`bash
#!/bin/bash
MAX_RETRIES=6
DELAY=2
for (( attempt=1; attempt<=MAX_RETRIES; attempt++ )); do
if curl -sf https://api.example.com/health > /dev/null; then
echo "Service healthy on attempt $attempt."
exit 0
fi
echo "Attempt $attempt failed. Retrying in ${DELAY}s…"
sleep "$DELAY"
DELAY=$(( DELAY * 2 ))
done
echo "Service unreachable after $MAX_RETRIES attempts." >&2
exit 1
“`
Це подвоює час очікування при кожній невдачі: 2с, 4с, 8с, 16с, 32с, 64с. Загальний максимальний час очікування перед відмовою становить 126 секунд. Цей шаблон є стандартним у скриптах розгортання на виробничих серверах, перевірках стану та CI/CD-конвеєрах.
3. API-виклики з обмеженням частоти запитів
При взаємодії з API, що застосовують квоти запитів, `sleep` забезпечує необхідний інтервал між запитами:
“`bash
#!/bin/bash
API_KEY="your_key_here"
ENDPOINTS=("users" "orders" "products" "inventory")
for endpoint in "${ENDPOINTS[@]}"; do
curl -s -H "Authorization: Bearer $API_KEY"
"https://api.example.com/v1/${endpoint}"
-o "${endpoint}.json"
echo "Fetched: $endpoint"
sleep 1 # Respect 1 req/sec rate limit
done
“`
4. Виконання фонових завдань за таймером
Запуск відкладеної команди без блокування поточного сеансу оболонки вимагає поєднання `sleep` з фоновим виконанням підоболонки:
“`bash
Trigger a cache flush 60 seconds after deployment completes
( sleep 60 && redis-cli FLUSHDB ) &
echo "Cache flush scheduled. PID: $!"
“`
Змінна `$!` зберігає PID фонової підоболонки, який пізніше можна використати з `wait` або `kill`, якщо завдання потрібно скасувати.
5. Цикл сторожового таймера та моніторингу процесів
“`bash
#!/bin/bash
SERVICE="mysqld"
CHECK_INTERVAL=30
while true; do
if ! pgrep -x "$SERVICE" > /dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') $SERVICE not running. Restarting…"
>> /var/log/watchdog.log
systemctl start "$SERVICE"
fi
sleep "$CHECK_INTERVAL"
done
“`
Цей шаблон використовується для легкого нагляду за процесами, коли повноцінний демон-супервізор (systemd, supervisord, s6) недоступний або недоречний — що є типовим у контейнеризованих середовищах або мінімальних екземплярах VPS Хостингу.
6. Таймер зворотного відліку зі зворотним зв’язком для користувача
Для інтерактивних скриптів, де оператору потрібна інформація про час, що залишився:
“`bash
#!/bin/bash
COUNTDOWN=10
echo "Starting in:"
for (( i=COUNTDOWN; i>=1; i– )); do
printf "r%2d seconds remaining…" "$i"
sleep 1
done
printf "rGo! n"
“`
`printf "r"` перезаписує поточний рядок замість додавання нових рядків, створюючи чистий відлік у терміналі.
`sleep` порівняно з альтернативними механізмами синхронізації
| Механізм | Гранулярність | Блокує оболонку | Абсолютний час | Випадок використання |
|---|
| —————– | —————– | ————– | ————— | ———————————————– |
|---|
| `sleep` | Субсекундна (GNU) | Так (якщо не `&`) | Ні | Відносні затримки всередині скриптів |
|---|
| `cron` | 1 хвилина | Ні | Так | Повторювані заплановані завдання |
|---|
| `at` | 1 хвилина | Ні | Так | Одноразове виконання в майбутньому |
|---|
| `systemd timer` | 1 секунда | Ні | Так | Постійні, журнальовані завдання з урахуванням залежностей |
|---|
| `usleep` (C) | Мікросекунда | Так | Ні | Точність на рівні ядра/C (не є нативним для Bash) |
|---|
| `read -t` | Субсекундна | Так | Ні | Тайм-аут із можливим введенням від користувача |
|---|
Коли використовувати `read -t` замість `sleep`: Якщо вашому скрипту потрібно зробити паузу, але також дозволити користувачу перервати або відповісти під час очікування, `read -t SECONDS` є правильним примітивом. Він повертає код виходу 1 при тайм-ауті та 0, якщо користувач натискає Enter, надаючи умовну логіку без окремого процесу.
“`bash
echo "Press Enter to skip the 10-second wait, or wait for automatic continuation."
read -t 10 -r || true
echo "Continuing…"
“`
Точність, числа з плаваючою комою та поведінка на різних платформах
GNU `sleep` приймає десяткові дроби, що важливо в скриптах, які керують анімацією, обмежують перегляд журналів або імітують потоки даних у реальному часі:
“`bash
Tail a log file and print one line per 0.2 seconds (5 lines/sec)
while IFS= read -r line; do
echo "$line"
sleep 0.2
done < /var/log/app.log
“`
Фактична тривалість сну є мінімальною, а не гарантованою. Планувальник ядра може пробудити процес із невеликим запізненням залежно від навантаження системи та роздільної здатності таймера (`CONFIG_HZ`). На сильно завантаженому Виділеному Сервері, що виконує десятки паралельних процесів, `sleep 0.1` може фактично призупинитися на 0,11–0,15 секунди. Для скриптів, де таке відхилення неприйнятне, використовуйте посилання на монотонний годинник:
“`bash
#!/bin/bash
INTERVAL=5
NEXT=$(date +%s%N) # Current time in nanoseconds
while true; do
NEXT=$(( NEXT + INTERVAL * 1000000000 ))
do_work
NOW=$(date +%s%N)
REMAINING=$(( (NEXT – NOW) / 1000000 )) # Convert to milliseconds
[ "$REMAINING" -gt 0 ] && sleep "$(echo "scale=3; $REMAINING/1000" | bc)"
done
“`
Цей цикл із компенсацією відхилення підтримує стабільний інтервал незалежно від того, скільки часу займає `do_work`.
Обробка сигналів та переривання `sleep`
Запущений процес `sleep` реагує на сигнали. Надсилання `SIGALRM` процесу sleep негайно пробуджує його. Практичніше: натискання `Ctrl+C` надсилає `SIGINT` всій групі процесів, завершуючи як скрипт, так і будь-який `sleep` на передньому плані.
Щоб скрипт коректно обробляв переривання під час сну:
“`bash
#!/bin/bash
cleanup() {
echo "Interrupted. Cleaning up…"
exit 1
}
trap cleanup SIGINT SIGTERM
echo "Waiting 60 seconds…"
sleep 60 &
SLEEP_PID=$!
wait "$SLEEP_PID"
echo "Wait complete."
“`
Виконуючи `sleep` у фоні та використовуючи `wait`, `trap` спрацьовує негайно при `SIGINT`, а не відкладається до завершення сну. Це правильний шаблон для довготривалих скриптів автоматизації на виробничих серверах.
Практичні підводні камені та граничні випадки
Підводний камінь 1: Використання `sleep` у щільних циклах без умови завершення. Цикл `while true; do sleep 1; done` без шляху виходу виконуватиметься нескінченно, займаючи слот процесу та накопичуючись у виводі `ps`. Завжди визначайте максимальну кількість ітерацій або сигнальну умову.
Підводний камінь 2: Припущення, що `sleep` є синхронним із підоболонками. Коли ви розгалужуєте підоболонку за допомогою `&`, батьківський скрипт не чекає завершення `sleep` підоболонки, якщо ви явно не викликаєте `wait`. Це спричиняє стани гонки в паралельних скриптах розгортання.
Підводний камінь 3: Жорстке кодування затримок для готовності служби. Використання `sleep 5` після запуску служби є ненадійним. Служба може бути готова за 1 секунду або може зайняти 30 секунд під навантаженням. Надійною альтернативою є опитування готовності:
“`bash
#!/bin/bash
wait_for_port() {
local host="$1" port="$2" timeout="${3:-30}"
local elapsed=0
until nc -z "$host" "$port" 2>/dev/null; do
[ "$elapsed" -ge "$timeout" ] && return 1
sleep 1
(( elapsed++ ))
done
}
systemctl start postgresql
wait_for_port localhost 5432 30 && echo "PostgreSQL ready."
“`
Підводний камінь 4: Sleep із плаваючою комою на системах BusyBox. Контейнери Alpine Linux використовують `sleep` від BusyBox, який не підтримує десяткові числа. Спроба виконати `sleep 0.5` призведе до помилки. Перевіряйте своє середовище перед розгортанням скриптів, що покладаються на субсекундну точність.
Інтеграція `sleep` в автоматизовані серверні робочі процеси
На керованому VPS з cPanel, скрипти автоматизованого обслуговування часто поєднують `sleep` з cron для реалізації субхвилинного планування. Оскільки мінімальна роздільна здатність cron становить одну хвилину, ви можете досягти 15-секундних інтервалів таким чином:
“`bash
crontab entry — runs the script 4 times per minute
- * * * * /usr/local/bin/check_queue.sh
- * * * * sleep 15 && /usr/local/bin/check_queue.sh
- * * * * sleep 30 && /usr/local/bin/check_queue.sh
- * * * * sleep 45 && /usr/local/bin/check_queue.sh
“`
Ця техніка широко використовується для обробників черг, перевірок стану та збирачів метрик на спільній інфраструктурі, де встановлення спеціального планувальника завдань не дозволено.
Для скриптів оновлення SSL-сертифікатів `sleep` забезпечує затримку між спробами, коли поширення ACME-виклику вимагає закінчення DNS TTL перед тим, як CA зможе підтвердити право власності. Якщо ви керуєте сертифікатами на власній інфраструктурі, SSL-сертифікати з автоматизованими конвеєрами оновлення виграють від точно налаштованих інтервалів повторних спроб.
Аналогічно, скрипти перевірки поширення домену — корисні після оновлення записів через Реєстрацію доменів — використовують цикли `sleep` для опитування DNS-резолверів з інтервалами, узгодженими з очікуваними значеннями TTL.
Матриця рішень: вибір правильної стратегії затримки
| Сценарій | Рекомендований підхід |
|---|
| ———————————————– | ————————————————— |
|---|
| Фіксована пауза між двома послідовними командами | `sleep N` |
|---|
| Повторні спроби до успіху, уникнення ефекту натовпу | Експоненційний відступ із `sleep` |
|---|
| Повторюване завдання кожні N хвилин | `cron` (не `sleep`) |
|---|
| Субхвилинне повторюване завдання | `cron` + трюк зі зміщенням `sleep` |
|---|
| Затримка без блокування терміналу | `( sleep N && command ) &` |
|---|
| Пауза з можливістю переривання користувачем | `sleep N &` + `wait $!` + `trap` |
|---|
| Перевірка готовності служби | Цикл опитування порту/стану з `sleep 1` на спробу |
|---|
| Високоточний інтервал (з компенсацією відхилення) | Посилання на монотонний годинник із розрахованим `sleep` |
|---|
| Субсекундна затримка на Alpine/BusyBox | Уникайте; використовуйте цілі секунди або змініть базовий образ |
|---|
Ключові технічні висновки
- Завжди використовуйте `sleep "$VARIABLE"` з подвійними лапками, щоб уникнути помилок розбиття слів, коли змінна містить десятковий роздільник.
- Надавайте перевагу `sleep 1m 30s` над `sleep 90` для читабельності в довготривалих скриптах обслуговування.
- Виконуйте `sleep` у фоні за допомогою `wait` та `trap`, коли ваш скрипт повинен реагувати на сигнали під час паузи.
- Ніколи не використовуйте жорстко закодований `sleep` як замінник належної перевірки готовності — використовуйте цикл опитування з тайм-аутом.
- Перевіряйте поведінку `sleep` на цільовій ОС перед розгортанням скриптів, що використовують синтаксис із плаваючою комою або кількома аргументами.
- На виробничих серверах записуйте мітку часу до і після тривалих викликів `sleep` для виявлення відхилень планувальника під час аналізу інцидентів.
- При побудові автоматизації на Панелях керування VPS, перевіряйте, чи вже надає планувальник завдань панелі контроль інтервалів, перш ніж додавати логіку `sleep` вручну.
FAQ
Чи споживає `sleep` CPU під час очікування?
Ні. `sleep` викликає `nanosleep()` (або еквівалент) на рівні ядра, переводячи процес у стан переривчастого сну (`S` у виводі `ps`). Він не споживає циклів CPU під час очікування — лише невелику кількість пам’яті для запису процесу в таблиці процесів.
Яке максимальне значення приймає `sleep`?
У GNU/Linux `sleep` приймає значення до меж числа з плаваючою комою `double`, що є практично необмеженим для реальних цілей. `sleep 1d` (86400 секунд) є поширеним; `sleep 365d` є допустимим. Практичним обмеженням є час роботи системи.
Чому `sleep 0.5` не працює в моєму Docker-контейнері?
Alpine Linux використовує BusyBox, реалізація `sleep` якого приймає лише цілі секунди. Перейдіть на базовий образ Debian або Ubuntu, або встановіть GNU coreutils (`apk add coreutils`) для підтримки десяткових чисел.
Чи можна скасувати фоновий процес `sleep`?
Так. Збережіть його PID за допомогою `SLEEP_PID=$!` одразу після запуску у фоні, а потім використовуйте `kill "$SLEEP_PID"` для його завершення. Якщо ви використовували `( sleep N && command ) &`, завершення PID підоболонки також запобіжить виконанню наступної команди.
Чи безпечно використовувати `sleep` всередині скрипту `systemd` сервісного юніту `ExecStart`?
Так, але з застереженнями. Якщо сервісний юніт має встановлений `TimeoutStartSec`, тривалий `sleep` під час запуску змусить systemd завершити службу як невдалий старт. Для затримок після запуску використовуйте `ExecStartPost` з опитуванням готовності, або налаштуйте `Type=forking` з належним керуванням PID-файлом, а не покладайтеся на `sleep` для відкладення ініціалізації.
