1/41
Czym jest powłoka (shell)?
Slajd definiuje powłokę systemową jako warstwę pośredniczącą pomiędzy użytkownikiem a jądrem systemu operacyjnego.

Powłoka jako interfejs użytkownika

  • Rola powłoki systemowej: program pośredniczący tłumaczący polecenia tekstowe użytkownika na bezpośrednie wywołania jądra systemu.
  • Podział interfejsów: rozróżnienie pomiędzy tekstowym interfejsem wiersza poleceń (CLI) a graficznym środowiskiem okienkowym (GUI).
  • Przewaga środowiska CLI: precyzyjna kontrola nad parametrami narzędzi, niskie zużycie zasobów oraz natywna podatność na automatyzację.
  • Idea skryptowania: zapisywanie powtarzalnych sekwencji komend w plikach wykonywalnych w celu eliminacji błędów manualnych.
Powłoka systemowa (shell): program działający w przestrzeni użytkownika, który interpretuje polecenia wprowadzane z klawiatury lub pliku skryptowego i zleca ich realizację jądru systemu operacyjnego. Zapewnia środowisko uruchomieniowe, zarządzanie zmiennymi oraz przekierowywanie strumieni danych. Do klasycznych powłok uniksowych należą sh, bash, zsh oraz ksh.
  +--------------------+
  |    Użytkownik      |
  +--------------------+
           ^
           | Interakcja
           v
  +--------------------+
  |   Powłoka (Bash)   |
  | (tu działają       |
  |  skrypty)          |
  +--------------------+
           ^
           | Wywołania systemowe
           v
  +--------------------+
  |  Jądro Systemu OS  |
  +--------------------+
           ^
           | Sterowanie
           v
  +--------------------+
  |    Sprzęt (CPU)    |
  +--------------------+
2/41
Wprowadzenie do powłoki Bash
Slajd przedstawia genezę, właściwości i pozycję powłoki GNU Bash w rodzinie systemów operacyjnych Linux.

Czym jest Bash?

  • Projekt GNU (1989 r.): opracowanie przez Briana Foxa wolnodostępnego odpowiednika standardowej powłoki Bourne shell (sh).
  • Standard branżowy: domyślna powłoka interaktywna i interpreter skryptów w większości współczesnych dystrybucji Linuksa.
  • Zgodność z POSIX: zapewnienie pełnej kompatybilności wstecznej z tradycyjnymi narzędziami uniksowymi i powłoką sh.
  • Mechanizm interpretacji: sekwencyjne przetwarzanie i wykonywanie instrukcji linia po linii bezpośrednio z pliku tekstowego.
GNU Bash (Bourne-Again Shell): wieloplatformowy interpreter poleceń i zaawansowany język skryptowy opracowany w ramach projektu GNU. Łączy w sobie najlepsze cechy powłok sh, ksh i csh, oferując bogate mechanizmy tablicowe, dopełnianie poleceń oraz zaawansowaną kontrolę procesów. Stanowi fundament administrowania serwerami linuksowymi.
# Sprawdzenie, jakiej powłoki aktualnie używamy
echo $SHELL
# Wynik w większości systemów Linux: /bin/bash

# Sprawdzenie wersji powłoki Bash
bash --version
3/41
Nasz pierwszy skrypt: "Hello, World!"
Slajd omawia strukturę pliku skryptowego oraz rolę nagłówka shebang w określaniu interpretera poleceń.

Struktura pliku skryptu

  • Format pliku skryptowego: zwykły plik tekstowy zawierający komendy powłoki zapisany z uniksowym znakiem końca linii LF.
  • Mechanizm shebang (#!/bin/bash): sekwencja znaków w pierwszym wierszu wskazująca jądru bezwzględną ścieżkę do interpretera.
  • Komentarze w kodzie: linie rozpoczynające się od znaku # służące do dokumentowania logiki i pomijane przez parser.
  • Formatowanie tekstu: instrukcja echo służąca do wypisywania komunikatów i rezultatów na standardowe wyjście stdout.
Shebang (#!): dwubajtowa sekwencja znaków (0x23 0x21) umieszczana w pierwszej linii pliku wykonywalnego w systemach uniksowych. Wskazuje programowi ładującemu jądra systemu (wywołanie execve) ścieżkę do interpretera, który ma przetworzyć zawartość danego pliku. Umożliwia bezpośrednie uruchamianie skryptów tak samo jak programów binarnych.
#!/bin/bash

# To jest mój pierwszy skrypt w powłoce Bash.
# Jego celem jest wyświetlenie komunikatu powitalnego.

echo "Hello, World!"
4/41
Uruchamianie skryptu
Slajd charakteryzuje system uprawnień uniksowych oraz metody uruchamiania skryptów w środowisku operacyjnym.

Nadawanie uprawnień i wykonanie

  • Prawa wykonywalności: konieczność jawnego nadania uprawnienia execute za pomocą polecenia chmod +x.
  • Uruchamianie bezpośrednie: wywołanie skryptu przez podanie ścieżki względnej ./skrypt.sh lub bezwzględnej.
  • Uruchomienie przez interpreter: wywołanie bash skrypt.sh niewymagające wcześniejszego nadawania bitu wykonywalności.
  • Bezpieczeństwo systemu: domyślny brak bieżącego katalogu w zmiennej PATH wymuszający jawne użycie prefiksu ./.
Bit wykonywalności (chmod +x): atrybut w systemie plików uniksowych określający, czy dany plik może zostać uruchomiony jako proces przez jądro systemu. Wraz z uprawnieniami odczytu (r) i zapisu (w) tworzy trójkową strukturę praw dla właściciela, grupy i pozostałych użytkowników. Do modyfikacji atrybutów służy polecenie chmod.
# Krok 1: Sprawdź aktualne uprawnienia (brak 'x')
# > ls -l hello.sh
# Wynik: -rw-r--r-- 1 użytkownik grupa 85 paź 8 22:10 hello.sh

# Krok 2: Nadaj uprawnienia do wykonywania
chmod +x hello.sh

# Krok 3: Sprawdź ponownie uprawnienia (pojawiło się 'x')
# > ls -l hello.sh
# Wynik: -rwxr-xr-x 1 użytkownik grupa 85 paź 8 22:11 hello.sh

# Krok 4: Uruchom skrypt
./hello.sh

# Oczekiwany wynik na konsoli:
# Hello, World!
5/41
Zmienne: wprowadzenie
Slajd wprowadza zasady definiowania i beztypowego traktowania zmiennych w powłoce Bash.

Czym są zmienne?

  • Natura zmiennych w powłoce: zmienne są dynamicznymi pojemnikami na dane traktowanymi domyślnie jako ciągi tekstowe.
  • Składnia przypisania: rygorystyczny zakaz stosowania spacji wokół operatora przypisania (NAZWA=wartość).
  • Konwencja nazewnicza: stosowanie wielkich liter dla zmiennych konfiguracyjnych i małych dla zmiennych lokalnych.
  • Podstawianie parametrów: rozpoznawanie i przetwarzanie wartości zmiennych w fazie parsowania wiersza poleceń.
Zmienna powłoki: nazwany obszar pamięci procesu powłoki przechowujący wartość w postaci łańcucha znaków (string). W powłoce Bash zmienne nie wymagają wcześniejszej deklaracji typu i są alokowane dynamicznie w momencie pierwszego przypisania. Wartość zmiennej istnieje w pamięci do momentu zakończenia procesu lub usunięcia poleceniem unset.
#!/bin/bash

# Poprawna deklaracja zmiennej
GREETING="Witaj w bashu"

# Błędna deklaracja - to spowoduje błąd "command not found"
# BAD_VAR = "To nie zadziała"

# Zmienna przechowująca liczbę (nadal jako tekst)
USER_COUNT=10

# Zmienna bez cudzysłowu (możliwe, jeśli wartość nie ma spacji)
HOSTNAME=serwer01
6/41
Zmienne: odwoływanie się do wartości
Slajd omawia mechanizm odwoływania się do wartości zmiennych przy użyciu operatora dolara i nawiasów klamrowych.

Używanie wartości zmiennych

  • Operator dereferencji ($): znak dolara informuje powłokę o konieczności zastąpienia nazwy zmiennej jej zawartością.
  • Notacja klamrowa (${ZMIENNA}): jednoznaczne wyznaczenie granic nazwy zmiennej zapobiegające sklejaniu z tekstem.
  • Podstawianie wewnątrz tekstu: możliwość płynnego łączenia wartości zmiennych z prefiksami i sufiksami w ścieżkach.
  • Puste referencje: odwołanie do niezdefiniowanej zmiennej zwraca domyślnie pusty ciąg bez przerywania skryptu.
Dereferencja zmiennej: operacja odczytania zawartości komórki pamięci skojarzonej z danym identyfikatorem i podstawienia jej w miejsce wystąpienia symbolu dolara ($). W powłoce Bash nawiasy klamrowe ${NAZWA} stanowią zalecaną formę zapisu, która eliminuje niejednoznaczności leksykalne i umożliwia zaawansowane operacje na łańcuchach.
#!/bin/bash

IMIE="Alicja"
STANOWISKO="programista"

# Proste odwołanie
echo "Użytkownik: $IMIE"
echo "Stanowisko: $STANOWISKO"

# Przykład, gdzie nawiasy klamrowe są konieczne:
# Chcemy utworzyć tekst "Alicja_programista.log"
# Błędne podejście:
echo "Plik logu: $IMIE_$STANOWISKO.log" 
# bash szuka zmiennej o nazwie IMIE_ (która nie istnieje)

# Poprawne podejście z nawiasami klamrowymi:
echo "Plik logu: ${IMIE}_${STANOWISKO}.log"
7/41
Zmienne: rola cudzysłowów
Slajd wyjaśnia zasady cytowania danych oraz różnice pomiędzy cudzysłowem podwójnym a pojedynczym.

Znaczenie cudzysłowów (quoting)

  • Cudzysłów podwójny (słaby): pozwala na rozwijanie zmiennych ($) i poleceń (`/$) chroniąc spacje przed podziałem argumentów.
  • Cudzysłów pojedynczy (silny): traktuje każdy zawarty znak w sposób dosłowny, blokując wszelkie podstawienia powłoki.
  • Znak ucieczki (backslash \): anuluje specjalne znaczenie pojedynczego znaku znajdującego się bezpośrednio za nim.
  • Word Splitting i Globbing: podwójne cudzysłowy zapobiegają rozbijaniu zmiennych zawierających spacje na odrębne parametry.
Cytowanie (quoting): mechanizm kontroli parsowania w powłoce, który pozwala na wyłączenie specjalnego traktowania znaków zarezerwowanych (np. spacji, gwiazdek, znaków dolara). Rozróżnia się cytowanie słabe (podwójny cudzysłów), które zachowuje rozwijanie parametrów, oraz cytowanie silne (pojedynczy cudzysłów), traktujące tekst całkowicie dosłownie.
#!/bin/bash

USER="Jan Kowalski"
FILES_COUNT=$(ls -1 | wc -l) # Podstawienie polecenia, omówimy później

# 1. Podwójny cudzysłów (zalecane)
echo "Użytkownik: $USER. Liczba plików: $FILES_COUNT."
# Wynik: Użytkownik: Jan Kowalski. Liczba plików: 15.
# Zmienne zostały zinterpretowane.

# 2. Pojedynczy cudzysłów (wszystko dosłownie)
echo 'Użytkownik: $USER. Liczba plików: $FILES_COUNT.'
# Wynik: Użytkownik: $USER. Liczba plików: $FILES_COUNT.
# Znaki $ i () potraktowano jak zwykły tekst.

# 3. Brak cudzysłowu (niebezpieczne!)
echo Użytkownik: $USER. Liczba plików: $FILES_COUNT.
# Wynik: Użytkownik: Jan Kowalski. Liczba plików: 15.
# W tym prostym przypadku działa, ale jest to zła praktyka.
# Polecenie 'echo' otrzymało 6 osobnych argumentów.
8/41
Zmienne: wczytywanie danych od użytkownika
Slajd przedstawia instrukcję read jako narzędzie interaktywnego pobierania danych z klawiatury użytkownika.

Polecenie read

  • Pobieranie strumienia stdin: zatrzymanie wykonywania skryptu i oczekiwanie na wprowadzenie wiersza tekstu przez użytkownika.
  • Flaga zachowania ukośników (-r): bezwzględnie zalecany przełącznik blokujący interpretację znaku \ jako znaku ucieczki.
  • Wyświetlanie zachęty (-p): możliwość zintegrowania tekstu promptu bezpośrednio w wywołaniu polecenia read.
  • Wprowadzanie haseł (-s): tryb cichy wyłączający echo na terminalu w celu bezpiecznego wprowadzania danych poufnych.
Polecenie read: wbudowane polecenie powłoki Bash służące do odczytywania pojedynczego wiersza tekstu ze standardowego wejścia (lub wskazanego deskryptora) i przypisywania poszczególnych słów do zdefiniowanych zmiennych. Stanowi podstawowy mechanizm tworzenia skryptów interaktywnych w środowisku CLI.
#!/bin/bash

# Użycie opcji -p do wyświetlenia zachęty
read -p "Wpisz swoje imię: " IMIE

# Użycie opcji -s do wczytania hasła
read -s -p "Wpisz swoje hasło: " HASLO
echo "" # Dodajemy nową linię, bo read -s jej nie dodaje

echo "-----------------------------------"
echo "Witaj, $IMIE!"
# Oczywiście, w realnym skrypcie NIGDY nie wyświetlamy hasła!
# To tylko demonstracja, że zmienna je przechowała.
echo "Twoje hasło zostało wczytane i ma ${#HASLO} znaków."
9/41
Zmienne: podstawienie polecenia
Slajd omawia mechanizm podstawiania poleceń pozwalający na przechwytywanie wyników wykonania programów.

Zapisywanie wyniku polecenia do zmiennej

  • Składnia $(polecenie): nowoczesny standard podstawiania wyniku polecenia pozwalający na zagnieżdżanie do zmiennej lub tekstu.
  • Składnia apostrofów odwrotnych (`...`): archaiczny format backticks podatny na błędy interpretacji przy zagnieżdżaniu.
  • Usuwanie znaków nowej linii: powłoka automatycznie obcina końcowe znaki \n z przechwyconego strumienia wyjściowego.
  • Automatyzacja pomiarów: zastosowanie do rejestrowania daty, liczby procesów, sum kontrolnych i wolnego miejsca na dysku.
Podstawienie polecenia (Command Substitution): konstrukcja powłoki umożliwiająca uruchomienie programu w podpowłoce (subshell), przechwycenie jego standardowego wyjścia (stdout) i wstawienie otrzymanego tekstu w miejsce wywołania. Zalecaną i w pełni standaryzowaną składnią POSIX jest $(polecenie).
#!/bin/bash

# Nowoczesna, zalecana składnia
AKTUALNA_DATA=$(date "+%Y-%m-%d %H:%M:%S")
LISTA_PLIKOW=$(ls)
BIEŻĄCY_KATALOG=$(pwd)

echo "Log wygenerowany dnia: $AKTUALNA_DATA"
echo "Jesteś w katalogu: $BIEŻĄCY_KATALOG"
echo "Pliki w tym katalogu to:"
echo "$LISTA_PLIKOW"

# Starsza składnia (działa, ale unikaj)
KERNEL_VERSION=`uname -r`
echo "Wersja jądra systemu: $KERNEL_VERSION"
10/41
Zmienne: zmienne wbudowane i środowiskowe
Slajd charakteryzuje zmienne wbudowane oraz zmienne środowiskowe sterujące konfiguracją procesów potomnych.

Predefiniowane zmienne

  • Zmienne predefiniowane: specjalne rejestry powłoki dostarczające danych o statusie wykonania ($?), PID ($$) i parametrach.
  • Zmienne środowiskowe: zmienne eksportowane do procesów potomnych za pomocą dyrektywy export (USER, HOME, PATH).
  • Dziedziczenie środowiska: procesy uruchamiane ze skryptu otrzymują kopię zmiennych środowiskowych rodzica.
  • Konfiguracja powłoki: zarządzanie zachowaniem programów narzędziowych poprzez zmienne LC_ALL, LANG czy HISTSIZE.
Zmienna środowiskowa (Environment Variable): zmienna globalna w systemie operacyjnym, która jest dziedziczona przez wszystkie procesy potomne utworzone przez dany proces powłoki. Przechowuje informacje konfiguracyjne, takie jak katalog domowy ($HOME), nazwa użytkownika ($USER) czy ścieżki przeszukiwania ($PATH). Do przekształcenia zmiennej lokalnej w środowiskową służy polecenie export.
#!/bin/bash

echo "Witaj, $USER!"
echo "Twój katalog domowy to: $HOME"
echo "Aktualnie znajdujesz się w: $PWD"
echo "Pracujesz na maszynie o nazwie: $HOSTNAME"
echo ""
echo "Identyfikator tego procesu to: $$"

# Sprawdzenie kodu wyjścia
ls /etc/passwd > /dev/null # To polecenie się powiedzie
echo "Kod wyjścia po udanym 'ls': $?"

ls /nieistniejacy/plik > /dev/null 2>&1 # To polecenie się nie powiedzie
echo "Kod wyjścia po nieudanym 'ls': $?"
11/41
Zmienne: zmienne tylko do odczytu (stałe)
Slajd przedstawia sposoby definiowania zmiennych tylko do odczytu w celu ochrony integralności stałych w skrypcie.

Polecenie readonly

  • Polecenie readonly: zablokowanie możliwości modyfikacji oraz usunięcia zmiennej (unset) po jej zainicjalizowaniu.
  • Składnia declare -r: alternatywna metoda tworzenia stałych zintegrowana z wewnętrznym systemem typów Basha.
  • Ochrona parametrów krytycznych: zabezpieczenie stałych konfiguracyjnych, ścieżek bazodanowych i portów sieciowych.
  • Niezmienność w cyklu życia: gwarancja stałości wartości przez cały czas trwania procesu skryptu.
Stała (zmienna tylko do odczytu): zmienna powłoki oznaczona specjalnym atrybutem uniemożliwiającym zmianę jej wartości oraz usunięcie za pomocą polecenia unset w trakcie działania procesu. Wykorzystywana do definiowania parametrów konfiguracyjnych i stałych matematycznych, których zmiana mogłaby naruszyć bezpieczeństwo lub stabilność skryptu.
#!/bin/bash

# Sposób 1: Najpierw deklaracja, potem 'readonly'
LOG_DIR="/var/log/myapp"
readonly LOG_DIR

# Sposób 2: Deklaracja i 'readonly' w jednej linii
readonly DB_HOST="192.168.1.100"

echo "Katalog logów: $LOG_DIR"
echo "Host bazy danych: $DB_HOST"

# Próba zmiany wartości zmiennej readonly
echo "Próbuję zmienić LOG_DIR..."
LOG_DIR="/tmp/logs" 

# Powyższa linia spowoduje błąd w trakcie wykonania skryptu:
# ./script.sh: line 17: LOG_DIR: readonly variable
# Skrypt kontynuuje działanie (chyba że włączono set -e).

echo "Ta linia wyświetli się mimo błędu (bez set -e)."
12/41
Wyrażenia arytmetyczne
Slajd omawia mechanizm obliczeń na liczbach całkowitych przy użyciu wbudowanej konstrukcji arytmetycznej $(( )).

Operacje na liczbach

  • Konstrukcja $(( wyrażenie )): wbudowany w Bash szybki mechanizm ewaluacji operacji arytmetycznych na liczbach całkowitych.
  • Podstawowe operatory: wsparcie dla dodawania (+), odejmowania (-), mnożenia (*), dzielenia (/) oraz modulo (%).
  • Inkrementacja i dekrementacja: obsługa operatorów pre- i post-inkrementacji (++var, var++) znanych z języka C.
  • Ograniczenie do liczb całkowitych: operacja dzielenia odrzuca część ułamkową bez zaokrąglania (np. 7/2 daje wynik 3).
Rozwijanie arytmetyczne (Arithmetic Expansion): mechanizm składniowy powłoki oznaczany symbolem $(( wyrażenie )), który umożliwia wykonywanie operacji matematycznych na 64-bitowych liczbach całkowitych bezpośrednio w kodzie skryptu bez powoływania zewnętrznych procesów. Wewnątrz nawiasów zmienne mogą być używane bez poprzedzającego znaku dolara.
#!/bin/bash

A=10
B=3

# Użycie składni $((...))
SUMA=$((A + B))
ROZNICA=$((A - B))
ILOCZYN=$((A * B))
ILORAZ=$((A / B))   # Wynik to 3, bo to dzielenie całkowite
RESZTA=$((A % B))   # Wynik to 1
POTEGA=$((A  B))  # 10 do potęgi 3

echo "$A + $B = $SUMA"
echo "$A / $B = $ILORAZ (iloraz) i $RESZTA (reszta)"
echo "$A do potęgi $B = $POTEGA"

# Inkrementacja w stylu C
LICZNIK=0
LICZNIK=$((LICZNIK + 1))
echo "Licznik: $LICZNIK"

# Użycie 'let'
let LICZNIK=LICZNIK+1
echo "Licznik po 'let': $LICZNIK"
13/41
Operacje na liczbach zmiennoprzecinkowych (bc)
Slajd prezentuje zaawansowane obliczenia zmiennoprzecinkowe przy użyciu zewnętrznego kalkulatora bc.

Ograniczenia arytmetyki powłoki Bash

  • Ograniczenia powłoki: brak natywnej obsługi ułamków i liczb rzeczywistych w wewnętrznym silniku Basha.
  • Kalkulator bc: wykorzystanie potoku do przekazania wyrażenia matematycznego do kalkulatora arbitralnej precyzji.
  • Ustalanie precyzji (scale): zmienna scale precyzująca wymaganą liczbę miejsc po przecinku w wyniku dzielenia.
  • Zależności pakietowe: konieczność weryfikacji obecności pakietu bc w minimalnych obrazach serwerowych i kontenerach.
Kalkulator bc (Basic Calculator): zewnętrzny interpreter języka obliczeń matematycznych o dowolnej precyzji, dostępny w systemach uniksowych. W skryptach powłoki jest powszechnie wykorzystywany do realizacji obliczeń zmiennoprzecinkowych poprzez przekazywanie danych wejściowych za pomocą potoku tekstowego (np. echo 'scale=2; 10/3' | bc).
#!/bin/bash

# Próba operacji zmiennoprzecinkowej w powłoce Bash (spowoduje błąd)
# WYNIK=$((5 / 2.5)) 
# -> bash: 5 / 2.5: syntax error: invalid arithmetic operator

# Poprawne użycie `bc`
LICZBA1=10.5
LICZBA2=2

WYNIK=$(echo "$LICZBA1 * $LICZBA2" | bc)
echo "$LICZBA1 * $LICZBA2 = $WYNIK"

# Dzielenie z większą precyzją
# 'scale' ustawia liczbę miejsc po przecinku
PRECYZYJNY_WYNIK=$(echo "scale=4; 10 / 3" | bc)
echo "10 / 3 z precyzją do 4 miejsc: $PRECYZYJNY_WYNIK"

# Użycie biblioteki matematycznej (-l) do obliczenia sinusa
SINUS_1=$(echo "s(1)" | bc -l)
echo "Sinus(1) = $SINUS_1"
14/41
Operatory porównania: liczby
Slajd przedstawia operatory dedykowane do numerycznego porównywania liczb całkowitych w instrukcjach warunkowych.

Porównywanie liczb całkowitych

  • Operatory mnemoniczne: stosowanie dedykowanych flag -eq (równy), -ne (różny), -lt (mniejszy), -gt (większy).
  • Operatory nierówności: wykorzystanie -le (mniejszy lub równy) oraz -ge (większy lub równy) w testach logicznych.
  • Pułapka operatorów znakowych: użycie znaków < lub > w klasycznym teście [ ] skutkuje porównaniem leksykograficznym, a nie numerycznym.
  • Konstrukcja (( )): możliwość stosowania klasycznych operatorów algebraicznych (==, !=, <, >) wewnątrz podwójnych nawiasów okrągłych.
Operatory porównania liczb: zestaw flag logicznych wykorzystywanych wewnątrz instrukcji testującej ([ ] lub [[ ]]) do sprawdzania relacji wielkości pomiędzy dwiema liczbami całkowitymi. Flagi te wywodzą się z języka asemblera i poleceń wczesnego Uniksa (np. -eq od equal, -lt od less than), chroniąc przed pomyłkami z operatorami przekierowania strumieni.
#!/bin/bash

read -p "Podaj swój wiek: " WIEK

# Sprawdzenie, czy wiek jest liczbą (prosty przykład z wyrażeniem regularnym)
if ! [[ "$WIEK" =~ ^[0-9]+$ ]]; then
    echo "Błąd: Wprowadzona wartość nie jest liczbą."
    exit 1
fi

# Porównania liczbowe
if [[ "$WIEK" -ge 18 ]]; then
    echo "Jesteś osobą pełnoletnią."
else
    echo "Nie jesteś jeszcze osobą pełnoletnią."
fi

if [[ "$WIEK" -eq 42 ]]; then
    echo "To jest odpowiedź na wielkie pytanie o życie, wszechświat i całą resztę."
fi
15/41
Operatory porównania: ciągi znaków
Slajd omawia operatory badania relacji leksykograficznych oraz sprawdzania długości ciągów tekstowych.

Porównywanie tekstu

  • Badanie równości łańcuchów: wykorzystanie operatorów == oraz != do weryfikacji tożsamości dwóch ciągów znaków.
  • Sprawdzanie pustego ciągu (-z): flaga -z (zero length) zwraca prawdę, gdy zmienna tekstowa nie zawiera znaków.
  • Sprawdzanie niepustego ciągu (-n): flaga -n (non-zero length) weryfikuje obecność danych w łańcuchu tekstowym.
  • Porównanie leksykograficzne: sortowanie alfabetyczne przy użyciu operatorów < oraz > wewnątrz konstrukcji [[ ]].
Porównywanie ciągów znaków: operacje logiczne badające zgodność łańcuchów tekstowych pod względem równości, kolejności alfabetycznej według tablicy kodowej ASCII/UTF-8 oraz faktu zainicjalizowania zmiennej. W instrukcjach warunkowych zaleca się stosowanie nawiasów [[ ]], które eliminują konieczność cytowania operandów i chronią przed błędami pustych zmiennych.
#!/bin/bash

read -p "Wpisz 'tak' lub 'nie': " ODPOWIEDZ

# Porównanie ciągu znaków
if [[ "$ODPOWIEDZ" == "tak" ]]; then
    echo "Wybrano opcję twierdzącą."
elif [[ "$ODPOWIEDZ" == "nie" ]]; then
    echo "Wybrano opcję przeczącą."
else
    echo "Nieznana odpowiedź."
fi

# Sprawdzenie, czy zmienna jest pusta
read -p "Wpisz swoje nazwisko (lub zostaw puste): " NAZWISKO

if [[ -z "$NAZWISKO" ]]; then
    echo "Nie podano nazwiska."
else
    echo "Podane nazwisko to: $NAZWISKO"
fi
16/41
Operatory logiczne
Slajd charakteryzuje operatory algebry Boole'a stosowane do łączenia złożonych warunków logicznych w skryptach.

Łączenie warunków

  • Koniunkcja logiczna (AND): operator && zwraca prawdę wyłącznie wtedy, gdy oba sprawdzane warunki są spełnione.
  • Alternatywa logiczna (OR): operator || zwraca prawdę, jeśli przynajmniej jeden z testowanych warunków jest prawdziwy.
  • Negacja logiczna (NOT): operator ! odwraca wynik logiczny testowanego wyrażenia (prawda staje się fałszem).
  • Wykonanie warunkowe w CLI: idiomatyczne łączenie komend: polecenie && sukces || błąd bez używania pełnego bloku if.
Operatory logiczne w powłoce: symbole algebry Boole'a (&&, ||, !) pozwalające na tworzenie złożonych wyrażeń warunkowych oraz sterowanie przepływem sterowania na podstawie kodów zakończenia programów. W powłoce Bash warunek jest uznawany za spełniony, gdy kod zakończenia badanego polecenia wynosi 0 (sukces).
#!/bin/bash

read -p "Podaj swój wiek: " WIEK
read -p "Czy posiadasz prawo jazdy? (tak/nie): " PRAWKO

# Operator AND (&&)
if [[ "$WIEK" -ge 18 && "$PRAWKO" == "tak" ]]; then
    echo "Możesz prowadzić samochód."
else
    echo "Nie spełniasz warunków do prowadzenia samochodu."
fi

# Operator OR (||)
read -p "Podaj nazwę użytkownika: " USERNAME

if [[ "$USERNAME" == "admin" || "$USERNAME" == "root" ]]; then
    echo "Witaj, administratorze!"
fi

# Operator NOT (!)
read -p "Czy chcesz zakończyć? (tak/nie): " KONIEC

if ! [[ "$KONIEC" == "tak" ]]; then
    echo "Kontynuujemy pracę..."
fi
17/41
Operacje na plikach i katalogach
Slajd przedstawia operatory testowania atrybutów plików, katalogów i praw dostępu w systemie plików.

Sprawdzanie atrybutów plików

  • Istnienie i typ pliku: flaga -e sprawdza istnienie obiektu, -f weryfikuje, czy obiekt jest plikiem zwykłym, a -d czy katalogiem.
  • Badanie praw dostępu: operatory -r (odczyt), -w (zapis) oraz -x (wykonywanie) weryfikują uprawnienia bieżącego procesu.
  • Badanie rozmiaru pliku: flaga -s sprawdza, czy plik istnieje i czy jego rozmiar jest większy niż zero bajtów.
  • Porównywanie czasu modyfikacji: operatory -nt (newer than) oraz -ot (older than) badają chronologię utworzenia plików.
Testy atrybutów plików: grupa jednoargumentowych operatorów logicznych realizowanych przez instrukcję test ([ ]) lub [[ ]], które odpytują metadane systemu plików (i-węzły) o właściwości wskazanego obiektu. Stanowią fundament bezpiecznej automatyzacji zadań plikowych i tworzenia kopii zapasowych.
#!/bin/bash

PLIK_KONFIGuracyjny="/etc/resolv.conf"
KATALOG_DOMOWY="$HOME"
PLIK_NIEISTNIEJACY="/tmp/tego_pliku_na_pewno_nie_ma"

# Sprawdzenie, czy plik istnieje i jest plikiem
if [[ -f "$PLIK_KONFIGuracyjny" ]]; then
    echo "Plik konfiguracyjny '$PLIK_KONFIGuracyjny' istnieje."

    # Sprawdzenie, czy jest odczytywalny
    if [[ -r "$PLIK_KONFIGuracyjny" ]]; then
        echo "I można go odczytać."
    fi
else
    echo "Brak pliku konfiguracyjnego."
fi

# Sprawdzenie, czy katalog istnieje
if [[ -d "$KATALOG_DOMOWY" ]]; then
    echo "Katalog domowy '$KATALOG_DOMOWY' istnieje."
fi

# Sprawdzenie nieistniejącego pliku
if [[ -e "$PLIK_NIEISTNIEJACY" ]]; then
    echo "To się nie powinno wyświetlić."
else
    echo "Plik '$PLIK_NIEISTNIEJACY' nie istnieje."
fi
18/41
Instrukcja warunkowa `if`
Slajd omawia podstawową konstrukcję warunkową if pozwalającą na warunkowe rozgałęzianie wykonania kodu.

Podejmowanie decyzji w skrypcie

  • Struktura składniowa: blok rozpoczyna się słowem kluczowym if, po którym następuje warunek, słowo then i blok instrukcji.
  • Domknięcie konstrukcji: każdy blok warunkowy w powłoce Bash musi zostać bezwzględnie domknięty odwróconym słowem fi.
  • Ocena kodu wyjścia: instrukcja if bada kod zakończenia polecenia; wartość 0 oznacza sukces i uruchamia sekcję then.
  • Zalecenie składni [[ ]]: wykorzystanie podwójnych nawiasów kwadratowych jako nowoczesnego i bezpiecznego standardu.
Instrukcja warunkowa if: podstawowa instrukcja sterująca przepływem programu w powłoce Bash. Bada kod zakończenia polecenia lub wyrażenia testowego umieszczonego po słowie if; w przypadku zwrócenia statusu 0 (sukces) wykonuje ciąg poleceń zdefiniowany wewnątrz bloku then. Konstrukcja kończy się słowem kluczowym fi.
if [[ warunek ]]; then
    # Blok kodu do wykonania,
    # jeśli warunek jest prawdziwy
fi
19/41
Instrukcja warunkowa `if-else`
Slajd przedstawia rozszerzenie instrukcji warunkowej o gałąź else obsługującą negatywny wynik testu.

Obsługa alternatywnych ścieżek

  • Obsługa alternatywy: gałąź else zawiera instrukcje realizowane w sytuacji, gdy warunek początkowy zwróci kod błędu (różny od 0).
  • Determinizm wykonania: gwarancja wykonania dokładnie jednego z dwóch przewidzianych wariantów działania skryptu.
  • Komunikacja z użytkownikiem: możliwość wypisania informacji o błędzie na strumień stderr i kontynuowania pracy.
  • Zwiększenie odporności: zapobieganie awariom skryptu w przypadku braku wymaganych plików lub niepoprawnych danych.
Konstrukcja if-else: dwugałęziowa struktura warunkowa zapewniająca wykonanie określonych instrukcji w bloku then, gdy badany warunek jest spełniony (status 0), lub przejście do wykonania alternatywnego bloku instrukcji zdefiniowanego w sekcji else, gdy warunek nie został spełniony.
if [[ warunek ]]; then
    # Blok 'then' - wykonuje się,
    # gdy warunek jest prawdziwy
else
    # Blok 'else' - wykonuje się,
    # gdy warunek jest fałszywy
fi
20/41
Instrukcja warunkowa `if-elif-else`
Slajd omawia konstrukcję if-elif-else przeznaczoną do sekwencyjnego testowania wielu warunków logicznych.

Sprawdzanie wielu warunków

  • Skrót elif (else if): umożliwia definiowanie kolejnych warunków testowych w ramach jednej zwartej struktury.
  • Sekwencyjna weryfikacja: warunki są badane po kolei od góry; wykonany zostaje wyłącznie pierwszy blok, który zwróci sukces.
  • Opcjonalna gałąź domyślna: końcowy blok else przechwytuje wszystkie przypadki, które nie spełniły żadnego z wcześniejszych testów.
  • Eliminacja zagnieżdżeń: poprawa czytelności kodu poprzez zastąpienie kaskady zagnieżdżonych instrukcji if-else.
Konstrukcja if-elif-else: wielogałęziowa struktura decyzyjna w powłoce Bash pozwalająca na sekwencyjne badanie wielu niezależnych warunków logicznych. Po napotkaniu pierwszego spełnionego warunku powłoka wykonuje przypisany mu blok kodu i natychmiast opuszcza całą konstrukcję, ignorując pozostałe gałęzie.
if [[ warunek1 ]]; then
    # Blok 1
elif [[ warunek2 ]]; then
    # Blok 2
elif [[ warunek3 ]]; then
    # Blok 3
else
    # Blok 'else'
fi
21/41
Instrukcja `case`
Slajd prezentuje instrukcję wyboru case jako efektywną metodę dopasowywania wartości do wzorców tekstowych.

Alternatywa dla `if-elif`

  • Dopasowywanie wzorców: porównywanie wartości zmiennej z ciągiem masek (wildcards) bez konieczności pisania złożonych if.
  • Separator wariantów (;;): dwukrotny średnik oznacza definitywne zakończenie bloku instrukcji dla danego wzorca.
  • Wzorzec domyślny (*): klauzula z gwiazdką przechwytująca wszystkie nieobsłużone wcześniej wartości.
  • Wielokrotne dopasowanie (|): możliwość grupowania kilku równorzędnych wzorców za pomocą pionowej kreski.
Instrukcja case: struktura wyboru wielowariantowego w powłoce Bash, która porównuje zadaną wartość z listą wzorców tekstowych (obsługujących maski wieloznaczne typu *, ?, [a-z]) i wykonuje blok poleceń przypisany do pierwszego pasującego wariantu. Domyka się odwróconym słowem esac.
#!/bin/bash

read -p "Co chcesz zrobić? (start|stop|restart): " AKCJA

case "$AKCJA" in
    start)
        echo "Uruchamianie usługi..."
        # /etc/init.d/apache2 start
        ;;
    stop)
        echo "Zatrzymywanie usługi..."
        # /etc/init.d/apache2 stop
        ;;
    restart|reload) # Dwa wzorce dla tego samego bloku
        echo "Restartowanie usługi..."
        # /etc/init.d/apache2 restart
        ;;
    *) # Wzorzec domyślny
        echo "Nieznana akcja. Użyj: start, stop lub restart."
        exit 1
        ;;
esac # 'case' czytane od tyłu
22/41
Pętla `for` - iteracja po liście
Slajd omawia działanie pętli for iterującej po zdefiniowanej kolekcji elementów lub liście plików.

Wykonywanie operacji na kolekcji elementów

  • Składnia for-in: pętla pobiera po kolei elementy ze zdefiniowanej listy słów i przypisuje je do zmiennej iteracyjnej.
  • Blok do...done: ciało pętli zawierające instrukcje do powtórzenia jest ujęte pomiędzy słowa kluczowe do oraz done.
  • Iteracja po plikach: wykorzystanie masek powłoki (np. *.log) do automatycznego przetwarzania zbiorów plików na dysku.
  • Separacja argumentów: elementy listy wejściowej są dzielone na podstawie wewnętrznego separatora pól IFS (domyślnie spacja).
Pętla for (kolekcyjna): konstrukcja iteracyjna powłoki Bash wykonująca sekwencję instrukcji określoną liczbę razy, odpowiadającą liczbie elementów przekazanych w liście argumentów. W każdej iteracji bieżący element jest przypisywany do wskazanej zmiennej sterującej, co umożliwia przetwarzanie list użytkowników, katalogów i pakietów.
for ZMIENNA in element1 element2 element3 ...; do
    # Blok kodu, który wykona się dla każdego elementu.
    # Wartość bieżącego elementu jest dostępna
    # w zmiennej $ZMIENNA.
done
23/41
Pętla `for` - iteracja po zakresie liczb
Slajd przedstawia metody generowania sekwencji liczbowych w pętli for w stylach uniksowym i klasycznym C.

Różne sposoby tworzenia sekwencji liczbowych

  • Rozwijanie klamrowe {start..stop}: wbudowany w Bash szybki generator sekwencji liczb lub liter bez powoływania procesów.
  • Krok sekwencji {start..stop..krok}: możliwość definiowania inkrementacji o zadaną wartość całkowitą.
  • Pętla w stylu języka C: składnia for (( i=0; i<n; i++ )) dedykowana do precyzyjnych liczników numerycznych.
  • Narzędzie seq: zewnętrzny program generatora sekwencji stosowany w starszych powłokach zgodnych z czystym POSIX.
Pętla for w stylu C: wariant pętli iteracyjnej w powłoce Bash wykorzystujący trójczłonowy nagłówek arytmetyczny (( inicjalizacja; warunek; modyfikacja )) zapożyczony z języka C. Umożliwia realizację klasycznych algorytmów numerycznych z bezpośrednią kontrolą nad indeksem i warunkiem zatrzymania.
#!/bin/bash

echo "--- Użycie rozwinięcia klamrowego ---"
for i in {1..5}; do
    echo "Liczba: $i"
done

echo ""
echo "--- Użycie polecenia 'seq' ---"
for i in $(seq 1 2 10); do # Od 1 do 10 z krokiem 2
    echo "Liczba nieparzysta: $i"
done

echo ""
echo "--- Użycie pętli w stylu C ---"
for (( j=10; j>0; j-- )); do # Odliczanie
    echo "Pozostało: $j"
done
echo "Start!"
24/41
Pętla `while`
Slajd charakteryzuje pętlę while wykonującą instrukcje, dopóki badany warunek logiczny pozostaje spełniony.

Pętla sterowana warunkiem

  • Pętla warunkowa: instrukcje wewnątrz bloku do...done są wykonywane tak długo, jak polecenie testowe zwraca status 0.
  • Modyfikacja zmiennej sterującej: konieczność ręcznej zmiany stanu wewnątrz pętli w celu uniknięcia zapętlenia nieskończonego.
  • Pętle nieskończone: konstrukcja while true wykorzystywana do tworzenia demonów i procesów monitorujących stan systemu.
  • Pętla dopóki (until): pokrewna konstrukcja wykonująca instrukcje, dopóki warunek jest fałszywy (odwrotność while).
Pętla while: instrukcja sterująca powłoki realizująca pętlę ze sprawdzaniem warunku na początku każdej iteracji. Wykonuje blok instrukcji tak długo, jak polecenie warunkowe kończy się sukcesem (kod wyjścia 0). Stanowi podstawowy mechanizm oczekiwania na zdarzenia systemowe i odczytu strumieni.
while [[ warunek ]]; do
    # Blok kodu do wykonania,
    # dopóki warunek jest prawdziwy.
    # Ważne: wewnątrz pętli coś musi
    # wpływać na warunek, aby uniknąć
    # pętli nieskończonej!
done
25/41
Czytanie pliku linia po linii
Slajd omawia kanoniczny wzorzec odczytu zawartości plików tekstowych linia po linii za pomocą pętli while.

Najważniejszy wzorzec pętli `while`

  • Konstrukcja while IFS= read -r: zabezpieczenie przed obcinaniem spacji brzegowych oraz interpretacją znaków ukośnika jako sekwencji ucieczki (ang. escaping).
  • Obsługa ostatniej linii: dodanie warunku || [ -n "$linia" ] gwarantujące przetworzenie pliku bez końcowego znaku \n.
  • Przekierowanie wejścia (< plik): podpięcie pliku bezpośrednio pod koniec pętli done zapobiega tworzeniu podpowłoki.
  • Praktyczne zastosowanie: analiza plików konfiguracyjnych (/etc/passwd, bazy CSV, logi serwerowe) wiersz po wierszu.
Wzorzec odczytu liniowego: bezpieczna i odporna na błędy konstrukcja powłoki Bash wykorzystująca pętlę while w połączeniu z poleceniem read i wyczyszczoną zmienną IFS. Pozwala na sekwencyjne przetwarzanie plików tekstowych dowolnego rozmiaru bez nadmiernego zużycia pamięci RAM, zachowując pełną wierność każdego wiersza danych.
while IFS= read -r LINIA; do
    # Przetwarzaj zmienną $LINIA
    echo "Odczytano: $LINIA"
done < "ścieżka/do/pliku.txt"
26/41
Sterowanie pętlami: `break` i `continue`
Slajd przedstawia instrukcje break oraz continue służące do programowego modyfikowania przebiegu pętli.

Modyfikowanie przebiegu pętli

  • Instrukcja break: natychmiastowe przerwanie wykonywania pętli i przejście do pierwszej instrukcji za słowem done.
  • Instrukcja continue: natychmiastowe zakończenie bieżącej iteracji i przejście do sprawdzania warunku kolejnego cyklu.
  • Parametry zagnieżdżenia: możliwość opuszczenia wielu pętli zagnieżdżonych jednocześnie poprzez wywołanie break N.
  • Optymalizacja algorytmów: eliminacja zbędnych przebiegów pętli po odnalezieniu poszukiwanego zasobu w systemie.
Instrukcje sterowania pętlą: specjalne dyrektywy wbudowane w powłokę Bash (break i continue), które pozwalają na dynamiczną modyfikację standardowego cyklu iteracyjnego. Instrukcja break trwale przerywa pętlę, natomiast continue pomija resztę bieżącego kroku, przechodząc do następnego elementu kolekcji.
#!/bin/bash

echo "--- Przykład 'continue' ---"
# Pomiń liczby parzyste
for i in {1..10}; do
    # Sprawdź, czy reszta z dzielenia przez 2 jest 0
    if (( i % 2 == 0 )); then
        continue # Pomiń resztę pętli dla tej iteracji
    fi
    echo "Liczba nieparzysta: $i"
done

echo ""
echo "--- Przykład 'break' ---"
# Szukaj pliku, przerwij po znalezieniu
SZUKANY_PLIK="bash"
for PLIK in /bin/*; do
    # basename zwraca samą nazwę pliku bez ścieżki
    if [[ "$(basename "$PLIK")" == "$SZUKANY_PLIK" ]]; then
        echo "Znaleziono plik: $PLIK"
        break # Zakończ pętlę, nie szukaj dalej
    fi
done
27/41
Funkcje: wprowadzenie
Slajd wprowadza funkcje jako modułowe bloki kodu wielokrotnego użytku w skryptach powłoki.

Grupowanie kodu do ponownego użycia

  • Definicja funkcji: blok nazwanego kodu tworzony za pomocą składni nazwa_funkcji() { ... } lub słowa function.
  • Modularność skryptu: eliminacja redundancji kodu oraz podział złożonych zadań administracyjnych na małe podprogramy.
  • Wywoływanie funkcji: funkcję uruchamia się wpisując jej nazwę tak samo jak zwykłe polecenie systemowe bez nawiasów.
  • Kolejność deklaracji: funkcja musi zostać zdefiniowana w kodzie skryptu przed miejscem jej pierwszego wywołania.
Funkcja w powłoce Bash: nazwany fragment kodu skryptu, który może być wielokrotnie wywoływany w trakcie działania procesu powłoki. Funkcje działają w bieżącym środowisku powłoki (nie tworzą domyślnie podpowłoki), co umożliwia im modyfikację stanu i zmiennych skryptu oraz współdzielenie otwartych deskryptorów plików.
# Sposób 1 (preferowany)
nazwa_funkcji() {
    # Ciało funkcji
    # ...
}

# Sposób 2 (ze słowem kluczowym 'function')
function nazwa_innej_funkcji {
    # Ciało funkcji
    # ...
}
28/41
Funkcje: przekazywanie argumentów
Slajd omawia mechanizm przekazywania argumentów do funkcji oraz izolowanie zmiennych lokalnych.

Uczynienie funkcji bardziej elastycznymi

  • Parametry pozycyjne ($1, $2): funkcja odczytuje przekazane jej dane za pomocą zmiennych pozycyjnych $1, $2 ... $9.
  • Licznik argumentów ($#): zmienna $# informuje funkcję o dokładnej liczbie przekazanych parametrów wejściowych.
  • Kluczowe słowo local: deklaracja local zmienna zapobiega nadpisywaniu zmiennych globalnych o tej samej nazwie.
  • Wszystkie argumenty ($@): zmienna "$@" reprezentuje kompletną listę przekazanych argumentów w nienaruszonej formie.
Zmienna lokalna (local): zmienna zadeklarowana wewnątrz ciała funkcji za pomocą słowa kluczowego local, której zasięg i czas życia są ograniczone wyłącznie do danej funkcji i jej podprogramów. Zapobiega powstawaniu trudnych do wykrycia efektów ubocznych i konfliktów nazw ze zmiennymi globalnymi skryptu.
#!/bin/bash

# Funkcja, która przyjmuje dwa argumenty
powitaj_uzytkownika() {
    # Sprawdzenie, czy podano odpowiednią liczbę argumentów
    if [[ $# -ne 2 ]]; then
        echo "Błąd funkcji: Oczekiwano 2 argumentów (imię, miasto)."
        return 1 # Zwróć kod błędu
    fi

    local imie=$1   # Użycie 'local' dla zmiennych wewnątrz funkcji
    local miasto=$2
    
    echo "Witaj, $imie z miasta $miasto!"
}

# Wywołania funkcji z różnymi argumentami
powitaj_uzytkownika "Anna" "Kraków"
powitaj_uzytkownika "Marek" "Warszawa"

# Wywołanie z nieprawidłową liczbą argumentów
powitaj_uzytkownika "Piotr"
29/41
Funkcje: zwracanie wartości
Slajd wyjaśnia mechanizm zwracania wyników z funkcji i rozróżnienie kodu wyjścia od danych wyjściowych.

Dwa sposoby "zwracania" wartości z funkcji

  • Instrukcja return: zwraca wyłącznie jednobajtowy kod statusu wykonania (0-255); nie służy do przekazywania tekstu.
  • Zwracanie danych przez stdout: funkcja wypisuje wynik za pomocą echo, a wywołujący przechwytuje go przez wynik=$(funkcja).
  • Referencje (declare -n): nowoczesny mechanizm przekazywania zmiennej przez referencję dostępny od Basha 4.3.
  • Konwencja sukcesu: zwrócenie return 0 oznacza pomyślne wykonanie; każda inna wartość (1-255) sygnalizuje błąd.
Wartość zwracana z funkcji: mechanizm przekazywania rezultatu działania funkcji w powłoce. Składa się z dwóch niezależnych kanałów: jednobajtowego kodu zakończenia (0-255) zwracanego instrukcją return i odczytywanego przez $?, oraz strumienia danych tekstowych emitowanego na stdout i przechwytywanego przez podstawienie polecenia $(...).
#!/bin/bash

# 1. Funkcja zwracająca kod wyjścia
sprawdz_plik() {
    if [[ -f "$1" ]]; then
        return 0 # Sukces
    else
        return 1 # Błąd
    fi
}

# 2. Funkcja "zwracająca" tekst przez stdout
dodaj_dwie_liczby() {
    local suma=$(($1 + $2))
    echo "$suma"
}

# Użycie funkcji z kodem wyjścia
if sprawdz_plik "/etc/hosts"; then
    echo "Plik /etc/hosts istnieje."
else
    echo "Plik /etc/hosts nie istnieje."
fi
echo "Kod wyjścia funkcji sprawdz_plik: $?"


# Użycie funkcji zwracającej tekst
WYNIK_DODAWANIA=$(dodaj_dwie_liczby 15 27)
echo "Wynik dodawania 15 i 27 to: $WYNIK_DODAWANIA"
30/41
Argumenty wywołania skryptu
Slajd przedstawia obsługę parametrów pozycyjnych przekazywanych do skryptu w momencie jego wywołania z CLI.

Przekazywanie danych do skryptu z zewnątrz

  • Nazwa skryptu ($0): zmienna $0 przechowuje ścieżkę lub nazwę pliku skryptu użytą w wierszu poleceń.
  • Argumenty skryptu ($1..$9): dostęp do poszczególnych parametrów wywołania; argumenty powyżej dziewiątego wymagają klamer (${10}).
  • Licznik argumentów ($#): zmienna przechowująca całkowitą liczbę parametrów przekazanych z linii poleceń.
  • Przesuwanie argumentów (shift): instrukcja shift usuwająca pierwszy argument i przesuwająca pozostałe w lewo ($2 staje się $1).
Parametry pozycyjne skryptu: specjalne zmienne wbudowane w powłokę ($0, $1, $2 ... $N), które są automatycznie inicjalizowane wartościami argumentów wpisanych przez użytkownika w wierszu poleceń podczas uruchamiania skryptu. Umożliwiają tworzenie elastycznych narzędzi CLI reagujących na parametry wywołania.
#!/bin/bash
# Uruchom ten skrypt np. tak:
# ./moj_skrypt.sh "Jan Kowalski" /tmp/logs _backup

echo "Nazwa skryptu: $0"
echo "Liczba przekazanych argumentów: $#"
echo "--- Poszczególne argumenty ---"
echo "Argument 1: $1"
echo "Argument 2: $2"
echo "Argument 3: $3"
echo "------------------------------"

# Pętla po wszystkich argumentach
echo "Iteracja po wszystkich argumentach za pomocą 'for arg in \"\$@\"':"
for arg in "$@"; do
    echo "  - Przetwarzam argument: '$arg'"
done
31/41
Argumenty: praktyczny przykład i walidacja
Slajd prezentuje wzorce walidacji parametrów wejściowych oraz generowania komunikatów pomocy (usage).

Sprawdzanie poprawności argumentów

  • Weryfikacja liczby argumentów: sprawdzanie warunku [[ $# -lt WARTOSC ]] na samym początku skryptu przed rozpoczęciem pracy.
  • Funkcja pomocy (usage): dedykowana procedura wypisująca składnię, dostępne flagi i przykłady użycia narzędzia.
  • Wypisywanie na stderr: kierowanie komunikatów o błędach składni na strumień błędów za pomocą echo ... >&2.
  • Kod błędu (exit 1): bezwzględne przerywanie skryptu kodem różnym od zera po wykryciu niepoprawnych parametrów.
Walidacja argumentów: zespół instrukcji kontrolnych umieszczanych na początku skryptu, których zadaniem jest sprawdzenie poprawności liczby, typu oraz formatu parametrów przekazanych przez użytkownika. W przypadku wykrycia błędów skrypt wyświetla instrukcję użycia (usage) i kończy pracę z kodem błędu, zapobiegając uszkodzeniu danych.
#!/bin/bash
# Skrypt do tworzenia kopii zapasowej pliku.
# Oczekuje dwóch argumentów: pliku źródłowego i katalogu docelowego.

# Walidacja liczby argumentów
if [[ $# -ne 2 ]]; then
    echo "Błąd: Nieprawidłowa liczba argumentów!"
    echo "Użycie: $0 <katalog_do_archiwizacji> "
    exit 1
fi

PLIK_ZRODLOWY=$1
KATALOG_DOCELOWY=$2

# Walidacja, czy źródło istnieje i jest plikiem
if ! [[ -f "$PLIK_ZRODLOWY" ]]; then
    echo "Błąd: Plik źródłowy '$PLIK_ZRODLOWY' nie istnieje."
    exit 1
fi

# Walidacja, czy cel istnieje i jest katalogiem
if ! [[ -d "$KATALOG_DOCELOWY" ]]; then
    echo "Błąd: Katalog docelowy '$KATALOG_DOCELOWY' nie istnieje."
    exit 1
fi

echo "Kopiowanie '$PLIK_ZRODLOWY' do '$KATALOG_DOCELOWY'..."
cp -v "$PLIK_ZRODLOWY" "$KATALOG_DOCELOWY"
echo "Kopia zapasowa zakończona sukcesem."
32/41
Przekierowanie strumieni: wprowadzenie
Slajd wprowadza standardowe deskryptory wejścia i wyjścia w systemach uniksowych i zasady ich przekierowywania.

Standardowe strumienie I/O

  • Trzy strumienie standardowe: każdy proces uniksowy posiada otwarte strumienie: stdin (0), stdout (1) oraz stderr (2).
  • Przekierowanie wejścia (<): zastąpienie klawiatury plikiem dyskowym jako źródłem danych dla programu.
  • Przekierowanie wyjścia (>): zapisanie standardowego wyjścia do pliku z nadpisaniem jego dotychczasowej zawartości.
  • Dopisywanie do pliku (>>): dołączenie wyjścia programu na koniec istniejącego pliku bez niszczenia wcześniejszych danych.
Deskryptor pliku (File Descriptor): całkowitoliczbowy identyfikator używany przez jądro systemu operacyjnego do śledzenia otwartych plików i strumieni w danym procesie. Każdy standardowy proces powłoki startuje z trzema zdefiniowanymi deskryptorami: 0 (standardowe wejście stdin), 1 (standardowe wyjście stdout) oraz 2 (standardowe wyjście błędów stderr).
  Klawiatura           Ekran
 (stdin, 0) <--- [   PROCES   ] ---> (stdout, 1)
                  [ (program)  ] ---> (stderr, 2)
33/41
Przekierowanie wyjścia (stdout i stderr)
Slajd omawia zaawansowane techniki przekierowywania strumieni stdout i stderr do plików oraz urządzenia /dev/null.

Zapisywanie wyników i błędów do plików

  • Rozdzielenie danych i błędów: zapis wyników do pliku (1> plik.log) oraz błędów do osobnego rejestru (2> err.log).
  • Łączenie strumieni (2>&1): przekierowanie deskryptora błędów 2 w to samo miejsce, gdzie skierowano strumień 1.
  • Skrót składniowy (&>): nowoczesna składnia Basha kierująca jednocześnie stdout i stderr do jednego pliku.
  • Czarna dziura (/dev/null): przekierowanie niechcianych komunikatów do urządzenia wirtualnego /dev/null w celu ich wyciszenia.
Urządzenie /dev/null: specjalny plik wirtualny w systemach uniksowych, który bezpowrotnie odrzuca wszystkie zapisywane do niego dane, potwierdzając natychmiast sukces operacji zapisu. Próba odczytu z /dev/null natychmiast zwraca znak końca pliku (EOF). Powszechnie stosowany w skryptach do wyciszania komunikatów (np. 2> /dev/null).
#!/bin/bash

# Przekieruj listę plików do pliku (nadpisz)
ls -l /etc > /tmp/lista_etc.txt

# Dopisz listę katalogu domowego do tego samego pliku
ls -l "$HOME" >> /tmp/lista_etc.txt

# Uruchom polecenie, które wygeneruje zarówno wynik, jak i błąd
# 'find /etc' - poprawny wynik
# 'find /root' - błąd braku uprawnień
find /etc /root -maxdepth 1 -name "hosts" > /tmp/wyniki.txt 2> /tmp/bledy.txt

# Przekieruj wszystko do jednego pliku (nowa składnia)
date &> /tmp/data_log.txt
ls /nie/istnieje &>> /tmp/data_log.txt

# Wyczyszczenie
# rm /tmp/lista_etc.txt /tmp/wyniki.txt /tmp/bledy.txt /tmp/data_log.txt
34/41
Potoki (pipelines)
Slajd charakteryzuje mechanizm potoków uniksowych łączących procesy w wydajne ciągi przetwarzania danych.

Łączenie poleceń w łańcuchy

  • Operator potoku (|): łączy standardowe wyjście (stdout) programu po lewej ze standardowym wejściem (stdin) programu po prawej.
  • Równoległość procesów: wszystkie ogniwa potoku pracują równocześnie w oddzielnych procesach potomnych, synchronizowane buforem jądra.
  • Opcja pipefail: włączenie set -o pipefail wymusza zwrócenie kodu błędu, jeśli którekolwiek ogniwo potoku ulegnie awarii.
  • Tablica PIPESTATUS: wbudowana tablica Basha przechowująca kody zakończenia wszystkich poszczególnych komend potoku.
Potok uniksowy (Pipeline): jeden z najważniejszych mechanizmów komunikacji międzyprocesowej (IPC) w systemach uniksowych, reprezentowany w powłoce symbolem |. Umożliwia przekazywanie strumienia danych bezpośrednio w pamięci RAM pomiędzy procesami bez zapisywania plików tymczasowych na dysku, co radykalnie przyspiesza przetwarzanie informacji.
#!/bin/bash

# Przykład 1: Zliczanie plików w katalogu /bin
# ls -1 /bin -> listuje pliki, każdy w nowej linii
# wc -l      -> zlicza liczbę linii na swoim wejściu
echo "Liczba plików w /bin:"
ls -1 /bin | wc -l

echo ""

# Przykład 2: Znalezienie 5 największych plików w systemie
# du -ah / 2>/dev/null -> oblicza rozmiar wszystkich plików (błędy do kosza)
# sort -hr             -> sortuje wynik w formacie czytelnym dla człowieka (-h) w odwrotnej kolejności (-r)
# head -n 5            -> wyświetla pierwsze 5 linii
echo "Top 5 największych plików/katalogów w systemie:"
du -ah / 2>/dev/null | sort -hr | head -n 5

echo ""

# Przykład 3: Wyświetlenie zalogowanych użytkowników, bez duplikatów
# who   -> pokazuje, kto jest zalogowany
# awk '{print $1}' -> wycina pierwszą kolumnę (nazwy użytkowników)
# sort -u -> sortuje i usuwa duplikaty
echo "Aktualnie zalogowani unikalni użytkownicy:"
who | awk '{print $1}' | sort -u
35/41
Narzędzia do przetwarzania tekstu: `grep`
Slajd przedstawia narzędzie grep jako standardowy filtr do wyszukiwania wzorców tekstowych i wyrażeń regularnych.

Filtrowanie tekstu i wyszukiwanie wzorców

  • Wyszukiwanie tekstu: filtrowanie plików lub strumieni wejściowych w celu wypisania linii pasujących do wzorca.
  • Podstawowe przełączniki: flaga -i ignoruje wielkość liter, -v odwraca dopasowanie (negacja), a -n podaje numer wiersza.
  • Zliczanie trafień (-c): wypisanie liczby pasujących wierszy bez konieczności używania dodatkowego programu wc -l.
  • Wyrażenia regularne (-E): obsługa rozszerzonych wyrażeń regularnych (ERE) z operatorami powtórzeń i alternatywy.
Narzędzie grep: standardowy program wiersza poleceń w systemach uniksowych, którego nazwa wywodzi się z polecenia edytora ed: g/re/p (globally search for a regular expression and print). Służy do błyskawicznego przeszukiwania plików tekstowych i strumieni pod kątem występowania zadanych wzorców lub wyrażeń regularnych.
#!/bin/bash

# Znajdź linie zawierające 'root' w pliku /etc/passwd
echo "--- Użytkownik 'root' w /etc/passwd ---"
grep "root" /etc/passwd

echo ""

# Zlicz, ilu użytkowników używa powłoki Bash
echo "--- Liczba użytkowników z powłoką bash ---"
grep -c "bash" /etc/passwd

echo ""

# Wyświetl listę procesów, ale odfiltruj proces samego polecenia grep
echo "--- Procesy demona 'systemd' ---"
ps aux | grep "systemd" | grep -v "grep"

echo ""

# Znajdź wszystkie pliki .conf w /etc, które zawierają słowo 'network'
echo "--- Pliki konfiguracyjne ze słowem 'network' ---"
grep -r -i "network" /etc/*.conf 2>/dev/null
36/41
Narzędzia do przetwarzania tekstu: `sed`
Slajd omawia edytor strumieniowy sed jako narzędzie do automatycznej transformacji i edycji tekstu w locie.

Edytor strumieniowy

  • Edycja strumieniowa: przetwarzanie tekstu linia po linii bez konieczności otwierania interaktywnego edytora.
  • Operacja podstawienia (s/stary/nowy/g): zamiana wystąpień wzorca na nowy ciąg znaków; flaga g oznacza zamianę globalną w wierszu.
  • Edycja w miejscu (-i): modyfikacja zawartości pliku bezpośrednio na dysku bez konieczności tworzenia plików pomocniczych.
  • Usuwanie wierszy (d): kasowanie linii spełniających określony warunek lub pasujących do zadanego numeru.
Edytor strumieniowy sed (Stream Editor): potężne narzędzie uniksowe przeznaczone do automatycznego filtrowania i przekształcania tekstu. Działa w oparciu o cykl: pobranie wiersza do przestrzeni roboczej (pattern space), wykonanie sekwencji zdefiniowanych skryptem instrukcji edycyjnych i wypisanie wyniku na standardowe wyjście.
#!/bin/bash

# Przykład 1: Prosta zamiana
echo "Ala ma kota, a kot ma pchły." | sed 's/kot/pies/'
# Wynik: Ala ma piesa, a kot ma pchły. (tylko pierwsze wystąpienie)

# Przykład 2: Zamiana globalna (flaga 'g')
echo "Ala ma kota, a kot ma pchły." | sed 's/kot/pies/g'
# Wynik: Ala ma piesa, a pies ma pchły. (wszystkie wystąpienia)

# Przykład 3: Użycie na pliku
# Tworzymy plik tymczasowy
echo "user=admin" > /tmp/config.txt
echo "host=localhost" >> /tmp/config.txt

# Zmień 'localhost' na 'server.example.com' w pliku
# Opcja -i modyfikuje plik "w miejscu" (in-place)
# sed -i 's/localhost/server.example.com/' /tmp/config.txt
# cat /tmp/config.txt
# Wynik w pliku:
# user=admin
# host=server.example.com

# Przykład 4: Usunięcie linii (wzorzec 'd')
# Usuń wszystkie puste linie z pliku
# sed '/^$/d' plik.txt
37/41
Narzędzia do przetwarzania tekstu: `awk`
Slajd charakteryzuje język awk jako zaawansowane narzędzie do raportowania i strukturalnej analizy danych tabelarycznych.

Zaawansowane przetwarzanie kolumnowe

  • Struktura kolumnowa: automatyczny podział wiersza wejściowego na ponumerowane pola ($1, $2, $3 ... $NF).
  • Struktura programu: blokowa budowa algorytmu: sekcja wstępna BEGIN, reguły przetwarzania wierszy oraz sekcja końcowa END.
  • Separatory pól (-F): możliwość zdefiniowania własnego znaku podziału kolumn (np. dwukropek -F: dla /etc/passwd).
  • Pełny język programowania: wbudowane zmienne systemowe (NR - numer wiersza, NF - liczba kolumn), tablice asocjacyjne i funkcje.
Język awk: kompletny w sensie Turinga język programowania zorientowany na przetwarzanie wzorców i danych tabelarycznych, stworzony przez Aho, Weinbergera i Kernighana. Wyróżnia się automatycznym wczytywaniem wierszy, dzieleniem ich na kolumny oraz możliwością definiowania zaawansowanych raportów księgowych i telemetrycznych.
#!/bin/bash

# Przykład 1: Wyświetlenie pierwszej i trzeciej kolumny z wyniku 'ls -l'
# Wynik 'ls -l' wygląda mniej więcej tak:
# -rw-r--r-- 1 user group 1234 Oct 8 22:30 plik.txt
# Chcemy wyciąć 'user' i '1234'
# ls -l | awk '{print "Właściciel:", $3, "Rozmiar:", $5}'

# Przykład 2: Sumowanie rozmiarów plików
# Zsumuj piątą kolumnę (rozmiar) dla wszystkich plików
echo "Obliczanie całkowitego rozmiaru plików w /etc..."
# ls -l /etc | awk '/^-/ {sum += $5} END {print "Całkowity rozmiar:", sum, "bajtów"}'
# /^-/ -> warunek: wykonaj akcję tylko dla linii zaczynających się od '-' (pliki)
# {sum += $5} -> akcja: dodaj wartość 5. pola do zmiennej 'sum'
# END { ... } -> blok specjalny, wykonywany po przetworzeniu wszystkich linii

# Przykład 3: Zmiana separatora pól (opcja -F)
# Przetwarzanie pliku CSV (dane oddzielone przecinkami)
echo "user,pass,uid,gid" > /tmp/data.csv
echo "root,x,0,0" >> /tmp/data.csv
echo "daemon,x,1,1" >> /tmp/data.csv
awk -F',' '{print "Użytkownik:", $1, "ma UID:", $3}' /tmp/data.csv
38/41
Zarządzanie procesami
Slajd przedstawia techniki monitorowania, uruchamiania w tle i kontrolowania procesów z poziomu skryptu powłoki.

Kontrola nad uruchomionymi programami

  • Uruchamianie w tle (&): symbol & umieszczony na końcu polecenia uruchamia proces asynchronicznie, zwracając sterowanie do powłoki.
  • Zmienna $! (PID procesu w tle): rejestracja identyfikatora numerycznego ostatnio uruchomionego zadania asynchronicznego.
  • Oczekiwanie na proces (wait): polecenie wait wstrzymuje skrypt do momentu zakończenia działania wskazanego procesu potomnego.
  • Wysyłanie sygnałów (kill): sterowanie stanem procesów za pomocą sygnałów systemowych SIGTERM (15), SIGKILL (9) czy SIGHUP (1).
Zarządzanie procesami: zespół mechanizmów powłoki i jądra systemu operacyjnego umożliwiający kontrolowanie cyklu życia programów komputerowych. Obejmuje asynchroniczne uruchamianie zadań w tle, synchronizację procesów potomnych za pomocą polecenia wait oraz sterowanie stanem wykonania poprzez wysyłanie sygnałów uniksowych.
#!/bin/bash

# Uruchom proces w tle, który będzie działał przez 60 sekund
echo "Uruchamiam proces 'sleep 60' w tle..."
sleep 60 &
SLEEP_PID=$! # Specjalna zmienna $! przechowuje PID ostatniego procesu w tle

echo "Proces 'sleep' został uruchomiony z PID: $SLEEP_PID"
echo "Skrypt kontynuuje pracę, nie czekając na zakończenie 'sleep'."

# Sprawdź, czy proces o tym PID działa
# ps -p $SLEEP_PID zwróci 0, jeśli proces istnieje
if ps -p $SLEEP_PID > /dev/null 2>&1; then
    echo "Proces o PID $SLEEP_PID jest aktywny."
else
    echo "Proces o PID $SLEEP_PID już nie istnieje."
fi

# Po 5 sekundach zakończ proces
echo "Czekam 5 sekund, a następnie zatrzymuję proces..."
sleep 5
kill $SLEEP_PID

echo "Wysłano sygnał zakończenia do procesu $SLEEP_PID."
# Sprawdź ponownie
if ps -p $SLEEP_PID > /dev/null 2>&1; then
    echo "Proces nadal działa (być może potrzebuje 'kill -9')."
else
    echo "Proces został pomyślnie zatrzymany."
fi
39/41
Tablice (arrays)
Slajd omawia deklarację, indeksowanie i operacje na tablicach indeksowanych numerycznie oraz asocjacyjnych w Bashu.

Przechowywanie wielu wartości w jednej zmiennej

  • Tablice indeksowane: tworzenie kolekcji elementów za pomocą składni tablica=("a" "b" "c") o indeksach liczbowych od zera.
  • Odczyt wszystkich elementów (${tab[@]}): składnia ${tab[@]} rozwija wszystkie przechowywane wartości jako odrębne słowa.
  • Liczba elementów (${#tab[@]}): prefiks # przed nazwą tablicy zwraca całkowitą liczbę aktualnie zapisanych elementów.
  • Tablice asocjacyjne (declare -A): zaawansowane słowniki mapujące klucze tekstowe na wartości dostępne od Basha w wersji 4.0.
Tablica w powłoce Bash: struktura danych umożliwiająca przechowywanie wielu wartości pod jedną nazwą zmiennej. Bash obsługuje jednowymiarowe tablice indeksowane numerycznie (o nieograniczonym rozmiarze) oraz od wersji 4.0 tablice asocjacyjne (wymagające jawnej deklaracji declare -A), w których indeksami są dowolne ciągi znaków.
#!/bin/bash

# Deklaracja tablicy z serwerami
SERWERY=("web-01" "db-01" "api-gateway" "monitoring-server")

# Wyświetlenie pierwszego elementu (indeks 0)
echo "Pierwszy serwer na liście: ${SERWERY[0]}"

# Wyświetlenie liczby elementów
echo "Liczba serwerów do sprawdzenia: ${#SERWERY[@]}"

# Dodanie nowego serwera do tablicy
SERWERY+=("backup-01")

echo "Dodano nowy serwer. Aktualna lista:"

# Iteracja po wszystkich elementach tablicy
for srv in "${SERWERY[@]}"; do
    echo "  - $srv"
done

# Możemy też tworzyć tablicę z wyniku polecenia
# Uwaga: ta składnia może źle działać przy nazwach plików ze spacjami
PLIKI_TXT=($(find . -maxdepth 1 -name "*.txt"))
echo "Znaleziono ${#PLIKI_TXT[@]} plików .txt"
40/41
Tryby debugowania skryptów
Slajd charakteryzuje metody debugowania kodu skryptowego i reguły bezpiecznego programowania Bash Strict Mode.

Jak znaleźć błędy w skrypcie?

  • Tryb śledzenia (set -x): wypisywanie każdego wykonywanego polecenia wraz z rozwiniętymi argumentami poprzedzonego znakiem +.
  • Weryfikacja składni (set -n): sprawdzenie poprawności gramatycznej skryptu bez faktycznego uruchamiania poleceń.
  • Zatrzymanie na błędzie (set -e): natychmiastowe przerwanie pracy skryptu, jeśli którekolwiek polecenie zwróci kod błędu.
  • Wykrywanie niezdefiniowanych zmiennych (set -u): traktowanie odwołania do niezadeklarowanej zmiennej jako błędu krytycznego.
Bash Strict Mode (set -euo pipefail): zespół dyrektyw konfiguracyjnych umieszczanych na początku skryptów powłoki, który znacząco zwiększa bezpieczeństwo wykonania. Wymusza przerwanie skryptu w przypadku błędu polecenia (-e), odwołania do niezdefiniowanej zmiennej (-u) lub awarii dowolnego ogniwa potoku (pipefail), zapobiegając uszkodzeniu systemu.
#!/bin/bash

# Włącz tryb śledzenia, aby zobaczyć, co się dzieje
set -x

UŻYTKOWNIK="test"
KATALOG_DOMOWY="/home/$UŻYTKOWNI" # Celowa literówka w nazwie zmiennej!

echo "Tworzenie katalogu dla użytkownika: $UŻYTKOWNIK"

# Bez 'set -u' to polecenie stworzy katalog o nazwie '/home/'!
mkdir -p "$KATALOG_DOMOWY/public_html"

# Wyłącz tryb śledzenia
set +x

echo "Zakończono."

# Uruchomienie tego skryptu pokaże:
# + UŻYTKOWNIK=test
# + KATALOG_DOMOWY=/home/
# + echo 'Tworzenie katalogu dla użytkownika: test'
# + mkdir -p /home//public_html
# + set +x

# Gdybyśmy dodali 'set -u', skrypt zakończyłby się błędem
# w linii z deklaracją KATALOG_DOMOWY.
41/41
Projekt końcowy: skrypt do archiwizacji
Slajd integruje zdobytą wiedzę w kompletnym, produkcyjnym projekcie skryptu do automatycznej archiwizacji danych.

Podsumowanie wiedzy w praktycznym skrypcie

  • Walidacja parametrów: weryfikacja obecności argumentu ścieżki i sprawdzenie, czy podany katalog istnieje na dysku.
  • Generowanie unikalnej nazwy: utworzenie archiwum tar.gz z dynamicznym znacznikiem czasu pobranym poleceniem date.
  • Obsługa błędów i kody wyjścia: rejestracja statusu operacji kompresji i zwrócenie jednoznacznego kodu zakończenia do systemu.
  • Raportowanie telemetryczne: wypisanie podsumowania: ścieżki pliku wynikowego, jego rozmiaru w bajtach oraz czasu trwania.
Skrypt archiwizacji (Backup Script): kompletna aplikacja skryptowa automatyzująca proces tworzenia skompresowanych kopii bezpieczeństwa wskazanych zasobów dyskowych. Łączy w sobie obsługę argumentów CLI, sprawdzanie uprawnień, generowanie znaczników czasu, kompresję narzędziem tar oraz pełną obsługę błędów i kodów zakończenia.
#!/bin/bash

# --- Skrypt do automatycznej archiwizacji katalogów ---

# (set -e celowo pominięte, aby pokazać ręczne sprawdzanie kodów wyjścia)

# --- Zmienne globalne i stałe ---
readonly ARCHIVE_DIR="/mnt/backups"
readonly DATE_FORMAT=$(date "+%Y-%m-%d_%H-%M")

# --- Funkcje ---

# Funkcja do logowania komunikatów z datą
log() {
    echo "[$(date "+%Y-%m-%d %H:%M:%S")] - $1"
}

# Funkcja wyświetlająca sposób użycia skryptu
usage() {
    echo "Użycie: $0 <katalog_do_archiwizacji>"
    exit 1
}


# --- Główna logika skryptu ---

# 1. Walidacja argumentów
if [[ $# -ne 1 ]]; then
    log "Błąd: Nie podano katalogu do archiwizacji."
    usage
fi

SOURCE_DIR=$1

if ! [[ -d "$SOURCE_DIR" ]]; then
    log "Błąd: Podana ścieżka '$SOURCE_DIR' nie jest katalogiem."
    exit 1
fi

# 2. Przygotowanie nazwy archiwum i ścieżki
BASENAME=$(basename "$SOURCE_DIR")
ARCHIVE_FILENAME="${BASENAME}_${DATE_FORMAT}.tar.gz"
DESTINATION_PATH="${ARCHIVE_DIR}/${ARCHIVE_FILENAME}"

# 3. Utworzenie katalogu na archiwa, jeśli nie istnieje
log "Sprawdzanie katalogu na archiwa: $ARCHIVE_DIR"
mkdir -p "$ARCHIVE_DIR"

# 4. Wykonanie archiwizacji
log "Rozpoczynam archiwizację katalogu '$SOURCE_DIR'..."
log "Plik docelowy: $DESTINATION_PATH"

tar -czf "$DESTINATION_PATH" -C "$(dirname "$SOURCE_DIR")" "$BASENAME"

# 5. Weryfikacja i podsumowanie
if [[ $? -eq 0 ]]; then
    log "Archiwizacja zakończona pomyślnie!"
    log "Rozmiar archiwum: $(du -sh "$DESTINATION_PATH" | awk '{print $1}')"
else
    log "Błąd: Wystąpił problem podczas tworzenia archiwum."
    exit 1
fi

exit 0