Bash if, elif, else — instrukcje warunkowe w skryptach z przykładami
Jak działa if, elif i else w Bash? Składnia, różnica między [ ] a [[ ]], operatory porównań i testy plików, łączenie warunków oraz najczęstsze błędy w skryptach.

W skrócie
- Składnia: if WARUNEK; then … elif WARUNEK; then … else … fi — elif możesz powtórzyć dowolnie wiele razy, else jest opcjonalne.
- if nie sprawdza „prawdy”, tylko kod wyjścia polecenia: 0 oznacza sukces (warunek spełniony), wszystko inne — niepowodzenie.
- W Bash używaj [[ … ]] do tekstów i plików oraz (( … )) do liczb; [ … ] zostaw do skryptów, które muszą działać w czystym sh.
- Wokół nawiasów kwadratowych obowiązkowe są spacje, a zmienne w [ … ] zawsze bierz w cudzysłów.
- Przy wielu porównaniach jednej zmiennej z konkretnymi wartościami czytelniejsze od długiego łańcucha elif jest case.
Spis treści
W Bash instrukcja warunkowa ma postać if WARUNEK; then … elif INNY_WARUNEK; then … else … fi. Bash sprawdza warunki po kolei i wykonuje blok pierwszego spełnionego — elif (skrót od „else if”) dokłada kolejny warunek, a else wykonuje się, gdy żaden nie był spełniony. Całość zawsze zamyka słowo fi.
Najważniejsza rzecz, której nie mówi większość poradników: if w Bash nie ocenia „prawdy” jak w Pythonie czy JavaScripcie. Uruchamia polecenie i patrzy na jego kod wyjścia — 0 oznacza sukces, czyli warunek spełniony. Nawiasy [ ] to też polecenie. Kiedy to zrozumiesz, większość dziwnych błędów przestaje być zagadką.
Składnia if, elif i else w Bash
Pełna konstrukcja wygląda tak:
if WARUNEK_1; then
polecenia gdy WARUNEK_1 spełniony
elif WARUNEK_2; then
polecenia gdy WARUNEK_2 spełniony
else
polecenia gdy żaden warunek nie jest spełniony
fi
Zasady, które warto zapamiętać:
thenmoże być w tej samej linii po średniku albo w nowej linii — oba zapisy są poprawne,- bloków
elifmoże być dowolnie wiele, każdy z własnymthen, elsejest opcjonalny i występuje najwyżej raz, zawsze na końcu, bezthen,- Bash wykonuje tylko pierwszy pasujący blok — kolejne warunki nie są już sprawdzane,
- blok nie może być pusty; jeśli chcesz „nic nie robić”, wpisz
:(polecenie, które zawsze kończy się sukcesem).
Najprostszy if i if-else
#!/usr/bin/env bash
if [[ -f /etc/os-release ]]; then
echo "Plik /etc/os-release istnieje"
else
echo "Brak pliku /etc/os-release"
fi
To samo w jednej linii, np. do wpisania prosto w terminalu:
if [[ -f /etc/os-release ]]; then echo "jest"; else echo "brak"; fi
Bash elif w praktyce: kilka warunków po kolei
Klasyczny przykład elif to klasyfikacja wartości według progów. Poniższy skrypt sprawdza zajętość partycji głównej i wypisuje odpowiedni komunikat:
#!/usr/bin/env bash
uzycie=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if (( uzycie >= 95 )); then
echo "KRYTYCZNIE: dysk zajęty w ${uzycie}%"
exit 2
elif (( uzycie >= 80 )); then
echo "OSTRZEŻENIE: dysk zajęty w ${uzycie}%"
exit 1
else
echo "OK: dysk zajęty w ${uzycie}%"
fi
Kolejność warunków ma znaczenie. Gdybyś najpierw sprawdził >= 80, a potem >= 95, drugi blok nigdy by się nie wykonał — przy 97% pierwszy warunek już jest spełniony. Przy progach zawsze zaczynaj od najbardziej restrykcyjnego.
Kody wyjścia 0, 1, 2 w przykładzie nie są przypadkowe — taką konwencję (OK, ostrzeżenie, błąd krytyczny) rozumieją systemy monitoringu, np. Nagios i jego następcy.
elif a else if — to nie to samo
elif dopisuje warunek do tego samego bloku. Zapis else if tworzy nowy, zagnieżdżony if, więc wymaga własnego fi:
if [[ $x -gt 10 ]]; then
echo "większe niż 10"
else
if [[ $x -gt 5 ]]; then
echo "od 6 do 10"
fi
fi
Działa, ale jest mniej czytelne. Zapomniane drugie fi kończy się błędem syntax error: unexpected end of file — to jedna z najczęstszych pomyłek osób przychodzących z C czy Javy.
[ ], [[ ]] i (( )) — czym sprawdzać warunki
W Bash masz trzy główne sposoby zapisu warunku. Każdy ma swoje zastosowanie.
| Zapis | Co to jest | Do czego używać |
|---|---|---|
[ … ] | Polecenie test (zgodne z POSIX) | Skrypty, które muszą działać także w sh, dash, BusyBox |
[[ … ]] | Słowo kluczowe Bash | Teksty, pliki, wzorce i wyrażenia regularne w skryptach Bash |
(( … )) | Arytmetyka Bash | Porównania liczb całkowitych, z normalnymi operatorami <, >=, == |
| samo polecenie | Dowolny program | Sprawdzanie, czy polecenie się udało, np. grep -q, ping -c1 |
Dlaczego [[ ]] jest bezpieczniejsze od [ ]? W [[ ]] Bash nie dzieli zmiennych na słowa i nie rozwija w nich znaków *, więc pusta lub zawierająca spacje zmienna nie psuje testu. Obsługuje też &&, ||, wzorce i wyrażenia regularne. Jeśli jednak skrypt zaczyna się od #!/bin/sh, a na Debianie czy Ubuntu sh to dash, [[ skończy się błędem [[: not found.
Operatory porównań i testy plików
Porównywanie liczb
Operator w [ ] / [[ ]] | W (( )) | Znaczenie |
|---|---|---|
-eq | == | równe |
-ne | != | różne |
-lt | < | mniejsze |
-le | <= | mniejsze lub równe |
-gt | > | większe |
-ge | >= | większe lub równe |
Bash liczy tylko na liczbach całkowitych. Porównanie [[ 2.5 -gt 2 ]] skończy się błędem — do ułamków użyj bc lub awk.
Porównywanie tekstów
| Test | Znaczenie |
|---|---|
[[ $a == $b ]] lub [ "$a" = "$b" ] | teksty równe |
[[ $a != $b ]] | teksty różne |
[[ -z $a ]] | tekst pusty (długość zero) |
[[ -n $a ]] | tekst niepusty |
[[ $plik == *.log ]] | dopasowanie do wzorca (tylko [[ ]], prawa strona bez cudzysłowu) |
[[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] | dopasowanie do wyrażenia regularnego (tylko [[ ]]) |
Uwaga na pułapkę: w [ ] znaki < i > są przekierowaniami. [ "$a" > "$b" ] nie porówna tekstów, tylko utworzy plik o nazwie z wartością $b.
Testy plików
| Test | Prawda, gdy… |
|---|---|
-e plik | istnieje (dowolny typ) |
-f plik | istnieje i jest zwykłym plikiem |
-d plik | istnieje i jest katalogiem |
-L plik | jest dowiązaniem symbolicznym |
-s plik | istnieje i ma rozmiar większy od zera |
-r / -w / -x plik | masz prawo odczytu / zapisu / wykonania |
plik1 -nt plik2 | plik1 jest nowszy od plik2 |
Pełną listę znajdziesz w podręczniku GNU Bash w sekcji o wyrażeniach warunkowych albo lokalnie poleceniem help test.
Łączenie warunków: AND, OR i negacja
W [[ ]] operatory logiczne piszesz wewnątrz nawiasów:
if [[ $wiek -ge 18 && $wiek -lt 65 ]]; then
echo "wiek produkcyjny"
fi
if [[ $srodowisko == "prod" || $srodowisko == "staging" ]]; then
echo "Uwaga: środowisko współdzielone"
fi
if [[ ! -d /var/backup ]]; then
mkdir -p /var/backup
fi
W [ ] łączysz osobne testy operatorami powłoki: [ "$a" -gt 0 ] && [ "$a" -lt 10 ]. Starsze opcje -a i -o wewnątrz [ ] są w standardzie POSIX oznaczone jako przestarzałe — lepiej ich unikać.
Nawiasy grupujące w [[ ]] działają normalnie: [[ ( $a -gt 5 && $a -lt 10 ) || $a -eq 0 ]].
Warunek jako polecenie: grep, command, ping
Skoro if sprawdza kod wyjścia, warunkiem może być dowolny program. To bardzo częsty wzorzec w skryptach administracyjnych:
if grep -q "^PermitRootLogin yes" /etc/ssh/sshd_config; then
echo "Logowanie roota przez SSH jest włączone"
fi
if ! command -v docker >/dev/null 2>&1; then
echo "Docker nie jest zainstalowany" >&2
exit 1
fi
if systemctl is-active --quiet nginx; then
echo "nginx działa"
elif systemctl is-enabled --quiet nginx; then
echo "nginx jest włączony, ale nie działa — restartuję"
systemctl restart nginx
else
echo "nginx wyłączony"
fi
Opcja -q w grep i --quiet w systemctl wyciszają wynik — interesuje nas tylko kod wyjścia. Więcej o zarządzaniu usługami przeczytasz w poradniku systemctl — do czego służy.
Skrót && i || — i jego pułapka
Proste warunki często zapisuje się bez if:
[[ -d /var/backup ]] || mkdir -p /var/backup
Konstrukcja warunek && A || B nie jest jednak pełnym odpowiednikiem if-else. B wykona się także wtedy, gdy warunek był spełniony, ale A zakończyło się błędem. Przy czymkolwiek ważniejszym niż echo pisz normalne if.
case — lepsza alternatywa dla długiego łańcucha elif
Gdy porównujesz jedną zmienną z wieloma konkretnymi wartościami, case jest czytelniejszy niż pięć bloków elif:
case "$1" in
start)
echo "Uruchamiam" ;;
stop)
echo "Zatrzymuję" ;;
restart|reload)
echo "Restartuję" ;;
*)
echo "Użycie: $0 {start|stop|restart}" >&2
exit 1 ;;
esac
elif zostaw na sytuacje, w których warunki są różnego typu (np. najpierw test pliku, potem porównanie liczby) albo sprawdzasz zakresy.
Najczęstsze błędy w instrukcjach warunkowych Bash
| Komunikat lub objaw | Przyczyna | Poprawka |
|---|---|---|
[: missing ']' lub command not found przy [ | brak spacji wokół nawiasów, np. [$a -gt 5] | [ "$a" -gt 5 ] — spacja po [ i przed ] |
unary operator expected | pusta zmienna bez cudzysłowu w [ ] | [ "$a" = "tak" ] albo [[ $a == tak ]] |
integer expression expected | porównanie liczbowe na tekście lub ułamku | sprawdź zawartość zmiennej, do ułamków użyj bc |
syntax error near unexpected token 'elif' | brak then po poprzednim warunku albo brak średnika | if [[ … ]]; then |
unexpected end of file | brakujące fi (często przy else if) | zamknij każdy if własnym fi lub użyj elif |
[[: not found | skrypt uruchamiany przez sh/dash | shebang #!/usr/bin/env bash i uruchamianie przez bash skrypt.sh |
| warunek zawsze prawdziwy | if [ $a=5 ] bez spacji przy = — test dostaje jeden niepusty tekst | [ "$a" = 5 ] ze spacjami |
Wskazówka: Zainstaluj ShellCheck (
sudo apt install shellchecklubsudo dnf install ShellCheck) i uruchamiajshellcheck skrypt.shprzed wdrożeniem. Wyłapuje brakujące cudzysłowy, pomylone operatory i większość błędów z tabeli powyżej.
Kompletny przykład: skrypt kopii zapasowej z walidacją argumentów
Na koniec skrypt, który łączy wszystkie omówione elementy — sprawdzanie argumentów, testy plików, elif i warunek oparty na poleceniu:
#!/usr/bin/env bash
set -euo pipefail
zrodlo="${1:-}"
cel="${2:-/var/backup}"
if [[ -z $zrodlo ]]; then
echo "Użycie: $0 KATALOG_ŹRÓDŁOWY [KATALOG_DOCELOWY]" >&2
exit 1
elif [[ ! -d $zrodlo ]]; then
echo "Błąd: $zrodlo nie jest katalogiem" >&2
exit 1
elif [[ ! -r $zrodlo ]]; then
echo "Błąd: brak prawa odczytu do $zrodlo" >&2
exit 1
fi
[[ -d $cel ]] || mkdir -p "$cel"
archiwum="$cel/backup-$(date +%F).tar.gz"
if tar -czf "$archiwum" -C "$(dirname "$zrodlo")" "$(basename "$zrodlo")"; then
echo "Kopia zapisana: $archiwum"
else
echo "Tworzenie kopii nie powiodło się" >&2
exit 1
fi
Zapis ${1:-} podstawia pusty tekst, gdy argument nie został podany — bez tego set -u przerwałby skrypt przed naszym czytelnym komunikatem. Jeśli dopiero zaczynasz pracę z terminalem, przyda Ci się też zestawienie 20 poleceń Linux, które musisz znać, a gdy skryptów zacznie przybywać i obsługujesz wiele serwerów — poradnik Ansible.
Najczęściej zadawane pytania
Czym różni się elif od else if w Bash?
elif to jedna instrukcja, która dokłada kolejny warunek do tego samego bloku if i kończy się jednym fi. Zapis else if tworzy nowy, zagnieżdżony if, więc wymaga dodatkowego fi na końcu.
Czy w Bash po elif trzeba pisać then?
Tak. Każdy warunek — zarówno po if, jak i po elif — musi być zakończony słowem then, np. elif [[ $x -gt 5 ]]; then. Po else nie piszesz then.
Jak sprawdzić w Bash, czy plik istnieje?
Użyj if [[ -f /sciezka/plik ]]; then … fi dla zwykłego pliku, -d dla katalogu albo -e, gdy wystarczy dowolny obiekt o tej nazwie.
Skąd błąd „unary operator expected”?
Najczęściej zmienna w [ … ] jest pusta i nie jest ujęta w cudzysłów, więc test widzi za mało argumentów. Pisz [ "$zmienna" = "tak" ] albo używaj [[ … ]].
Jak zapisać if w jednej linii w Bash?
Oddziel części średnikami: if [[ -d /backup ]]; then echo ok; else echo brak; fi. Średnik jest potrzebny przed then, else i fi.
Autor
Założyciel i redaktor XAD.pl. Pisze o sieciach, bezpieczeństwie IT, administracji systemami Windows i Linux oraz o sprzęcie, który sprawia ludziom problemy na co dzień.


