1/10 Ogólne zasady budowy skryptów pod systemem Linux
2/10 1. Wprowadzenie do skryptów powłoki

Skrypt powłoki to nic innego jak plik tekstowy zawierający zestaw poleceń systemowych, które interpreter (powłoka) odczytuje i wykonuje sekwencyjnie, linia po linii. W systemie Linux powłoka stanowi interfejs między użytkownikiem a jądrem systemu operacyjnego. Każde polecenie wpisywane w terminalu jest w istocie wywołaniem powłoki, która je analizuje, a następnie uruchamia odpowiedni program lub wbudowaną funkcję systemu.

Skrypty pozwalają zautomatyzować powtarzalne zadania, które w przeciwnym razie wymagałyby ręcznego wpisywania wielu poleceń. Zamiast codziennie wykonywać te same czynności ręcznie, można napisać skrypt, który wykona je za nas jednym poleceniem. Skrypty są fundamentalnym narzędziem administratorów systemów, deweloperów oprogramowania oraz wszystkich użytkowników, którzy chcą zwiększyć swoją produktywność w pracy z systemem Linux.

W świecie Linuksa spotkamy się z różnymi powłokami: Bash (Bourne Again Shell) jest najpopularniejszy i stanowi domyślną powłokę w większości dystrybucji, Zsh oferuje rozszerzone możliwości konfiguracji, Fish wyróżnia się przyjazną składnią, a Sh (Bourne Shell) jest klasyczną powłoką stanowiącą podstawę dla wielu innych. Nasze rozważania będą dotyczyć głównie Basha, jako najbardziej uniwersalnej i szeroko stosowanej powłoki.

3/10 2. Interpretacja i kompilacja programów

Zrozumienie różnicy między interpretacją a kompilacją jest kluczowe dla świadomego pisania skryptów. Programy komputerowe mogą być przetwarzane na dwa fundamentalnie różne sposoby, z których każdy ma swoje zalety i wady.

2.1 Kompilacja

Kompilacja to proces tłumaczenia kodu źródłowego na kod maszynowy przed jego wykonaniem. Kompilator analizuje cały program, sprawdza poprawność syntaktyczną i semantyczną, a następnie generuje plik wykonywalny zawierający bezpośrednie instrukcje procesora. Programy skompilowane charakteryzują się wysoką wydajnością, ponieważ kod jest już w formie zrozumiałej dla komputera. Przykładami języków kompilowanych są C, C++, Go czy Rust.

Wadą kompilacji jest brak przenośności plików wykonywalnych między systemami (plik skompilowany dla Windows nie uruchomi się pod Linuksem) oraz konieczność rekompilacji po każdej zmianie w kodzie. Proces kompilacji może być też czasochłonny dla dużych projektów.

2.2 Interpretacja

Interpretacja polega na tym, że specjalny program zwany interpreterem czyta kod źródłowy linia po linii i na bieżąco tłumaczy go na polecenia wykonywane przez system. Interpreter analizuje każdą linię, rozpoznaje polecenia i argumenty, a następnie wywołuje odpowiednie funkcje systemowe. Skrypty powłoki są typowym przykładem kodu interpretowanego.

Zalety interpretacji to natychmiastowa możliwość uruchomienia po zapisaniu pliku, łatwa przenośność między systemami (pod warunkiem posiadania odpowiedniego interpretera) oraz prostota nauki i szybkie prototypowanie. Wady obejmują mniejszą wydajność w porównaniu z programami skompilowanymi oraz konieczność posiadania interpretera na docelowym systemie.

2.3 Skrypty jako kod interpretowany

Skrypty powłoki są z definicji kodami interpretowanymi. Oznacza to, że aby skrypt mógł zostać wykonany, system musi mieć zainstalowany odpowiedni interpreter (np. /bin/bash). Pierwsza linia skryptu, zwana shebang (#!), wskazuje systemowi, który interpreter należy użyć. Jest to fundamentalna cecha skryptów, którą zawsze należy pamiętać przy ich tworzeniu.

# Przykładowa pierwsza linia skryptu Bash
# Wskazuje, że skrypt ma być interpretowany przez /bin/bash
#!/bin/bash
# Inne możliwe shebang:
#!/bin/sh # POSIX shell (uniwersalny)
#!/usr/bin/python3 # Interpreter Pythona
#!/usr/bin/perl # Interpreter Perla
# Uruchomienie skryptu:
user@localhost:~$ ./moj_skrypt.sh
bash: ./moj_skrypt.sh: Permission denied
# Plik nie ma praw wykonywania! Musimy je nadać:
user@localhost:~$ chmod +x moj_skrypt.sh
user@localhost:~$ ./moj_skrypt.sh
Witaj w moim pierwszym skrypcie!
4/10 3. Tworzenie i struktura skryptów

Tworzenie skryptów wymaga przestrzegania kilku podstawowych zasad dotyczących struktury pliku, nazewnictwa oraz organizacji kodu. Profesjonalny skrypt powinien być nie tylko funkcjonalny, ale także czytelny dla innych osób, które mogą go później analizować lub modyfikować.

3.1 Podstawowa struktura skryptu

Każdy poprawny skrypt powłoki powinien zawierać określone elementy. Na samym początku umieszczamy shebang, który wskazuje interpreter. Następnie mogą pojawić się komentarze dokumentujące przeznaczenie skryptu, jego autora i sposób użycia. Za komentarzami wstępnymi umieszczamy właściwy kod wykonawczy, a skrypt powinien kończyć się jawnym komunikatem o stanie zakończenia.

#!/bin/bash
#==========================================================
# Nazwa skryptu: backup_home.sh
# Autor: Jan Kowalski
# Data utworzenia: 2026-03-26
# Opis: Tworzy kopię zapasową katalogu domowego użytkownika
# Sposób użycia: ./backup_home.sh [katalog_docelowy]
#==========================================================
# Włączenie trybu ścisłego (zalecane)
set -euo pipefail
# Definicja zmiennych konfiguracyjnych
KATALOG_ZRODLOWY="$HOME"
DATA=$(date +%Y-%m-%d)
LOG_FILE="/var/log/backup.log"
# Funkcja logująca
loguj() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Główna logika skryptu
loguj "Rozpoczęcie kopii zapasowej"
tar -czf "/tmp/backup_${DATA}.tar.gz" "$KATALOG_ZRODLOWY"
loguj "Kopia zapasowa utworzona: /tmp/backup_${DATA}.tar.gz"
exit 0

3.2 Komentarze w skryptach

Komentarze stanowią niezbędny element każdego profesjonalnego skryptu. W Bashu komentarz rozpoczyna się od znaku hash (#), a interpreter traktuje wszystko od tego znaku do końca linii jako tekst ignorowany. Komentarze pozwalają wyjaśnić działanie kodu, ułatwiają jego późniejszą modyfikację i są wyrazem profesjonalizmu autora.

Dobrą praktyką jest umieszczanie komentarzy wyjaśniających nie tylko CO robi dany fragment kodu, ale przede wszystkim DLACZEGO. Kod mówi JAK, komentarz powinien wyjaśniać CEL. Unikajmy oczywistych komentarzy typu "zwiększ licznik o 1" przy instrukcji "licznik=$((licznik+1))" – taki komentarz nie wnosi żadnej wartości informacyjnej.

#!/bin/bash
# Prawidłowe komentarze - wyjaśniają CEL i KONTEKST
# Ustawiamy locale na polskie, bo skrypt generuje raporty po polsku
export LC_ALL=pl_PL.UTF-8
# Sprawdzamy czy użytkownik ma uprawnienia roota
# bo operacje na /var/log wymagają sudo
if [ "$EUID" -ne 0 ]; then
echo "Uruchom skrypt jako root: sudo $0"
exit 1
fi
# Budujemy ścieżkę do pliku konfiguracyjnego
# Najpierw sprawdzamy katalog domowy użytkownika,
# a jeśli go brak - używamy katalogu tymczasowego
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
CONFIG_FILE="$CONFIG_DIR/moja_aplikacja.conf"
# ZŁY komentarz - nie wnosi informacji:
licznik=$((licznik + 1)) # zwiększ licznik o 1

3.3 Nazewnictwo plików skryptów

Konwencje nazewnictwa plików skryptów powinny być spójne i intuicyjne. Rozszerzenie .sh jest tradycyjnie używane dla skryptów powłoki, choć sam system operacyjny nie wymaga żadnego rozszerzenia – interpreter rozpoznaje plik na podstawie shebang. Dobrą praktyką jest używanie małych liter i myślników do oddzielania wyrazów, np. backup-home.sh zamiast BackupHome.sh czy backup_home.sh.

# Poprawne nazwy plików skryptów:
backup-system.sh
update-packages.sh
check-disk-space.sh
generate-report.sh
# Niepoprawne nazwy:
BackupSystem.sh # wielkie litery są niezalecane
backup system.sh # spacja utrudnia wywołanie
skrypt1.sh # brak informacji o przeznaczeniu
# Nadawanie praw wykonywania:
user@localhost:~$ chmod +x backup-system.sh
# Teraz możemy uruchomić:
user@localhost:~$ ./backup-system.sh
5/10 4. Zmienne w skryptach powłoki

Zmienne stanowią fundament każdego skryptu, pozwalając na przechowywanie danych tymczasowych, konfigurację zachowania skryptu oraz komunikację między różnymi jego częściami. Powłoka Bash operuje na zmiennych tekstowych, choć oferuje mechanizmy arytmetyczne do manipulacji liczbowymi wartościami.

4.1 Zmienne systemowe

System operacyjny Linux definiuje wiele zmiennych środowiskowych, które są dostępne dla wszystkich uruchomionych procesów. Zmienne te przechowują informacje o konfiguracji systemu, lokalizacjach katalogów, danych użytkownika i wielu innych aspektach środowiska. Znajomość najważniejszych zmiennych systemowych jest niezbędna przy pisaniu skryptów, które mają działać w różnych środowiskach.

# Wyświetlenie wartości zmiennej systemowej:
user@localhost:~$ echo $HOME
/home/user
user@localhost:~$ echo $USER
user
user@localhost:~$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
user@localhost:~$ echo $PWD
/home/user/scripts
user@localhost:~$ echo $HOSTNAME
localhost
# Lista wszystkich zmiennych środowiskowych:
user@localhost:~$ env
SHELL=/bin/bash
TERM=xterm-256color
USER=user
HOME=/home/user
...
# Sprawdzenie czy zmienna jest ustawiona:
user@localhost:~$ echo ${MOJA_ZMIENNA:-"nie ustawiona"}
nie ustawiona

4.2 Tworzenie i używanie zmiennych użytkownika

Zmienne definiowane przez użytkownika w skrypcie tworzymy poprzez przypisanie wartości do nazwy. Nazwa zmiennej może zawierać litery, cyfry i znaki podkreślenia, ale nie może zaczynać się od cyfry. Ważne jest, aby wokół znaku równości nie było spacji – jest to częsty błąd początkujących.

Dostęp do wartości zmiennej uzyskujemy poprzedzając jej nazwę znakiem dolara ($). Możemy też użyć składni ${zmienna}, która jest bardziej czytelna i pozwala na operacje na wartości zmiennej.

#!/bin/bash
# Poprawne przypisanie zmiennej (bez spacji wokół =)
IMIE="Anna"
WIEK=25
KATALOG="/home/user/dokumenty"
# Wyświetlenie wartości zmiennej
echo "Cześć, $IMIE!"
Cześć, Anna!
# Składnia ${zmienna} - przydatna przy konkatenacji
echo "Masz ${WIEK} lat."
Masz 25 lat.
# Zmienna z wynikiem polecenia
DZISIAJ=$(date +%Y-%m-%d)
PLIKI=$(ls -1 | wc -l)
echo "Dzisiaj: $DZISIAJ, plików: $PLIKI"
# CZĘSTY BŁĄD - spacja wokół =
# IMIE = "Anna" <- to NIE zadziała!
# Powłoka zinterpretuje to jako polecenie "IMIE" z argumentami "=" i "Anna"
# Długie wartości wielowierszowe
WIADOMOSC="To jest długa wiadomość
która może zawierać
wiele linii tekstu."

4.3 Zmienne lokalne i globalne

W Bashu rozróżniamy zmienne lokalne (dostępne tylko w bieżącej powłoce) oraz zmienne środowiskowe (eksportowane do procesów potomnych). Zmienna zdefiniowana w skrypcie bez użycia polecenia export będzie zmienną lokalną, widoczną tylko w obrębie tego skryptu i jego funkcji.

Polecenie export sprawia, że zmienna staje się częścią środowiska, która jest dziedziczona przez wszystkie uruchamiane z tej powłoki procesy potomne. Jest to mechanizm komunikacji między skryptami a programami, które uruchamiają.

#!/bin/bash
# Zmienna lokalna - widoczna tylko w tym skrypcie
ZMIENNA_LOKALNA="wartość lokalna"
# Zmienna eksportowana - dziedziczona przez procesy potomne
export ZMIENNA_GLOBALNA="wartość globalna"
# Uruchomienie podpowłoki (nowy proces)
bash -c 'echo "W podpowłoce: $ZMIENNA_LOKALNA"'
W podpowłoce: <- zmienna lokalna NIE jest widoczna
bash -c 'echo "W podpowłoce: $ZMIENNA_GLOBALNA"'
W podpowłoce: wartość globalna <- zmienna eksportowana JEST widoczna
# Sprawdzenie czy zmienna jest eksportowana
user@localhost:~$ export | grep ZMIENNA
declare -x ZMIENNA_GLOBALNA="wartość globalna"
# Usunięcie zmiennej
unset ZMIENNA_LOKALNA

4.4 Zmienne specjalne powłoki

Bash udostępnia szereg zmiennych specjalnych, które mają ustalone znaczenie i są automatycznie ustawiane przez powłokę. Zmienne te pozwalają na dostęp do argumentów skryptu, parametrów wykonania oraz informacji o bieżącym stanie powłoki.

#!/bin/bash
# $0 - nazwa bieżącego skryptu
echo "Uruchomiono: $0"
# $1, $2, ... $9 - argumenty przekazane do skryptu
echo "Pierwszy argument: $1"
echo "Drugi argument: $2"
# $# - liczba argumentów
echo "Liczba argumentów: $#"
# $@ - wszystkie argumenty jako tablica
echo "Wszystkie argumenty: $@"
# $? - kod wyjścia ostatniego polecenia
ls /tmp
echo "Kod wyjścia ls: $?"
Kod wyjścia ls: 0
# $$ - PID bieżącego procesu
echo "PID tego skryptu: $$"
# $! - PID ostatniego procesu uruchomionego w tle
sleep 60 &
echo "PID procesu sleep: $!"
# Wywołanie: ./skrypt.sh arg1 arg2 arg3
6/10 5. Przekierowanie strumieni i przetwarzanie potokowe

Jedną z najpotężniejszych cech systemów Unix i Linux jest koncepcja przekierowania strumieni. Każdy uruchomiony program domyślnie ma trzy otwarte strumienie: standardowe wejście (stdin) do czytania danych, standardowe wyjście (stdout) do wyświetlania wyników oraz standardowe wyjście błędów (stderr) do komunikatów o błędach. Przekierowanie pozwala zmieniać źródła i cele tych strumieni.

5.1 Podstawowe przekierowania

Operator > przekierowuje standardowe wyjście do pliku, tworząc go lub nadpisując jego zawartość. Operator >> przekierowuje wyjście, dopisując do istniejącego pliku. Operator < pobiera dane wejściowe z pliku zamiast z klawiatury.

# Przekierowanie wyjścia do pliku (nadpisuje)
user@localhost:~$ echo "Witaj świecie" > plik.txt
user@localhost:~$ cat plik.txt
Witaj świecie
# Dopisanie do pliku
user@localhost:~$ echo "Nowa linia" >> plik.txt
user@localhost:~$ cat plik.txt
Witaj świecie
Nowa linia
# Przekierowanie wejścia z pliku
user@localhost:~$ wc -l < plik.txt
2
# Przekierowanie stderr (numer strumienia 2)
user@localhost:~$ ls /nonexistent 2> errors.log
# stdout idzie na ekran, stderr do pliku errors.log
# Przekierowanie obu strumieni do tego samego pliku
user@localhost:~$ ls /tmp > all_output.txt 2>&1
# Lub nowsza składnia:
user@localhost:~$ ls /tmp &> all_output.txt
# Wyrzucenie wyjścia do /dev/null (kosz)
user@localhost:~$ ls /tmp > /dev/null 2>&1

5.2 Przetwarzanie potokowe (pipe)

Potoki (pipes) pozwalają łączyć polecenia w sekwencje, gdzie wyjście jednego programu staje się wejściem kolejnego. Jest to fundamentalna koncepcja filozofii Unix: „Rób jedną rzecz, ale rób ją dobrze”. Skomplikowane operacje można budować z prostych, wyspecjalizowanych poleceń połączonych potokami.

# Podstawowy potok - wyjście ls staje się wejściem sort
user@localhost:~$ ls /usr/bin | sort | head -10
arch
bash
bunzip2
...
# Zliczanie plików w katalogu
user@localhost:~$ ls -la /etc | wc -l
142
# Wyszukiwanie w output polecenia
user@localhost:~$ ps aux | grep bash
user 1234 0.0 0.1 12345 5678 pts/0 Ss+ 10:00 0:00 bash
# Zaawansowany przykład: top 10 procesów według użycia pamięci
user@localhost:~$ ps aux --sort=-%mem | head -11
USER PID %CPU %MEM COMMAND
mysql 1234 2.1 8.5 mysqld
nginx 2345 0.5 4.2 nginx
...
# Zamiana wielkich liter na małe w nazwach plików
user@localhost:~$ ls | tr '[:upper:]' '[:lower:]'
# Przykład w skrypcie - logowanie z timestamp
./skrypt.sh | while read linia; do
echo "[$(date +%H:%M:%S)] $linia"
done > log_z_czasem.txt

5.3 Here document i here string

Here document (<<) pozwala na wprowadzenie wielowierszowego tekstu bezpośrednio w skrypcie, bez potrzeby tworzenia osobnego pliku. Here string (<<<) przekazuje pojedynczą linię tekstu jako stdin. Są to wygodne mechanizmy do wprowadzania danych lub konfiguracji.

# Here document - wprowadzenie wielowierszowego tekstu
user@localhost:~$ cat << 'KONIEC'
To jest pierwsza linia
To jest druga linia
A to trzecia
KONIEC
# Here document z podstawieniem zmiennych
user@localhost:~$ cat << EOF
Użytkownik: $USER
Katalog domowy: $HOME
Dzisiaj: $(date +%Y-%m-%d)
EOF
# Here string - przekazanie zmiennej do polecenia
user@localhost:~$ wc -w <<< "ala ma kota i psa"
5
# Praktyczny przykład - generowanie pliku konfiguracyjnego
cat > config.ini << 'EOF'
[database]
host=$DB_HOST
port=5432
name=$DB_NAME
EOF
7/10 6. Przekazywanie argumentów do skryptu

Skrypty bardzo rzadko działają w izolacji – zazwyczaj muszą otrzymywać dane wejściowe, aby działać w różnych kontekstach. Argumenty wiersza poleceń stanowią podstawowy mechanizm komunikacji między użytkownikiem a skryptem, pozwalając na parametryzację zachowania programu.

6.1 Dostęp do argumentów pozycyjnych

Argumenty przekazane do skryptu są dostępne przez zmienne specjalne $1, $2, ... $9. Zmienna $0 zawiera nazwę skryptu, a $# przechowuje liczbę argumentów. Zmienna $* (lub $@) reprezentuje wszystkie argumenty jako jeden string lub tablicę.

#!/bin/bash
# Plik: przyklad_arg.sh
# Informacje o skrypcie i argumentach
echo "Nazwa skryptu: $0"
echo "Liczba argumentów: $#"
echo "Wszystkie argumenty: $*"
echo "Pierwszy argument: $1"
echo "Drugi argument: $2"
echo "Trzeci argument: $3"
# Pętla po wszystkich argumentach
echo "--- Lista argumentów ---"
for arg in "$@"; do
echo "Arg: $arg"
done
# Wywołanie:
# ./przyklad_arg.sh plik.txt katalog /backup
# Wynik:
Nazwa skryptu: ./przyklad_arg.sh
Liczba argumentów: 3
Wszystkie argumenty: plik.txt katalog /backup
Pierwszy argument: plik.txt
Drugi argument: katalog
Trzeci argument: /backup

6.2 Parsowanie opcji z getopts

Dla bardziej zaawansowanych skryptów, które przyjmują opcje w stylu Unix (z myślnikiem, np. -h, --help, -v), należy użyć polecenia getopts lub bardziej zaawansowanego getopt. Getopts przetwarza argumenty w pętli, rozpoznając opcje i ich ewentualne wartości.

#!/bin/bash
# Skrypt z obsługą opcji: -n nazwa, -v (verbose), -h (help)
VERBOSE=false
NAZWA=""
usage() {
echo "Sposób użycia: $0 [-n nazwa] [-v] [-h]"
echo " -n Podaj nazwę"
echo " -v Tryb szczegółowy"
echo " -h Pokaż pomoc"
}
# Parsowanie opcji
while getopts "n:vh" opt; do
case $opt in
n)
NAZWA="$OPTARG"
echo "Podano nazwę: $NAZWA"
;;
v)
VERBOSE=true
echo "Tryb szczegółowy włączony"
;;
h)
usage
exit 0
;;
\?)
echo "Nieznana opcja: -$OPTARG" >&2
usage
exit 1
;;
esac
done
# Przesunięcie argumentów, aby dostać się do pozostałych
shift $((OPTIND-1))
# Reszta argumentów (bez opcji)
echo "Pozostałe argumenty: $@"
# Wywołanie:
# ./skrypt.sh -n "MojaNazwa" -v plik1 plik2

6.3 Walidacja argumentów

Przed rozpoczęciem właściwej pracy skrypt powinien zawsze sprawdzać poprawność otrzymanych argumentów. Brak walidacji może prowadzić do nieprzewidywalnych błędów, uszkodzenia danych lub potencjalnych luk bezpieczeństwa. Walidacja powinna obejmować sprawdzenie liczby argumentów, typu danych oraz istnienia plików czy katalogów.

#!/bin/bash
# Sprawdzenie minimalnej liczby argumentów
if [ "$#" -lt 2 ]; then
echo "Błąd: Zbyt mało argumentów!" >&2
echo "Użycie: $0 PLIK KATALOG" >&2
exit 1
fi
PLIK="$1"
KATALOG="$2"
# Sprawdzenie czy plik istnieje
if [ ! -f "$PLIK" ]; then
echo "Błąd: Plik '$PLIK' nie istnieje!" >&2
exit 2
fi
# Sprawdzenie czy katalog istnieje i jest katalogiem
if [ ! -d "$KATALOG" ]; then
echo "Błąd: '$KATALOG' nie jest katalogiem!" >&2
exit 3
fi
# Sprawdzenie czy katalog jest zapisywalny
if [ ! -w "$KATALOG" ]; then
echo "Błąd: Brak uprawnień do zapisu w '$KATALOG'!" >&2
exit 4
fi
# Wszystkie walidacje przeszły - można kontynuować
cp "$PLIK" "$KATALOG/"
echo "Plik skopiowany pomyślnie"
8/10 7. Skrypty interaktywne i wsadowe

Skrypty mogą działać w dwóch podstawowych trybach: interaktywnym, gdzie użytkownik wprowadza dane w odpowiedzi na pytania skryptu, oraz wsadowym (batch), gdzie skrypt wykonuje wszystkie operacje automatycznie bez interwencji użytkownika. Wybór trybu zależy od konkretnego zastosowania i wymagań.

7.1 Skrypty interaktywne

Skrypty interaktywne komunikują się z użytkownikiem poprzez terminal, zadając pytania i oczekując odpowiedzi. Polecenie read służy do pobierania danych od użytkownika. Jest to przydatne gdy skrypt ma działać w sposób elastyczny, dostosowując się do decyzji użytkownika w trakcie wykonania.

#!/bin/bash
# Podstawowe wczytywanie danych od użytkownika
echo "Podaj swoje imię:"
read IMIE
echo "Cześć, $IMIE!"
# Wczytanie z promptem w jednej linii
read -p "Podaj wiek: " WIEK
echo "Masz $WIEK lat"
# Wczytanie hasła (bez wyświetlania)
read -sp "Podaj hasło: " HASLO
echo "" # Nowa linia po haśle
# Wczytanie z domyślną wartością
read -p "Katalog [domyślny: /tmp]: " KATALOG
KATALOG="${KATALOG:-/tmp}"
# Limit czasu na odpowiedź
read -t 10 -p "Masz 10 sekund na odpowiedź: " ODPOWIEDZ
if [ -z "$ODPOWIEDZ" ]; then
echo "Czas minął!"
fi
# Wczytanie tablicy (wiele słów)
echo "Podaj imiona (Enter aby zakończyć):"
read -a IMIONA
echo "Pierwsze imię: ${IMIONA[0]}"

7.2 Idea przetwarzania wsadowego

Tryb wsadowy zakłada, że skrypt wykonuje wszystkie operacje automatycznie, bez zatrzymywania się na pytania do użytkownika. Wszystkie potrzebne dane są przekazywane jako argumenty wiersza poleceń, zmienne środowiskowe lub pliki konfiguracyjne. Tryb wsadowy jest niezbędny przy automatyzacji zadań, uruchamianiu skryptów z crona czy integracji z innymi systemami.

#!/bin/bash
# Skrypt wsadowy - nie wymaga interakcji
# Konfiguracja z pliku lub zmiennych
KATALOG_ZRODLOWY="${1:-/home/data}"
KATALOG_DOCELOWY="${2:-/backup}"
LOG_FILE="${LOG_FILE:-/var/log/batch.log}"
loguj() {
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $1" >> "$LOG_FILE"
}
loguj "Rozpoczęcie przetwarzania wsadowego"
loguj "Źródło: $KATALOG_ZRODLOWY"
loguj "Cel: $KATALOG_DOCELOWY"
# Automatyczne wykonanie bez pytań
rsync -av "$KATALOG_ZRODLOWY/" "$KATALOG_DOCELOWY/"
# Sprawdzenie wyniku
if [ $? -eq 0 ]; then
loguj "Przetwarzanie zakończone sukcesem"
else
loguj "BŁĄD: Przetwarzanie nie powiodło się"
fi
# Uruchomienie wsadowe:
# LOG_FILE=/var/log/moj_log.txt ./batch_skrypt.sh /src /dest

7.3 Komunikaty i informacje zwrotne

Komunikaty w skryptach pełnią kluczową rolę w informowaniu użytkownika o postępie operacji, błędach i wynikach. Dobrze zaprojektowany skrypt powinien wizualnie rozróżniać różne typy komunikatów: zwykłe informacje, ostrzeżenia i błędy. Pozwala to użytkownikowi szybko ocenić stan wykonania skryptu.

#!/bin/bash
# Różne poziomy komunikatów
info() {
echo "[INFO] $1"
}
warn() {
echo "[UWAGA] $1" >&2
}
error() {
echo "[BŁĄD] $1" >&2
}
success() {
echo "[OK] $1"
}
# Przykładowe użycie
info "Rozpoczynam instalację"
info "Sprawdzanie zależności..."
if ! command -v curl &> /dev/null; then
error "curl nie jest zainstalowany!"
exit 1
fi
warn "Katalog /tmp jest pełny, używam /var/tmp"
success "Instalacja zakończona"
# Progres (pasek postępu)
for i in {1..100}; do
echo -ne "\rPostęp: [$i%]"
sleep 0.1
done
echo ""
9/10 8. Automatyzacja zadań - harmonogramy

Prawdziwa potęga skryptów ujawnia się, gdy połączymy je z mechanizmami automatycznego uruchamiania. Zamiast ręcznie wykonywać skrypty w określonych momentach, możemy zaplanować ich wykonanie w harmonogramie. W systemie Linux służy do tego demon cron, który uruchamia zadania o określonych porach bez interwencji użytkownika.

8.1 Wprowadzenie do crona

Cron to usługa (demon) działająca w tle systemu, która czyta tabelę crontab i uruchamia zadania o określonych porach. Każdy użytkownik może mieć własną tabelę crontab, definiującą kiedy i jakie polecenia mają być wykonane. System cron jest idealny do automatyzacji zadań konserwacyjnych, backupów, aktualizacji i wielu innych operacji.

# Wyświetlenie tabeli crontab użytkownika
user@localhost:~$ crontab -l
no crontab for user
# Edycja tabeli crontab
user@localhost:~$ crontab -e
# Struktura wpisu crontab:
# ┌───────────── minuta (0-59)
# │ ┌───────────── godzina (0-23)
# │ │ ┌───────────── dzień miesiąca (1-31)
# │ │ │ ┌───────────── miesiąc (1-12)
# │ │ │ │ ┌───────────── dzień tygodnia (0-7, 0 i 7 to niedziela)
# │ │ │ │ │
# │ │ │ │ │ polecenie
# * * * * * /sciezka/do/polecenia
# Przykłady:
0 * * * * /home/user/scripts/hourly.sh # co godzinę
0 0 * * * /home/user/scripts/daily.sh # codziennie o północy
0 2 * * 0 /home/user/scripts/weekly.sh # co niedzielę o 2:00
0 3 1 * * /home/user/scripts/monthly.sh # pierwszego dnia miesiąca o 3:00

8.2 Zaawansowane wzorce cron

Cron oferuje zaawansowane możliwości definiowania harmonogramów, w tym wartości listowe, zakresy i wartości z krokiem. Pozwalają one na precyzyjne planowanie zadań bez konieczności tworzenia wielu wpisów dla podobnych harmonogramów.

# Zaawansowane wzorce cron
# Co 15 minut
*/15 * * * * /scripts/check.sh
# W dni robocze o 8:00
0 8 * * 1-5 /scripts/workday.sh
# O 9:00, 12:00 i 18:00
0 9,12,18 * * * /scripts/meals.sh
# Pierwszy i piętnasty każdego miesiąca
0 0 1,15 * * /scripts/payday.sh
# Co 2 godziny w weekendy
0 */2 * * 0,6 /scripts/weekend.sh
# Zmienne środowiskowe w crontab
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@firma.pl
HOME=/home/user
# Pełny przykład crontab:
cat << 'EOF' | crontab -
# backup codzienny o 2:00
0 2 * * * /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1
# raport co poniedziałek o 7:00
0 7 * * 1 /home/user/scripts/report.sh
# monitoring co 5 minut
*/5 * * * * /home/user/scripts/monitor.sh
EOF

8.3 Alternatywy dla crona

Oprócz tradycyjnego crona, Linux oferuje inne mechanizmy planowania zadań. Systemd timer to nowoczesne rozwiązanie stosowane w wielu dystrybucjach, oferujące większą kontrolę i lepszą integrację z systemem. Polecenie at pozwala na jednorazowe zaplanowanie zadania w przyszłości, co jest przydatne gdy zadanie ma być wykonane tylko raz.

# Systemd timer - definicja usługi
cat > ~/.config/systemd/user/backup.service << 'EOF'
[Unit]
Description=Backup katalogu domowego
[Service]
Type=oneshot
ExecStart=/home/user/scripts/backup.sh
[Install]
WantedBy=default.target
EOF
# Systemd timer - definicja harmonogramu
cat > ~/.config/systemd/user/backup.timer << 'EOF'
[Unit]
Description=Timer dla backupu
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Uruchomienie i włączenie timera
user@localhost:~$ systemctl --user enable backup.timer
user@localhost:~$ systemctl --user start backup.timer
# Polecenie at - jednorazowe zaplanowanie
user@localhost:~$ echo "/home/user/scripts/raport.sh" | at 14:30
job 1 at Thu Mar 26 14:30:00 2026
user@localhost:~$ echo "/home/user/scripts/shutdown.sh" | at now + 1 hour
job 2 at Thu Mar 26 15:30:00 2026
# Sprawdzenie kolejki at
user@localhost:~$ atq
1 Thu Mar 26 14:30:00 2026 a user

8.4 Anacron - zadania dla systemów niepracujących 24/7

Anacron to narzędzie uzupełniające crona, przeznaczone głównie dla komputerów stacjonarnych i laptopów, które nie działają przez całą dobę. Anacron gwarantuje, że zadania zostaną wykonane nawet jeśli komputer był wyłączony w planowanym momencie – po włączeniu systemu uruchomi zaległe zadania automatycznie.

# Konfiguracja anacron (plik /etc/anacrontab)
root@localhost:~# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# period in days delay in minutes job identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 10 cron.weekly nice run-parts /etc/cron.weekly
30 15 cron.monthly nice run-parts /etc/cron.monthly
# Dodanie własnego zadania do anacron
root@localhost:~# cat >> /etc/anacrontab << 'EOF'
# Codzienny backup o 5 minut po starcie systemu
1 5 daily.backup /home/user/scripts/backup.sh
EOF
# Pliki timestamp anacron (śledzenie wykonania)
user@localhost:~$ ls -la /var/spool/anacron/
-rw------- 1 root root 27 Mar 26 10:00 cron.daily
-rw------- 1 root root 27 Mar 20 02:00 cron.weekly
# Ręczne uruchomienie zadania anacron
root@localhost:~# anacron -f daily.backup
10/10 9. Profesjonalne praktyki pisania skryptów

Pisanie skryptów na poziomie produkcyjnym wymaga stosowania sprawdzonych praktyk, które zapewniają niezawodność, bezpieczeństwo i łatwość utrzymania kodu. Poniższe zasady stanowią fundament profesjonalnego podejścia do tworzenia skryptów powłoki.

9.1 Tryb ścisły (strict mode)

Włączenie trybu ścisłego w Bashu powoduje, że interpreter natychmiast przerywa wykonanie skryptu w przypadku wystąpienia błędu (set -e), przy próbie użycia niezdefiniowanej zmiennej (set -u) oraz gdy jakieś polecenie w potoku zakończy się błędem (set -o pipefail). Znacząco zmniejsza to ryzyko ukrytych błędów i nieprzewidywalnego zachowania.

#!/bin/bash
# Włączenie trybu ścisłego - OBOWIĄZKOWE w profesjonalnych skryptach
set -euo pipefail
# Lub bardziej rozbudowana wersja z wyjaśnieniami:
set -o errexit # -e: zakończ przy błędzie polecenia
set -o nounset # -u: błąd przy niezdefiniowanej zmiennej
set -o pipefail # błąd potoku gdy jakiekolwiek polecenie się nie powiedzie
# Korzyści:
# - nieprzypadkowe użycie niezdefiniowanej zmiennej
# - skrypt nie kontynuuje po błędzie krytycznym
# - błędy w potokach są wykrywane
# Przykład - bezpieczne pobranie zmiennej z wartością domyślną
KATALOG="${MOJA_ZMIENNA:-/domyslny/katalog}"
# Z trybem ścisłym: set -u wyrzuci błąd dla ${NIEISTNIEJACA}
# Z operatorem :- używamy wartości domyślnej (bez przypisania)

9.2 Obsługa błędów

Profesjonalny skrypt powinien przewidywać możliwe błędy i odpowiednio na nie reagować. Obejmuje to walidację danych wejściowych, sprawdzanie wyników operacji krytycznych oraz czyszczenie zasobów w przypadku nagłego zakończenia (trap).

#!/bin/bash
set -euo pipefail
# Funkcja wykonywana przy wyjściu ze skryptu
cleanup() {
local exit_code=$?
# Czyszczenie tymczasowych plików
rm -f "$TEMP_FILE" 2>/dev/null || true
# Zwolnienie zasobów
umount /mnt/temp 2>/dev/null || true
echo "Kod wyjścia: $exit_code"
exit $exit_code
}
# Ustawienie pułapki na sygnały i wyjście
trap cleanup EXIT INT TERM
# Plik tymczasowy
TEMP_FILE=$(mktemp)
# Funkcja do sprawdzania błędów
sprawdz_blad() {
if [ $? -ne 0 ]; then
echo "BŁĄD w linii $1: $2" >&2
exit 1
fi
}
# Użycie funkcji sprawdzającej
cp "$PLIK" "$KATALOG"
sprawdz_blad $LINENO "Nie udało się skopiować pliku"

9.3 Szablon profesjonalnego skryptu

Stosowanie spójnego szablonu dla wszystkich skryptów ułatwia ich późniejszą analizę i utrzymanie. Szablon powinien zawierać: shebang, komentarze dokumentacyjne, włączenie trybu ścisłego, definicję zmiennych konfiguracyjnych, funkcje pomocnicze, główną logikę oraz obsługę błędów.

#!/bin/bash
#============================================================
# Skrypt: system-info.sh
# Cel: Wyświetla podstawowe informacje o systemie
# Autor: Jan Kowalski
# Wersja: 1.0.0
#============================================================
set -euo pipefail
# Zmienne konfiguracyjne
SCRIPT_NAME="$(basename "$0")"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_FILE="/var/log/${SCRIPT_NAME%.sh}.log"
# Kolory dla output
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
# Funkcje pomocnicze
loguj() { echo "[$(date +%H:%M:%S)] $1"; }
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
blad() { echo -e "${RED}[BŁĄD]${NC} $1" >&2; exit 1; }
# Sprawdzenie uprawnień
if [ "$EUID" -eq 0 ]; then
blad "Nie uruchamiaj jako root!"
fi
# Główna logika
main() {
info "Pobieranie informacji o systemie..."
echo "Host: $(hostname)"
echo "Kernel: $(uname -r)"
echo "Uptime: $(uptime -p)"
}
# Wywołanie main z obsługą argumentów
main "$@"
10/10 10. Podsumowanie

Skrypty powłoki w systemie Linux to niezwykle potężne narzędzie do automatyzacji zadań administracyjnych i codziennych operacji. Opanowanie podstawowych zasad ich budowy – od poprawnego shebang, przez zarządzanie zmiennymi, przekierowania strumieni i potoki, aż po argumenty i harmonogramowanie – pozwala na tworzenie efektywnych i niezawodnych rozwiązań. Kluczowe elementy to zrozumienie różnicy między interpretacją a kompilacją, stosowanie komentarzy i czytelnej organizacji kodu, świadome używanie zmiennych lokalnych i środowiskowych, wykorzystywanie potoków do przetwarzania danych, parametryzacja skryptów za pomocą argumentów oraz automatyzacja z użyciem crona i systemd.

Pamiętaj, że profesjonalny skrypt zawsze sprawdza poprawność danych wejściowych, obsługuje błędy, loguje swoje działanie i zwraca odpowiednie kody wyjścia. Włączanie trybu ścisłego (set -euo pipefail), stosowanie pułapek (trap) do czyszczenia zasobów oraz konsekwentne korzystanie z szablonów kodu to praktyki, które sprawią, że Twoje skrypty będą niezawodne i łatwe w utrzymaniu przez długi czas.