Automatyzacja Linux/macOS z Bash: Praktyczny Przewodnik

3/10/2025 Linux

Mateusz Kędziora

image

Hej, programiści! Zastanawialiście się kiedyś, jak efektywnie zarządzać swoimi systemami Linux lub macOS? A może chcielibyście zautomatyzować nudne, powtarzalne zadania, żeby mieć więcej czasu na rzeczy, które naprawdę lubicie? Jeśli tak, to trafiliście idealnie. Dziś zajmiemy się bashem - językiem skryptowym dostępnym w systemach Linux oraz MacOS!

Czym właściwie jest Bash?

Bash (Bourne Again Shell) to powłoka poleceń, interpreter, a jednocześnie język programowania. To interfejs między Tobą a jądrem systemu operacyjnego. Umożliwia wydawanie poleceń systemowi, uruchamianie programów, zarządzanie plikami i katalogami. Co najważniejsze, Bash pozwala łączyć te polecenia w skrypty, które automatyzują całe sekwencje zadań.

Dlaczego warto używać Bash’a?

  • Automatyzacja: Największa zaleta. Po co ręcznie wykonywać serię poleceń, skoro możesz napisać skrypt, który zrobi to za Ciebie? Codzienne kopie zapasowe? Zmiana nazw setek plików? Monitorowanie zasobów serwera? Bash ogarnie to wszystko.
  • Uniwersalność: Bash jest domyślną powłoką w większości dystrybucji Linuksa i dostępny jest również dla macOS. Oznacza to, że Twoje skrypty będą działać na wielu systemach bez większych modyfikacji.
  • Integracja: Bash świetnie integruje się z innymi narzędziami i programami w systemie. Możesz wywoływać inne skrypty, programy w C++, Pythonie, a nawet łączyć się z bazami danych.
  • Dostępność: Bash jest darmowy i open source. Nie musisz płacić za licencje, ani martwić się o ograniczenia.
  • Skuteczność: Bash to szybki i skuteczny sposób wykonywania operacji na plikach i systemach.

Podstawowe komendy Bash, które musisz znać:

Zanim zaczniemy pisać skrypty, przyjrzyjmy się kilku podstawowym komendom, które będą nam potrzebne. Traktuj to jako słowniczek początkującego Basha.

  • ls (list): Wyświetla listę plików i katalogów w bieżącym katalogu.

    • ls -l: Wyświetla listę w formacie szczegółowym (uprawnienia, data modyfikacji, rozmiar itp.).
    • ls -a: Wyświetla wszystkie pliki, w tym ukryte (zaczynające się od kropki).
    • ls -t: Sortuje pliki według daty modyfikacji (najnowsze na górze).

    Przykład:

    # Wyświetl zawartość bieżącego katalogu w szczegółowym formacie
    ls -l
  • cd (change directory): Zmienia bieżący katalog.

    • cd ..: Przechodzi do katalogu nadrzędnego.
    • cd ~: Przechodzi do katalogu domowego.
    • cd /: Przechodzi do katalogu głównego.

    Przykład:

    # Przejdź do katalogu o nazwie "dokumenty"
    cd dokumenty
  • mkdir (make directory): Tworzy nowy katalog.

    • mkdir -p: Tworzy całą ścieżkę katalogów, jeśli nie istnieją.

    Przykład:

    # Utwórz katalog o nazwie "nowy_katalog"
    mkdir nowy_katalog
  • rm (remove): Usuwa pliki i katalogi. Używaj tej komendy ostrożnie!

    • rm plik.txt: Usuwa plik o nazwie plik.txt.
    • rm -r katalog: Usuwa katalog i jego zawartość (rekurencyjnie).
    • rm -f plik.txt: Usuwa plik bez pytania o potwierdzenie.
    • rm -rf katalog: Usuwa katalog i jego zawartość bez pytania o potwierdzenie. Bardzo niebezpieczne!

    Przykład:

    # Usuń plik o nazwie "niepotrzebny_plik.txt"
    rm niepotrzebny_plik.txt
    
    # Usuń katalog o nazwie "stary_katalog" wraz z jego zawartością
    rm -r stary_katalog
  • cp (copy): Kopiuje pliki i katalogi.

    • cp plik.txt kopia.txt: Kopiuje plik.txt do kopia.txt.
    • cp -r katalog nowy_katalog: Kopiuje katalog katalog do nowy_katalog (rekurencyjnie).

    Przykład:

    # Skopiuj plik "ważny_plik.txt" do "kopia_wazny_plik.txt"
    cp ważny_plik.txt kopia_wazny_plik.txt
    
    # Skopiuj katalog "dane" do katalogu "backup_dane"
    cp -r dane backup_dane
  • mv (move): Przenosi lub zmienia nazwę plików i katalogów.

    • mv plik.txt nowy_plik.txt: Zmienia nazwę plik.txt na nowy_plik.txt.
    • mv plik.txt katalog: Przenosi plik.txt do katalog.

    Przykład:

    # Zmień nazwę pliku "staranazwa.txt" na "nowanazwa.txt"
    mv staranazwa.txt nowanazwa.txt
    
    # Przenieś plik "plik.txt" do katalogu "dokumenty"
    mv plik.txt dokumenty
  • cat (concatenate): Wyświetla zawartość pliku.

    Przykład:

    # Wyświetl zawartość pliku "raport.txt"
    cat raport.txt
  • echo: Wyświetla tekst.

    Przykład:

    # Wyświetl tekst "Witaj świecie!"
    echo "Witaj świecie!"
  • grep: Wyszukuje wzorce w plikach.

    • grep "wzorzec" plik.txt: Wyszukuje w plik.txt wiersze zawierające “wzorzec”.

    Przykład:

    # Wyszukaj w pliku "log.txt" wiersze zawierające słowo "error"
    grep "error" log.txt
  • chmod (change mode): Zmienia uprawnienia dostępu do plików i katalogów. Używane do definiowania kto może czytać, pisać i wykonywać plik.

    • chmod +x skrypt.sh: Dodaje uprawnienia do wykonywania skryptu.
    • chmod 755 skrypt.sh: Ustawia uprawnienia na rwxr-xr-x (właściciel może czytać, pisać i wykonywać, grupa i inni tylko czytać i wykonywać).

    Przykład:

    # Ustaw uprawnienia do wykonywania skryptu "moj_skrypt.sh"
    chmod +x moj_skrypt.sh
    
    # Ustaw uprawnienia na 755 dla pliku "plik.txt"
    chmod 755 plik.txt
  • chown (change owner): Zmienia właściciela i grupę pliku.

    • chown user:group plik.txt: Zmienia właściciela na user i grupę na group dla plik.txt.

    Przykład:

    # Zmień właściciela pliku "plik.txt" na użytkownika "jan" i grupę "grupa_jan"
    chown jan:grupa_jan plik.txt
  • find: Wyszukuje pliki i katalogi na podstawie różnych kryteriów.

    • find . -name "*.txt": Wyszukuje wszystkie pliki z rozszerzeniem .txt w bieżącym katalogu i jego podkatalogach.
    • find / -size +10M: Wyszukuje wszystkie pliki większe niż 10 MB w całym systemie plików.

    Przykład:

    # Wyszukaj wszystkie pliki z rozszerzeniem ".log" w katalogu "/var/log"
    find /var/log -name "*.log"
    
    # Wyszukaj wszystkie pliki większe niż 100MB w katalogu domowym
    find ~ -size +100M
  • ps (process status): Wyświetla informacje o uruchomionych procesach.

    • ps aux: Wyświetla wszystkie procesy uruchomione przez wszystkich użytkowników.
    • ps -ef: Podobne do ps aux, ale wyświetla więcej informacji.

    Przykład:

    # Wyświetl wszystkie uruchomione procesy
    ps aux
  • top: Wyświetla dynamiczny podgląd procesów, które zużywają najwięcej zasobów systemowych (CPU, pamięć).

    Przykład:

    # Uruchom program "top"
    top
  • kill: Wysyła sygnał do procesu, zazwyczaj sygnał zakończenia (SIGTERM).

    • kill PID: Zabija proces o podanym PID (Process ID).
    • kill -9 PID: Wysyła sygnał SIGKILL (natychmiastowe zabicie procesu). Używaj ostrożnie!

    Przykład:

    # Zabij proces o ID 1234
    kill 1234
    
    # Zabij proces o ID 5678, używając sygnału SIGKILL
    kill -9 5678
  • df (disk free): Wyświetla informacje o zajętości dysków.

    • df -h: Wyświetla informacje w formacie czytelnym dla człowieka (np. GB, MB).

    Przykład:

    # Wyświetl informacje o zajętości dysków w czytelnym formacie
    df -h
  • du (disk usage): Wyświetla informacje o zajętości dysków przez pliki i katalogi.

    • du -sh: Wyświetla sumaryczny rozmiar bieżącego katalogu w czytelnym formacie.
    • du -h --max-depth=1: Wyświetla rozmiary katalogów w bieżącym katalogu (do głębokości 1).

    Przykład:

    # Wyświetl rozmiar bieżącego katalogu w czytelnym formacie
    du -sh
    
    # Wyświetl rozmiary katalogów w bieżącym katalogu
    du -h --max-depth=1

To tylko wierzchołek góry lodowej, ale te komendy są fundamentem, na którym będziemy budować nasze skrypty.

Pierwszy skrypt Bash: Hello World!

Czas na praktykę. Stwórzmy nasz pierwszy skrypt. Otwórz edytor tekstu (np. nano, vim, gedit) i wpisz następujący kod:

#!/bin/bash
# To jest mój pierwszy skrypt Bash
echo "Witaj świecie!"

Zapisz plik jako hello.sh. Teraz musimy nadać mu uprawnienia do wykonywania:

chmod +x hello.sh

A następnie uruchom skrypt:

./hello.sh

Powinieneś zobaczyć w terminalu:

Witaj świecie!

Co tu się stało?

  • #!/bin/bash: To tak zwany “shebang”. Mówi systemowi, który interpreter ma użyć do wykonania skryptu (w tym przypadku Bash). Musi być to pierwsza linia pliku.
  • # To jest mój pierwszy skrypt Bash: Komentarz. Bash ignoruje wszystko po znaku # aż do końca linii. Komentarze są bardzo ważne, żeby Twój kod był czytelny i zrozumiały.
  • echo "Witaj świecie!": Wyświetla tekst “Witaj świecie!” na standardowe wyjście (czyli w terminalu).

Zmienne w Bash’u

Zmienne to podstawowy element każdego języka programowania. W Bash’u używamy ich do przechowywania danych, które możemy później wykorzystać w skrypcie.

#!/bin/bash

# Definiowanie zmiennej
imie="Jan"
nazwisko="Kowalski"

# Używanie zmiennej
echo "Witaj, $imie $nazwisko!"

# Zmienne środowiskowe
echo "Twój katalog domowy: $HOME"
echo "Twój użytkownik: $USER"

Wyjaśnienie:

  • imie="Jan": Przypisanie wartości “Jan” do zmiennej o nazwie imie. Pamiętaj: przed i po znaku = nie może być spacji!
  • echo "Witaj, $imie $nazwisko!": Użycie zmiennych. $ przed nazwą zmiennej powoduje, że Bash podstawi jej wartość. Możemy używać zmiennych wewnątrz ciągów znaków. Jeśli chcesz użyć zmiennej z bardziej złożoną nazwą, np. moja_zmienna, możesz użyć echo "Wartość: ${moja_zmienna}", aby uniknąć niejednoznaczności.
  • $HOME i $USER: To zmienne środowiskowe, które są automatycznie ustawiane przez system. $HOME zawiera ścieżkę do Twojego katalogu domowego, a $USER Twoją nazwę użytkownika.

Instrukcje warunkowe (if, then, else, fi)

Instrukcje warunkowe pozwalają wykonywać różne fragmenty kodu w zależności od tego, czy dany warunek jest spełniony, czy nie.

#!/bin/bash

plik="plik.txt"

# Sprawdź, czy plik istnieje
if [ -f "$plik" ]; then
  echo "Plik $plik istnieje."
else
  echo "Plik $plik nie istnieje."
fi

# Porównywanie liczb
liczba1=10
liczba2=20

if [ "$liczba1" -gt "$liczba2" ]; then
  echo "$liczba1 jest większe od $liczba2."
elif [ "$liczba1" -lt "$liczba2" ]; then
  echo "$liczba1 jest mniejsze od $liczba2."
else
  echo "$liczba1 jest równe $liczba2."
fi

Wyjaśnienie:

  • if [ -f "$plik" ]; then: Sprawdza, czy plik o nazwie przechowywanej w zmiennej plik istnieje. [ ] to polecenie test, a -f to operator, który sprawdza, czy dany plik jest zwykłym plikiem. "$plik" jest otoczone cudzysłowami, żeby uniknąć problemów, jeśli nazwa pliku zawiera spacje. then oznacza, że jeśli warunek jest spełniony, to wykonaj kod po then.
  • else: Jeśli warunek w if nie jest spełniony, wykonaj kod po else.
  • fi: Zamyka instrukcję if.
  • elif: Skrót od “else if”. Pozwala na dodanie kolejnych warunków.
  • -gt, -lt, -eq, -ne, -ge, -le: Operatory porównania liczb. -gt (greater than), -lt (less than), -eq (equal), -ne (not equal), -ge (greater or equal), -le (less or equal).
  • WAŻNE: Wewnątrz [ ] spacje są bardzo ważne. [ "$liczba1" -gt "$liczba2" ] jest poprawne, ale ["$liczba1"-gt"$liczba2"] już nie.

Pętle (for, while, until)

Pętle pozwalają powtarzać fragment kodu określoną liczbę razy lub dopóki dany warunek jest spełniony.

  • Pętla for:

    #!/bin/bash
    
    # Iteracja po liście plików
    for plik in *.txt; do
      echo "Przetwarzam plik: $plik"
    done
    
    # Iteracja po zakresie liczb
    for i in {1..5}; do
      echo "Liczba: $i"
    done
    
    # Iteracja po elementach tablicy
    owoce=("jabłko" "gruszka" "banan")
    for owoc in "${owoce[@]}"; do
      echo "Owoc: $owoc"
    done

    Wyjaśnienie:

    • for plik in *.txt; do: Iteruje po wszystkich plikach z rozszerzeniem .txt w bieżącym katalogu. *.txt to wzorzec globbing, który pasuje do wszystkich plików z tym rozszerzeniem.
    • done: Zamyka pętlę for.
    • for i in {1..5}; do: Iteruje po zakresie liczb od 1 do 5.
    • owoce=("jabłko" "gruszka" "banan"): Definicja tablicy.
    • "${owoce[@]}": Rozwija tablicę owoce do listy elementów. Cudzysłowy są ważne, żeby uniknąć problemów, jeśli elementy tablicy zawierają spacje.
  • Pętla while:

    #!/bin/bash
    
    licznik=1
    
    while [ "$licznik" -le 5 ]; do
      echo "Licznik: $licznik"
      licznik=$((licznik + 1))
    done

    Wyjaśnienie:

    • while [ "$licznik" -le 5 ]; do: Wykonuje kod w pętli dopóki wartość zmiennej licznik jest mniejsza lub równa 5.
    • licznik=$((licznik + 1)): Zwiększa wartość zmiennej licznik o 1. $((...)) to notacja arytmetyczna w Bash’u.
  • Pętla until:

    #!/bin/bash
    
    licznik=1
    
    until [ "$licznik" -gt 5 ]; do
      echo "Licznik: $licznik"
      licznik=$((licznik + 1))
    done

    Wyjaśnienie:

    • until [ "$licznik" -gt 5 ]; do: Wykonuje kod w pętli dopóki wartość zmiennej licznik nie jest większa od 5. Inaczej mówiąc, pętla wykonuje się dopóki warunek jest fałszywy.

Funkcje

Funkcje pozwalają na grupowanie fragmentów kodu, które wykonują konkretne zadanie. Możemy je wywoływać wielokrotnie z różnych miejsc w skrypcie.

#!/bin/bash

# Definicja funkcji
funkcja_przywitanie() {
  echo "Witaj, $1!"
}

# Wywołanie funkcji
funkcja_przywitanie "Jan"
funkcja_przywitanie "Anna"

# Funkcja z wartością zwrotną
funkcja_suma() {
  local suma=$(( $1 + $2 ))
  echo $suma
}

wynik=$(funkcja_suma 10 20)
echo "Suma: $wynik"

Wyjaśnienie:

  • funkcja_przywitanie() { ... }: Definicja funkcji o nazwie funkcja_przywitanie.
  • $1: Pierwszy argument przekazany do funkcji. $2 to drugi argument, $3 to trzeci, i tak dalej.
  • funkcja_przywitanie "Jan": Wywołanie funkcji z argumentem “Jan”.
  • local suma=$(( $1 + $2 )): Deklaracja zmiennej lokalnej suma. local oznacza, że zmienna jest widoczna tylko wewnątrz funkcji.
  • echo $suma: Funkcja zwraca wartość poprzez wypisanie jej na standardowe wyjście.
  • wynik=$(funkcja_suma 10 20): Wywołanie funkcji i przypisanie jej wyniku do zmiennej wynik. $(...) to command substitution, która pozwala na wykonanie polecenia i podstawienie jego wyniku w danym miejscu.

Przykłady skryptów automatyzujących typowe zadania

Teraz pokażę kilka konkretnych przykładów skryptów, które mogą Ci się przydać w codziennej pracy.

Skrypt do tworzenia kopii zapasowych

#!/bin/bash

# Katalog do skopiowania
source_dir="/home/user/dokumenty"
# Katalog docelowy dla kopii zapasowej
backup_dir="/home/user/backup"
# Nazwa pliku kopii zapasowej
backup_file="backup_$(date +%Y-%m-%d_%H-%M-%S).tar.gz"

# Sprawdź, czy katalog docelowy istnieje
if [ ! -d "$backup_dir" ]; then
  echo "Katalog docelowy $backup_dir nie istnieje. Tworzę go..."
  mkdir -p "$backup_dir"
fi

# Utwórz kopię zapasową
tar -czvf "$backup_dir/$backup_file" "$source_dir"

# Sprawdź, czy kopia zapasowa została utworzona poprawnie
if [ -f "$backup_dir/$backup_file" ]; then
  echo "Kopia zapasowa została utworzona pomyślnie: $backup_dir/$backup_file"
else
  echo "Błąd podczas tworzenia kopii zapasowej!"
  exit 1 # Zakończ skrypt z kodem błędu
fi

exit 0 # Zakończ skrypt z kodem sukcesu

Wyjaśnienie:

  • Skrypt tworzy kopię zapasową katalogu dokumenty w katalogu backup.
  • Nazwa pliku kopii zapasowej zawiera datę i godzinę utworzenia.
  • Używamy tar do spakowania i skompresowania katalogu.
  • Skrypt sprawdza, czy katalog docelowy istnieje i tworzy go, jeśli nie istnieje.
  • Skrypt sprawdza, czy kopia zapasowa została utworzona poprawnie.
  • exit 0 oznacza, że skrypt zakończył się pomyślnie. exit 1 oznacza, że wystąpił błąd. Kody wyjścia są używane do sygnalizowania statusu wykonania skryptu innym programom lub skryptom.

Skrypt do wsadowej zmiany nazw plików

#!/bin/bash

# Wzorzec plików do zmiany nazw
wzorzec="plik_*.txt"
# Nowy przedrostek dla nazw plików
nowy_przedrostek="nowy_"

# Iteruj po wszystkich plikach pasujących do wzorca
for plik in $wzorzec; do
  # Pobierz starą nazwę pliku
  stara_nazwa="$plik"
  # Pobierz nową nazwę pliku
  nowa_nazwa="${plik/plik_/$nowy_przedrostek}" # Zamień "plik_" na "nowy_"

  # Zmień nazwę pliku
  mv "$stara_nazwa" "$nowa_nazwa"

  echo "Zmieniono nazwę pliku: $stara_nazwa -> $nowa_nazwa"
done

Wyjaśnienie:

  • Skrypt zmienia nazwy wszystkich plików pasujących do wzorca plik_*.txt w bieżącym katalogu.
  • Zamienia przedrostek plik_ na nowy_.
  • Używamy mv do zmiany nazwy pliku.
  • ${plik/plik_/$nowy_przedrostek} to parametr expansion, który pozwala na manipulowanie wartością zmiennej. W tym przypadku zamieniamy fragment nazwy pliku.

Skrypt do monitorowania stanu systemu

#!/bin/bash

# Próg wykorzystania CPU (w procentach)
cpu_prog=80
# Próg wykorzystania pamięci (w procentach)
pamiec_prog=90

# Pobierz wykorzystanie CPU
cpu_wykorzystanie=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')

# Pobierz wykorzystanie pamięci
pamiec_wykorzystanie=$(free | awk '/Mem:/ {print $3/$2 * 100.0}')

# Sprawdź, czy przekroczono próg wykorzystania CPU
if (( $(echo "$cpu_wykorzystanie > $cpu_prog" | bc -l) )); then
  echo "Ostrzeżenie: Wykorzystanie CPU przekroczyło $cpu_prog%: $cpu_wykorzystanie%"
fi

# Sprawdź, czy przekroczono próg wykorzystania pamięci
if (( $(echo "$pamiec_wykorzystanie > $pamiec_prog" | bc -l) )); then
  echo "Ostrzeżenie: Wykorzystanie pamięci przekroczyło $pamiec_prog%: $pamiec_wykorzystanie%"
fi

Wyjaśnienie:

  • Skrypt monitoruje wykorzystanie CPU i pamięci.
  • Używa top i free do pobrania informacji o stanie systemu.
  • Porównuje wykorzystanie CPU i pamięci z zdefiniowanymi progami.
  • Wyświetla ostrzeżenie, jeśli próg został przekroczony.
  • bc -l jest używane do wykonywania operacji na liczbach zmiennoprzecinkowych. Bash domyślnie operuje tylko na liczbach całkowitych.

Wskazówki dla początkujących

  • Czytaj dokumentację: man bash to Twój najlepszy przyjaciel. Znajdziesz tam szczegółowe informacje o wszystkich komendach i funkcjach Bash’a.
  • Eksperymentuj: Nie bój się próbować różnych rzeczy. Najlepszym sposobem na naukę jest praktyka.
  • Używaj komentarzy: Komentuj swój kod, żeby był czytelny i zrozumiały. Nawet dla Ciebie w przyszłości!
  • Szukaj w internecie: Jeśli masz problem, prawdopodobnie ktoś inny już go rozwiązał. Google i Stack Overflow to Twoi sprzymierzeńcy.
  • Debuguj krok po kroku: Używaj opcji -x do debugowania skryptu. bash -x skrypt.sh wyświetli każdy wiersz skryptu przed jego wykonaniem, co pomoże Ci zrozumieć, co się dzieje.
  • Wykorzystaj narzędzia: Używaj shellcheck skrypt.sh do analizy statycznej skryptu i wychwycenia potencjalnych błędów.

Dodatkowe materiały do nauki

Podsumowanie

Bash scripting to niezwykle przydatne narzędzie dla każdego, kto pracuje z systemami Linux i macOS. Pozwala na automatyzację zadań, oszczędność czasu i zwiększenie efektywności. Ten artykuł to dopiero początek Twojej przygody z Bash’em. Zachęcam do eksperymentowania, czytania dokumentacji i pogłębiania wiedzy. W kolejnych postach omówimy bardziej zaawansowane techniki, takie jak wyrażenia regularne, praca z plikami konfiguracyjnymi i tworzenie interaktywnych skryptów.

Do zobaczenia wkrótce! I pamiętaj: najlepszy sposób na naukę to praktyka!

Polecane artykuły