Bash skripte 3 — uslovi i petlje
U drugom delu skripta je počela da radi sa različitim ulazima. Sada je učimo da donosi odluke i da ponavlja posao — bez toga skripta ostaje spisak komandi.
Sve počinje od izlaznog koda
Bash nema pravi tip „tačno/netačno". Umesto toga gleda izlazni kod komande: nula znači uspeh, sve ostalo neuspeh.
$ ls /etc > /dev/null $ echo $? 0 $ ls /nepostojeci 2> /dev/null $ echo $? 2
Zato se if u bash-u ne pita „da li je ovo tačno" nego „da li
je ova komanda uspela". Kad to jednom shvatiš, sve ostalo ima smisla.
if
#!/usr/bin/env bash
if [ -f /etc/passwd ]; then
echo "Fajl postoji"
fi
Puni oblik:
if uslov; then
komande
elif drugi_uslov; then
komande
else
komande
fi
Blok se zatvara sa fi — if unazad.
Razmaci unutar uglastih zagrada su obavezni
if [ -f "$fajl" ]; then # radi if [-f "$fajl"]; then # ne radi
Razlog je što je [ zapravo komanda, a ne deo sintakse. Bez
razmaka bash traži komandu koja se zove [-f.
Provera fajlova
Ovo su operatori koje ćeš kao administrator koristiti najčešće:
| Operator | Tačno ako |
|---|---|
-e putanja | postoji (bilo šta) |
-f putanja | postoji i običan je fajl |
-d putanja | postoji i direktorijum je |
-L putanja | simbolički link |
-s putanja | postoji i nije prazan |
-r putanja | imaš pravo čitanja |
-w putanja | imaš pravo pisanja |
-x putanja | izvršno je |
#!/usr/bin/env bash
konfig="/etc/httpd/conf/httpd.conf"
if [ ! -f "$konfig" ]; then
echo "Konfiguracija nije pronađena: $konfig"
exit 1
fi
if [ ! -r "$konfig" ]; then
echo "Nemam pravo čitanja. Pokreni kao root."
exit 1
fi
echo "Konfiguracija je u redu"
Uzvičnik obrće uslov. Ovakav „izađi rano ako nešto ne valja" obrazac je osnova pouzdane skripte.
Razlika između -e i -f ume da zbuni:
-e je tačno i za direktorijum, uređaj ili soket, a
-f samo za običan fajl. Kad proveravaš da li fajl postoji pre
čitanja, hoćeš -f.
Poređenje brojeva
| Operator | Značenje |
|---|---|
-eq | jednako |
-ne | različito |
-lt | manje od |
-le | manje ili jednako |
-gt | veće od |
-ge | veće ili jednako |
#!/usr/bin/env bash
zauzeto=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$zauzeto" -gt 90 ]; then
echo "UPOZORENJE: root particija je $zauzeto% puna"
exit 1
elif [ "$zauzeto" -gt 75 ]; then
echo "Pažnja: $zauzeto% zauzeto"
else
echo "Disk u redu: $zauzeto%"
fi
Ovo je gotova skripta za nadzor — zakači je na cron i dobijaš upozorenje pre nego što ti se disk napuni.
Poređenje teksta
| Operator | Značenje |
|---|---|
= | isti tekst |
!= | različit tekst |
-z | prazno |
-n | nije prazno |
if [ "$1" = "start" ]; then
echo "Pokrećem servis"
fi
if [ -z "$1" ]; then
echo "Upotreba: $0 [start|stop]"
exit 1
fi
Pazi: za brojeve koristi -eq, za tekst
=. Zamena mesta daje tiho pogrešne rezultate — poređenje
"10" < "9" je tačno ako se gleda kao tekst.
Navodnici ovde nisu opcioni
fajl="" if [ $fajl = "test" ]; then # greška u sintaksi if [ "$fajl" = "test" ]; then # radi
Bez navodnika prazna promenljiva nestaje i uslov ostane bez leve strane.
Više uslova
if [ -f "$fajl" ] && [ -r "$fajl" ]; then
echo "Postoji i mogu da ga čitam"
fi
if [ "$odgovor" = "d" ] || [ "$odgovor" = "D" ]; then
echo "Nastavljam"
fi
Dvostruke zagrade
Bash ima i noviji oblik [[ ]], koji je udobniji:
if [[ -f "$fajl" && -r "$fajl" ]]; then
echo "U redu"
fi
if [[ "$ime" == alen* ]]; then
echo "Ime počinje sa 'alen'"
fi
if [[ "$fajl" =~ \.log$ ]]; then
echo "Ovo je log fajl"
fi
Unutar [[ ]] promenljive se ne moraju štititi navodnicima,
radi poređenje šablona sa == i regularni izrazi sa
=~.
Jedina mana: [[ ]] je bash proširenje i ne radi u čistom
sh. Ako je tvoj shebang #!/usr/bin/env bash —
a jeste — slobodno ga koristi.
Uslov nad komandom
Uglaste zagrade nisu obavezne. Bilo koja komanda može biti uslov:
if systemctl is-active --quiet httpd; then
echo "Apache radi"
else
echo "Apache ne radi — pokrećem ga"
systemctl start httpd
fi
if grep -q "error" /var/log/httpd/error_log; then
echo "Ima grešaka u logu"
fi
Opcija -q kod grep-a
ućutkuje ispis — zanima nas samo izlazni kod. Ovo je čest i vrlo čitljiv
obrazac.
&& i || kao prečica
mkdir -p /backup && echo "Direktorijum spreman" systemctl start httpd || echo "Pokretanje nije uspelo"
Druga komanda se izvršava samo ako je prva uspela (&&),
odnosno samo ako nije (||). Zgodno za kratke provere; za
išta duže koristi pun if.
case
Kad ima više mogućnosti, case je čitljiviji od gomile
elif-ova:
#!/usr/bin/env bash
case "$1" in
start)
echo "Pokrećem"
systemctl start httpd
;;
stop)
echo "Zaustavljam"
systemctl stop httpd
;;
restart)
echo "Restartujem"
systemctl restart httpd
;;
status)
systemctl status httpd
;;
*)
echo "Upotreba: $0 {start|stop|restart|status}"
exit 1
;;
esac
Svaka grana se zatvara sa ;;, a *) hvata sve
ostalo. Blok se zatvara sa esac.
Grane prihvataju i šablone:
case "$fajl" in
*.tar.gz|*.tgz) tar -xzf "$fajl" ;;
*.zip) unzip "$fajl" ;;
*.gz) gunzip "$fajl" ;;
*) echo "Nepoznat format" ;;
esac
for petlja
Kroz spisak
for servis in httpd sshd crond; do
if systemctl is-active --quiet "$servis"; then
echo "$servis: radi"
else
echo "$servis: NE RADI"
fi
done
Kroz fajlove
for fajl in /var/log/*.log; do
echo "$fajl — $(wc -l < "$fajl") redova"
done
Bash sam proširuje *.log u spisak fajlova. Ako nijedan ne
odgovara šablonu, petlja se izvrši jednom sa doslovnim
/var/log/*.log — zato unutra proveri
[ -f "$fajl" ] kad to može da se desi.
Kroz argumente skripte
for arg in "$@"; do
echo "Obrađujem: $arg"
done
Kroz opseg brojeva
for i in {1..5}; do
echo "Pokušaj $i"
done
for i in {0..20..5}; do
echo "$i"
done
I klasični oblik, kad granica dolazi iz promenljive:
for (( i=1; i<=5; i++ )); do
echo "Broj $i"
done
Vitičaste zagrade ne rade sa promenljivama — {1..$n} se ne
proširuje. U tom slučaju koristi oblik sa dvostrukim zagradama ili
seq.
Kroz izlaz komande
for korisnik in $(cut -d: -f1 /etc/passwd); do
echo "$korisnik"
done
Oprez: ovo puca ako neka vrednost sadrži razmak. Za obradu redova
iz fajla bolji je while read, odmah ispod.
while petlja
Ponavlja se dok je uslov ispunjen:
brojac=1
while [ "$brojac" -le 5 ]; do
echo "Pokušaj $brojac"
brojac=$((brojac + 1))
done
Čitanje fajla red po red
Ovo je najkorisniji oblik while petlje:
while read -r linija; do
echo "Red: $linija"
done < /etc/hosts
Opcija -r sprečava da bash tumači obrnute kose crte — piši
je uvek.
Sa razdvajanjem polja:
while IFS=: read -r korisnik _ uid _ _ home _; do
if [ "$uid" -ge 1000 ]; then
echo "$korisnik ($uid) — $home"
fi
done < /etc/passwd
Promenljiva IFS određuje razdvajač polja. Donja crta se po
dogovoru koristi za kolone koje te ne zanimaju.
Čekanje na uslov
until systemctl is-active --quiet httpd; do
echo "Čekam da se Apache podigne..."
sleep 2
done
echo "Apache je pokrenut"
until je obrnut while — vrti se dok uslov
nije ispunjen.
break i continue
for fajl in /var/log/*.log; do
if [ ! -r "$fajl" ]; then
continue # preskoči ovaj, idi na sledeći
fi
if grep -q "FATAL" "$fajl"; then
echo "Fatalna greška u $fajl"
break # prekini petlju
fi
done
Sve zajedno: provera stanja servera
#!/usr/bin/env bash
# Provera osnovnog stanja servera
# Upotreba: ./provera.sh [prag_diska]
prag="${1:-85}"
greske=0
echo "=== Provera servisa ==="
for servis in httpd sshd crond; do
if systemctl is-active --quiet "$servis"; then
echo " $servis: radi"
else
echo " $servis: NE RADI"
greske=$((greske + 1))
fi
done
echo
echo "=== Provera diskova ==="
while read -r particija zauzeto; do
zauzeto="${zauzeto%\%}"
if [ "$zauzeto" -ge "$prag" ]; then
echo " $particija: $zauzeto% — PREKO PRAGA"
greske=$((greske + 1))
else
echo " $particija: $zauzeto%"
fi
done < <(df -h --output=target,pcent | tail -n +2)
echo
if [ "$greske" -eq 0 ]; then
echo "Sve u redu."
exit 0
else
echo "Pronađeno problema: $greske"
exit 1
fi
Zapis < <(komanda) prosleđuje izlaz komande u petlju
kao da je fajl. Deluje čudno, ali je bitan: da si napisao
df ... | while read, petlja bi se izvršila u zasebnom procesu
i promenljiva greske bi na kraju bila nula.
Skripta vraća 1 ako ima problema, pa je alat za nadzor ili
cron odmah prepoznaje kao neuspeh.
Vežba
Napiši skriptu koja prima direktorijum kao argument i:
- proverava da direktorijum postoji, inače izlazi sa porukom
- prolazi kroz sve fajlove u njemu
- za svaki ispisuje ime, veličinu i da li je stariji od 30 dana
- na kraju ispisuje ukupan broj fajlova i koliko ih je starih
Trebaće ti find sa opcijom
-mtime ili operator -nt za poređenje datuma
fajlova.
Sledeći deo: funkcije, obrada grešaka sa set -euo i
kompletna backup skripta sa rotacijom starih arhiva.
Povezano: Bash skripte 2 — promenljive i argumenti · grep · systemctl · Programiranje
Comments
Post a Comment