Używanie polecenia `sleep` w skryptach Bash na Linux
Polecenie `sleep` w Linux wstrzymuje wykonywanie skryptu na precyzyjnie określony czas — podany w sekundach, minutach, godzinach lub dniach — przy użyciu składni `sleep [NUMBER][SUFFIX]`. Jest to jedno z najbardziej krytycznych operacyjnie prymitywów w skryptach Bash, umożliwiające ograniczanie częstotliwości żądań, logikę ponownych prób, synchronizację procesów i automatyzację czasową bez konieczności korzystania z zewnętrznych harmonogramów.
W przeciwieństwie do cron lub `at`, `sleep` działa całkowicie w kontekście procesu własnego skryptu, co czyni go właściwym narzędziem, gdy opóźnienie musi być względne wobec zakończenia poprzedniego polecenia, a nie bezwzględnego czasu zegarowego.
Składnia i odniesienie do jednostek czasu
“`bash
sleep NUMBER[SUFFIX]
“`
| Sufiks | Jednostka | Przykład | Odpowiednik w sekundach |
|---|
| ——– | ——— | —————- | ———————– |
|---|
| `s` | Sekundy | `sleep 30s` | 30 |
|---|
| `m` | Minuty | `sleep 5m` | 300 |
|---|
| `h` | Godziny | `sleep 2h` | 7200 |
|---|
| `d` | Dni | `sleep 1d` | 86400 |
|---|
| (brak) | Sekundy | `sleep 10` | 10 |
|---|
Sufiks jest opcjonalny. Gdy zostanie pominięty, jednostka domyślnie przyjmuje wartość sekund. W systemach GNU/Linux (GNU coreutils), `sleep` akceptuje również wartości zmiennoprzecinkowe i wiele argumentów — funkcja niedostępna w implementacjach BSD i macOS, chyba że GNU coreutils jest zainstalowane przez Homebrew.
“`bash
GNU coreutils: both of these are valid
sleep 1.5
sleep 1m 30s # Equivalent to 90 seconds
“`
Krytyczna uwaga dotycząca przenośności: POSIX wymaga jedynie całkowitoliczbowych sekund bez sufiksu. Jeśli skrypt musi działać na Alpine Linux (BusyBox), macOS lub AIX, ogranicz się do `sleep INTEGER` i unikaj łączenia wielu argumentów.
Podstawowe przypadki użycia w skryptach Bash
1. Sekwencyjne opóźnienie między poleceniami
Najprostsze zastosowanie — wstawianie pauzy między dwiema operacjami, gdy drugie polecenie nie może się rozpocząć, dopóki warunek rzeczywisty nie zdąży się ustabilizować:
“`bash
#!/bin/bash
echo "Restarting nginx…"
systemctl restart nginx
sleep 3
systemctl status nginx
“`
Pauza 3-sekundowa uwzględnia tutaj asynchroniczną sekwencję uruchamiania menedżera usług. Bez niej `status` może zgłosić nieaktualny stan przechwycony przed pełną inicjalizacją procesu.
2. Pętla odpytywania z wykładniczym wycofaniem
Naiwna pętla ponownych prób ze stałym interwałem marnuje zasoby i może zwiększać obciążenie przeciążonej usługi downstream. Właściwym wzorcem jest wykładnicze wycofanie z jitterem:
“`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
“`
Czas oczekiwania podwaja się przy każdym niepowodzeniu: 2s, 4s, 8s, 16s, 32s, 64s. Całkowity maksymalny czas oczekiwania przed rezygnacją wynosi 126 sekund. Ten wzorzec jest standardem w skryptach wdrożeń produkcyjnych, kontrolach stanu i potokach CI/CD.
3. Wywołania API z ograniczeniem częstotliwości
Podczas interakcji z API wymuszającymi limity żądań, `sleep` egzekwuje wymagany interwał między żądaniami:
“`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. Wykonywanie zadań w tle z opóźnieniem czasowym
Uruchomienie opóźnionego polecenia bez blokowania bieżącej sesji powłoki wymaga połączenia `sleep` z uruchamianiem podpowłoki w tle:
“`bash
Trigger a cache flush 60 seconds after deployment completes
( sleep 60 && redis-cli FLUSHDB ) &
echo "Cache flush scheduled. PID: $!"
“`
Zmienna `$!` przechwytuje PID podpowłoki działającej w tle, który można później użyć z `wait` lub `kill`, jeśli zadanie wymaga anulowania.
5. Pętla watchdog i monitora procesów
“`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
“`
Ten wzorzec jest używany w lekkim nadzorze procesów, gdy pełny demon nadzorcy (systemd, supervisord, s6) jest niedostępny lub nieodpowiedni — powszechne w środowiskach kontenerowych lub minimalnych instancjach VPS Hosting.
6. Odliczanie z informacją zwrotną dla użytkownika
W przypadku skryptów interaktywnych, gdzie operator potrzebuje wglądu w pozostały czas oczekiwania:
“`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"` nadpisuje bieżącą linię zamiast dołączać nowe linie, tworząc czyste odliczanie w terminalu.
`sleep` a alternatywne mechanizmy czasowe
| Mechanizm | Granularność | Blokuje powłokę | Czas bezwzględny | Przypadek użycia |
|---|
| —————– | —————– | ————– | ————— | ———————————————– |
|---|
| `sleep` | Poniżej sekundy (GNU) | Tak (chyba że `&`) | Nie | Względne opóźnienia w skryptach |
|---|
| `cron` | 1 minuta | Nie | Tak | Cykliczne zaplanowane zadania |
|---|
| `at` | 1 minuta | Nie | Tak | Jednorazowe przyszłe wykonanie |
|---|
| `systemd timer` | 1 sekunda | Nie | Tak | Trwałe, rejestrowane zadania z obsługą zależności |
|---|
| `usleep` (C) | Mikrosekunda | Tak | Nie | Precyzja na poziomie jądra/C (nie natywna dla Bash) |
|---|
| `read -t` | Poniżej sekundy | Tak | Nie | Limit czasu z opcjonalnym wejściem użytkownika |
|---|
Kiedy używać `read -t` zamiast `sleep`: Jeśli skrypt musi się zatrzymać, ale jednocześnie umożliwić użytkownikowi przerwanie lub odpowiedź podczas oczekiwania, `read -t SECONDS` jest właściwym prymitywem. Zwraca kod wyjścia 1 po upływie limitu czasu i 0, gdy użytkownik naciśnie Enter, dając logikę warunkową bez osobnego procesu.
“`bash
echo "Press Enter to skip the 10-second wait, or wait for automatic continuation."
read -t 10 -r || true
echo "Continuing…"
“`
Precyzja, wartości zmiennoprzecinkowe i zachowanie na różnych platformach
GNU `sleep` akceptuje ułamki dziesiętne, co ma znaczenie w skryptach sterujących animacjami, ograniczających śledzenie logów lub symulujących kanały danych w czasie rzeczywistym:
“`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
“`
Rzeczywisty czas uśpienia jest wartością minimalną, nie gwarancją. Harmonogram jądra może obudzić proces nieco później w zależności od obciążenia systemu i rozdzielczości timera (`CONFIG_HZ`). Na mocno obciążonym Serwerze Dedykowanym obsługującym dziesiątki równoległych procesów, `sleep 0.1` może faktycznie wstrzymać działanie na 0,11–0,15 sekundy. W przypadku skryptów, gdzie taki dryft jest niedopuszczalny, należy użyć odwołania do zegara monotonicznego:
“`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
“`
Ta pętla kompensująca dryft utrzymuje stały interwał niezależnie od tego, jak długo trwa `do_work`.
Obsługa sygnałów i przerywanie `sleep`
Działający proces `sleep` reaguje na sygnały. Wysłanie `SIGALRM` do procesu sleep natychmiast go budzi. Bardziej praktycznie, naciśnięcie `Ctrl+C` wysyła `SIGINT` do całej grupy procesów, kończąc zarówno skrypt, jak i każdy działający na pierwszym planie `sleep`.
Aby skrypt czysto obsługiwał przerwanie podczas uśpienia:
“`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."
“`
Poprzez uruchomienie `sleep` w tle i użycie `wait`, `trap` uruchamia się natychmiast po `SIGINT`, zamiast być odroczonym do zakończenia uśpienia. Jest to właściwy wzorzec dla długo działających skryptów automatyzacji na serwerach produkcyjnych.
Praktyczne pułapki i przypadki brzegowe
Pułapka 1: Używanie `sleep` w ciasnych pętlach bez warunku zakończenia. Pętla `while true; do sleep 1; done` bez ścieżki wyjścia będzie działać w nieskończoność, zajmując slot procesu i kumulując się w wynikach `ps`. Zawsze definiuj maksymalną liczbę iteracji lub warunek sentinel.
Pułapka 2: Zakładanie, że `sleep` jest synchroniczne z podpowłokami. Gdy rozgałęziasz podpowłokę za pomocą `&`, skrypt nadrzędny nie czeka na zakończenie `sleep` podpowłoki, chyba że jawnie wywołasz `wait`. Powoduje to wyścigi w równoległych skryptach wdrożeniowych.
Pułapka 3: Hardkodowanie opóźnień dla gotowości usługi. Używanie `sleep 5` po uruchomieniu usługi jest zawodne. Usługa może być gotowa w 1 sekundę lub może potrzebować 30 sekund pod obciążeniem. Solidną alternatywą jest odpytywanie gotowości:
“`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."
“`
Pułapka 4: Zmiennoprzecinkowe uśpienie w systemach BusyBox. Kontenery Alpine Linux używają implementacji `sleep` BusyBox, która nie obsługuje wartości dziesiętnych. Próba użycia `sleep 0.5` spowoduje błąd. Sprawdź swoje środowisko przed wdrożeniem skryptów korzystających z precyzji poniżej sekundy.
Integracja `sleep` w zautomatyzowanych przepływach pracy serwera
Na zarządzanym VPS z cPanel, zautomatyzowane skrypty konserwacyjne często łączą `sleep` z cron w celu implementacji harmonogramowania poniżej minuty. Ponieważ minimalna rozdzielczość cron wynosi jedną minutę, można osiągnąć interwały 15-sekundowe w następujący sposób:
“`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
“`
Technika ta jest powszechnie stosowana w procesorach kolejek, kontrolach stanu i zbieraczach metryk na współdzielonej infrastrukturze, gdzie instalacja dedykowanego harmonogramu zadań nie jest dozwolona.
W skryptach odnawiania certyfikatów SSL, `sleep` zapewnia opóźnienie między próbami, gdy propagacja wyzwania ACME wymaga wygaśnięcia DNS TTL przed weryfikacją własności przez CA. Jeśli zarządzasz certyfikatami na własnej infrastrukturze, Certyfikaty SSL z automatycznymi potokami odnawiania korzystają z precyzyjnie dostrojonych interwałów ponownych prób.
Podobnie, skrypty weryfikacji propagacji domeny — przydatne po aktualizacji rekordów przez Rejestrację Domen — używają pętli `sleep` do odpytywania resolverów DNS w interwałach dostosowanych do oczekiwanych wartości TTL.
Macierz decyzyjna: wybór właściwej strategii opóźnienia
| Scenariusz | Zalecane podejście |
|---|
| ———————————————– | ————————————————— |
|---|
| Stała pauza między dwoma sekwencyjnymi poleceniami | `sleep N` |
|---|
| Ponowne próby do sukcesu, unikanie efektu thundering herd | Wykładnicze wycofanie z `sleep` |
|---|
| Cykliczne zadanie co N minut | `cron` (nie `sleep`) |
|---|
| Cykliczne zadanie poniżej minuty | `cron` + przesunięcie `sleep` |
|---|
| Opóźnienie bez blokowania terminala | `( sleep N && command ) &` |
|---|
| Pauza z możliwością przerwania przez użytkownika | `sleep N &` + `wait $!` + `trap` |
|---|
| Sprawdzanie gotowości usługi | Pętla odpytywania portu/stanu z `sleep 1` na próbę |
|---|
| Interwał wysokiej precyzji (kompensacja dryftu) | Odwołanie do zegara monotonicznego z obliczonym `sleep` |
|---|
| Opóźnienie poniżej sekundy na Alpine/BusyBox | Unikaj; używaj całkowitoliczbowych sekund lub zmień obraz bazowy |
|---|
Kluczowe wnioski techniczne
- Zawsze używaj `sleep "$VARIABLE"` z podwójnymi cudzysłowami, aby zapobiec błędom podziału słów, gdy zmienna zawiera wartość dziesiętną.
- Preferuj `sleep 1m 30s` nad `sleep 90` dla czytelności w długo działających skryptach konserwacyjnych.
- Uruchamiaj `sleep` w tle z `wait` i `trap` zawsze, gdy skrypt musi reagować na sygnały podczas pauzy.
- Nigdy nie używaj hardkodowanego `sleep` jako substytutu właściwej kontroli gotowości — używaj pętli odpytywania z limitem czasu.
- Sprawdzaj zachowanie `sleep` na docelowym systemie operacyjnym przed wdrożeniem skryptów używających wartości zmiennoprzecinkowych lub składni z wieloma argumentami.
- Na serwerach produkcyjnych rejestruj znacznik czasu przed i po długich wywołaniach `sleep`, aby wykryć dryft harmonogramu w analizie po incydencie.
- Podczas budowania automatyzacji na Panelach Sterowania VPS, sprawdź, czy harmonogram zadań panelu już zapewnia kontrolę interwałów, zanim ręcznie dodasz logikę `sleep`.
FAQ
Czy `sleep` zużywa CPU podczas oczekiwania?
Nie. `sleep` wywołuje `nanosleep()` (lub odpowiednik) na poziomie jądra, umieszczając proces w stanie przerywalnego uśpienia (`S` w wynikach `ps`). Nie zużywa żadnych cykli CPU podczas oczekiwania — jedynie niewielką ilość pamięci dla wpisu procesu w tablicy procesów.
Jaka jest maksymalna wartość akceptowana przez `sleep`?
W GNU/Linux, `sleep` akceptuje wartości do limitów liczby zmiennoprzecinkowej `double`, co jest praktycznie nieograniczone dla celów praktycznych. `sleep 1d` (86400 sekund) jest powszechne; `sleep 365d` jest prawidłowe. Praktycznym limitem jest czas działania systemu.
Dlaczego `sleep 0.5` nie działa w moim kontenerze Docker?
Alpine Linux używa BusyBox, którego implementacja `sleep` akceptuje tylko całkowitoliczbowe sekundy. Przejdź na obraz bazowy Debian lub Ubuntu, lub zainstaluj GNU coreutils (`apk add coreutils`), aby włączyć obsługę wartości dziesiętnych.
Czy mogę anulować działający w tle proces `sleep`?
Tak. Przechwyć jego PID za pomocą `SLEEP_PID=$!` natychmiast po uruchomieniu w tle, a następnie użyj `kill "$SLEEP_PID"`, aby go zakończyć. Jeśli użyłeś `( sleep N && command ) &`, zabicie PID podpowłoki uniemożliwi również uruchomienie kolejnego polecenia.
Czy `sleep` jest bezpieczne do użycia w skrypcie `ExecStart` jednostki usługi `systemd`?
Tak, ale z zastrzeżeniami. Jeśli jednostka usługi ma ustawione `TimeoutStartSec`, długie `sleep` podczas uruchamiania spowoduje, że systemd zakończy usługę jako nieudany start. W przypadku opóźnień po uruchomieniu, użyj `ExecStartPost` z odpytywaniem gotowości lub skonfiguruj `Type=forking` z właściwym zarządzaniem plikiem PID, zamiast polegać na `sleep` do odroczenia inicjalizacji.
