GNU/Linux - 4. deo – Shell skripte i automatizacija procesa
Uvod
Posle upoznavanja sa osnovnim i napredni(ji)m konzolnim komandama, na red dolazi upoznavanje sa različitim metodama za automatizaciju procesa.
Glavna tema članka biće kreiranje i pokretanje shell skripti u okruženju Bash, ali, pre toga ćemo se upoznati sa privilegijama za pristup datotekama i direktorijumima (jer, bez dodele odgovarajućih privilegija, doslovno nije moguće pokretati skripte).
Nakon detaljnog upoznavanja sa prethodno navedenim temama, upoznaćemo se ukratko i sa programima watch
i cron
koji takođe predstavljaju mehanizme za automatizaciju procesa u UNIX-olikim operativnim sistemima (i mogu 'sarađivati' sa shell skriptama).
Kratak osvrt na kompatibilnost skripti
Striktno govoreći, shell skripte u UNIX-olikim operativnim sistemima nisu univerzalne i svako shell okruženje u kome se skripte izvršavaju (Bash, Zsh, Fish, Dash i sl) - donosi sa sobom pojedinosti o kojima se mora voditi računa (na primer, nije zagarantovano da će skripta napisana za Bash shell, moći da se izvršava u drugim okruženjima i sl).
Međutim, u praksi - pogotovo na početku - gotovo je sigurno da ćete kao shell program koristiti upravo Bash, * a ako ne koristite Bash, gotovo je sigurno da koristite Zsh ili Fish.
Za Fish smo još u uvodnom članku naveli da koristi sopstvenu shell sintaksu koja nije kompatibilna sa sintaksom okruženja Bash, * a što se tiče Zsh-a, skripte pisane za Bash su kompatibilne sa okruženjem Zsh (gotovo uvek), pri čemu najčešće važi i obrnuto (autor ovog teksta koristi upravo Zsh kao (glavni) shell program, ali, sve skripte koje ćete videti u članku, uspešno su isprobane u oba okruženja).
Najjednostavnije: može se reći da sintaksa okruženja Bash predstavlja de facto standard za pisanje shell skripti pod Linux-om (duži niz godina unazad), i zato će i primeri koje ćemo prikazivati biti usklađeni sa navedenim okolnostima.
Privilegije korisnika za pristup datotekama i direktorijumima
UNIX-olike operativne sisteme odlikuje precizan i nedvosmislen sistem privilegija za pristup datotekama i direktorijumima.
Za svaku datoteku ili direktorijum, jasno su definisane privilegije:
- vlasnika datoteke (vlasnik = korisnik koji je kreirao datoteku)
- grupe korisnika kojoj vlasnik pripada
- (svih) ostalih korisnika (koji ne pripadaju jednoj od prethodne dve kategorije)
Da bismo se upoznali sa sistemom privilegija u praksi, dovoljno je pokrenuti komandu ls -l
(pri čemu ćemo dobiti, kao što znamo od ranije, detaljan tabelarni listing datoteka i direktorijuma).
Može se primetiti da je vlasnik direktorijuma data
i datoteke .config
, korisnik korisnik_1
(koji pripada grupi admin
), a mogu se primetiti i podaci koji se tiču privilegija vlasnika i ostalih kategorija korisnika (podaci se nalaze sa leve strane, u prvoj koloni koja sadrži 10 znakova).
Prvi znak prikazuje da li je u pitanju direktorijum (d
), ili je u pitanju obična datoteka (-
), a sledeće tri troslovne niske definišu (kao što smo prethodno nagovestili): privilegije vlasnika datoteke, privilegije grupe kojoj vlasnik pripada i (na kraju), privilegije svih ostalih korisnika.
U konkretnom primeru koji smo prikazali, ako se usmerimo na direktorijum data
, mogu se zapaziti sledeće informacije:
d
- u pitanju je direktorijum (uz obične datoteke, na prvom mestu stoji odrednica-
)rwx
- vlasnik datoteke ima sve privilegije za pristup direktorijumu *r-x
- korisnici iz grupe kojoj vlasnik pripada, imaju privilegije pristupa (r
) i izvršavanja (x
)r-x
- svi ostali korisnici takođe imaju privilegije pristupa i izvršavanja
U zavisnosti od toga da li je u pitanju datoteka ili direktorijum, svaka od tri troslovne niske prikazuje (redom) sledeće privilegije:
r
- korisnik ili grupa korisnika ima privilegiju čitanja (datoteka se može čitati (tj. otvarati), odnosno, može se pristupiti direktorijumu)w
- korisnik ili grupa korisnika ima pravo upisa (u datoteku ili direktorijum)x
- korisnik ili grupa korisnika ima pravo da pokrene izvršnu datoteku-
- pojava znaka-
na bilo kom od tri mesta, označava da je privilegija uskraćena
Pravi smisao privilegija za pristup, najlakše je razumeti preko dodatnih primera.
Najočigledniji primer je sledeći: "nekako deluje", da - bez obzira na to "šta piše u tim 'čudnim' rwx niskama" - određeni korisnik "ipak" može pristupiti direktorijumu čiji je 'vlasnik', međutim - privilegije su vrlo nedvosmislene.
Zamislićemo da su u početnoj situaciji definisane sledeće privilegije za direktorijum /home/korisnik_1
....
.... nakon čega nastaje (nova) situacija, u kojoj su privilegije promenjene (primetite da drugi znak, odnosno, prvi znak u prvoj troslovnoj niski, nije više 'r'):
U novonastaloj situaciji, korisnik korisnik_1
doslovno neće više biti u stanju da pristupa sopstvenom home direktorijumu!
U sledećem primeru, korisnik korisnik_2
je vlasnik datoteke skripta_1.sh
i definisao je privilegije na sledeći način:
U navedenoj situaciji, različiti korisnici imaju sledeće privilegije:
- korisnik
korisnik_2
ima sve privilegije - korisnici iz grupe
admin
(kojoj pripada korisnikkorisnik_2
), takođe imaju sve privilegije - ostali korisnici, imaju privilegiju čitanja (mogu otvarati skriptu u editorima i sl), imaju privilegiju pokretanja skripte, ali, nemaju privilegije za modifikaciju sadržaja skripte
U prethodno navedenim okolnostima, svi korisnici (tj. administratori) iz grupe "admin", mogu menjati sadržaj skripte skripta_1.sh
, čiji je vlasnik korisnik korisnik_2
(pri čemu ostali korisnici mogu otvarati skriptu), međutim, u drugim okolnostima, mogu se podesiti drugačije privilegije:
U novim okolnostima:
- korisnici iz grupe "admin" mogu samo čitati i pokretati skriptu (na primer, 'pao je dogovor' da svaki administrator ubuduće uređuje samo svoje skripte)
- ostali korisnici (poslednja troslovna niska), mogu samo pokretati skriptu
Predstavljanje privilegija preko brojčanih kodova
U opštem smislu, "rwx niske" su (naravno/očigledno) - niske, ali, takve niske se lako mogu zamisliti i kao trocifreni binarni brojevi (sa uključenim ili isključenim bitovima na određenim pozicijama), to jest, praktičnije - "rwx niske" se mogu zamisliti kao odgovarajuće oktalne cifre (koje odgovaraju kombinacijama uključenih "bitova").
Pogledajmo nekoliko primera:
rwx
=> 111(2) => 7(8)rw-
=> 110(2) => 6(8)r-x
=> 101(2) => 5(8)r--
=> 100(2) => 4(8)
Poznavanje 'oktalnih kodova' za "rwx" statuse, biće od pomoći pri korišćenju komande chmod
.
chmod - promena privilegija datoteke/direktorijuma
Preko komande chmod
("Change Mode"), moguće je definisati (nove) privilegije za pristup određenoj datoteci, ili određenom direktorijumu.
Komanda chmod
može se pozivati na razne načine, a za početak ćemo razmotriti najjednostavniji primer:
.... pri čemu argument u+x
znači da vlasnik dobija privilegiju izvršavanja datoteke.
Ako je potrebno (praktično) poništiti dodelu privilegija iz prethodnog koraka, možemo pozvati sledeću komandu:
U drugom slučaju, u pitaju je oduzimanje (tj. uskraćivanje/ukidanje) privilegija, preko argumenta -
.
Ako je potrebno dopuniti privilegije, tako da korisnik (tj. vlasnik) dobije privilegije izvršavanja, a grupa kojoj korisnik pripada dobije privilegije upisa, komanda se može izvršiti na sledeći način:
Vlasnik (u
), dobija privilegije izvršavanja (+x
), a grupa (g
), kojoj korisnik pripada, dobija privilegije upisa (+w
).
Ako je potrebno izvršiti (umesto prethodne komande), komandu preko koje ....
- vlasnik dobija privilegiju izvršavanja
- grupa kojoj korisnik pripada dobija privilegiju upisa
- svi ostali korisnici gube privilegiju čitanja
.... komanda se može dopuniti na sledeći način:
Komande koje smo koristili u primerima, sasvim su adekvatne (same po sebi), ali, poslednjih nekoliko komandi deluje previše "opširno", i stoga - za primere sa više izmena privilegija - u praksi se gotovo uvek koriste oktalni kodovi (sa kojima smo se prethodno upoznali).
Korišćenje oktalnih kodova podrazumeva malo drugačiji način razmišljanja: nećemo (u praktičnom smislu), više razmišljati o tome "kom korisniku ili grupi korisnika treba dodeliti ili ukinuti koju privilegiju", već, samo o tome kako na kraju treba da budu definisani (novi) nivoi privilegija za sve tri grupe korisnika.
Za primer ćemo uzeti krajnje tipičnu situaciju (koja je, ujedno, usko povezana sa tematikom članka).
Prvi korak u kreiranju nove skripte podrazumeva (skoro uvek) - kreiranje nove prazne datoteke preko komande touch:
Datoteka koja je kreirana na prikazani način ima sledeće privilegije:
.... i može se primetiti da privilegija izvršavanja (x
), nije navedena ni na jednom mestu.
Postojeće (uključene) privilegije, mogu se svesti na oktalni kod 644
, po sledećem postupku:
rw- r-- r--
se može shvatiti kao110 100 100
110 100 100
- posle prevođenja u oktalni oblik - postaje644
.
Pošto je potrebno svim korisnicima dodeliti privilegiju izvršavanja - preko oktalnog koda (to jest, da budemo precizni, potrebno je da se u sve tri grupe aktivira atribut x
) - može se razmišljati na sledeći način:
rw- r-- r--
je početna niska, koja, posle uključivanja privilegija upisa (na sva tri mesta), postajerwx r-x r-x
rwx r-x r-x
se može shvatiti kao111 101 101
111 101 101
- posle prevođenja u oktalni oblik - postaje755
Pošto smo sagledali "idejno poreklo" argumenta 755
, privilegije za pokretanje skripte je najlakše dodeliti (odnosno - izmeniti), preko sledeće naredbe:
chown - promena vlasnika datoteke/direktorijuma
Promena vlasnika datoteke ili direktorijuma, obavlja se preko komande chown
("Change Owner").
Na primer, u sledećoj situaciji:
.... posle izvršavanja komande chown
:
.... datoteka (čiji je dotadašnji vlasnik bio korisnik korisnik_2
), dobija novog vlasnika (novi vlasnik je korisnik korisnik_1
):
Ukoliko je prethodni vlasnik datoteke root korisnik:
.... potrebno je komandu chown
pozvati preko komande sudo
:
.... posle čega (slično prethodnom primeru), nastaje sledeći rezultat:
Sada je vreme da se usmerimo na kreiranje skripti.
Prikazaćemo prvo tipičan postupak kreiranja i pokretanja skripte, a u nastavku ćemo se baviti tipovima podataka u shell skriptama i kontrolnim strukturama (naravno, uz odgovarajuće propratne primere).
Kreiranje i pokretanje skripti
Za početak, potrebno je pripremiti zaseban direktorijum sa prigodnim nazivom skripte
(ubuduće ćemo u navedeni direktorijum smeštati sve skripte):
Na UNIX-olikim sistemima, tipična procedura za kreiranje i pokretanje (nove) skripte, podrazumeva:
- kreiranje prazne datoteke
- dodeljivanje privilegija za pokretanje novoj (praznoj) datoteci
- unos sadržaja (tj. komandi), u datoteku
- čuvanje datoteke i pokretanje skripte
Prazna datoteka može se kreirati preko komande touch
(koja je poznata od ranije):
Privilegije za pokretanje datoteke, podesićemo preko komande chmod
:
.... ili, jednostavnije:
U skriptu ćemo uneti sledeći sadržaj:
.... i (na kraju), skripta se može pokrenuti na sledeći način:
U praktičnom smislu, primećujemo da skripta smešta (tabelarni) listing direktorijuma /home/korisnik_1
- u datoteku /home/korisnik_1/.skripte/listing_home
.
Što se tiče 'tehnikalija', osvrnimo se na sledećih nekoliko detalja:
- komentari u shell skriptama počinju znakom
#
(što znamo od ranije), ali .... - direktiva
#!/bin/bash
nije komentar, već, nalog operativnom sistemu da skriptu izvrši preko konkretnog shell programa (u primeru koji smo prikazali, u pitanju je Bash)
Primećujemo i to da smo u skripti koristili promenljive, što možemo smatrati dobrim uvodom u tematiku upotrebe promenljivih u shell skriptama (a upoznaćemo se uskoro i sa kontrolnim strukturama) ....
Promenljive u shell skriptama
Promenljive u shell skriptama (kao što smo već videli), definišu se na vrlo neposredan način (slično kao u Python-u) ....
.... i pozivaju se uz prefiks $
(slično kao u PHP-u) ....
Deklaracija i inicijalizacija promenljivih u shell skriptama, jeste veoma nalik deklaraciji i inicijalizaciji promenljivih u 'uobičajenijim' programskim jezicima (najveća je sličnost sa Python-om), ali, aritmetičke i logičke operacije se obavljaju na nešto drugačiji način.
Međutim, pretpostavićemo na ovom mestu da čitaoci (koji su zainteresovani za upoznavanje sa shell skriptama), imaju prethodnog iskustva u programiranju i solidno poznaju bar jedan programski jezik, * i stoga nećemo "taksativno nabrajati" operacije, već ćemo izneti zapažanja koja se tiču razlika u odnosu na pristup koji je poznat od ranije.
Aritmetičke operacije sa promenljivama
Kada je u pitanju obavljanje aritmetičkih i logičkih operacija, potrebno je prvo razumeti da se sintaksa shell skripti idejno razlikuje od sintakse (velike većine) programskih jezika.
Skripte se (doslovno) pokreću u okruženju koje je namenjeno interpretiranju konzolnih naredbi - pre nego interpretiranju naredbi kao što su c = a + b;
ili if (a > 5) printf("Pozdrav!");
- i stoga ne čudi što u svemu važe "druga pravila".
Pojava ("jednostrukih") zagrada praktično pokreće novo shell okruženje, u kome se mogu izvršavati programi, a rezultat izvršavanja se (nadalje) može dodeliti promenljivoj ....
.... međutim, u ovom trenutku, od većeg značaja je mogućnost uvođenja shell interpretatora u režim tumačenja algebarskih izraza - navođenjem "dupliranih" zagrada na početku i na kraju izraza:
Kada shell interpretator uđe u kontekst tumačenja algebarskih izraza (tzv. "aritmetički kontekst"), nije više potrebno navoditi prefiks $
pri obraćanju promenljivama, a dozvoljeno je koristiti i razmake u izrazu (zarad preglednosti).
Shodno navedenom, prethodni izraz se može zapisati i na drugi način:
.... ali, rekli bismo da je prvi način koji je prikazan, znatno intuitivniji.
Pored prikazanog pristupa, vrednost izraza može se računati i preko komande expr
: *
.... što znači (u širem kontekstu), da sličan 'obrazac razmišljanja' možete koristiti ukoliko je potrebno da u shell skriptama obrađujete podatke preko programa koje ste sami pisali.
Operatori poređenja i logičke operacije
Za početak, pogledajmo standardno if
grananje u C-u ....
.... koje ćemo uporediti sa sintaksom za proveru uslova, kakva se koristi u shell skriptama (i praktično obavlja isti zadatak):
Prva 'pomisao' je (najverovatnije): grananja u shell programima se zapisuju slično kao u C-u, "i samo se koriste drugačije zagrade".
Međutim (iako grananja u shell skriptama jesu 'vizuelno slična' grananjima u C-u), iza svega stoji ponešto drugačiji pristup.
Za proveru uslova u shell skriptama, zapravo se koristi ugrađena komanda test
.... *
.... koja se izvršava po sledećem principu: ako je uslov (koji je kao argument predat komandi test
) - tačan, pokreće se "komanda" (koja je takođe predata kao argument).
Negde u toku razvoja programa Bash, usvojena je (to jest, implementirana) - uprošćena sintaksa za komandu test
....
.... koja je (uz malo dorade), postala i deo sintakse za if
grananja (kao što smo već videli).
Unutar komande test
u shell skriptama (ili, praktičnije - unutar uslova u if
grananjima), koriste se i posebni operatori poređenja (u jednom od prethodnih primera, koristili smo operator -gt
, sa značenjem "veće od", a ostale operatore prikazujemo na slici ispod):
Sa navedenim operatorima dodatno ćemo se upoznati u nešto kasnijim poglavljima, preko primera (videćemo i kako se definišu složeni uslovi), ali, prvo ćemo se ukratko osvrnuti na različite načine za dodelu vrednosti promenljivama.
Sa jedne strane, eksplicitno zadavanje vrednosti, svakako je zanimljivo i korisno samo po sebi (i, pre svega - neophodno, u brojnim situacijama), ali, postoji i mogućnost da se promenljivoj kao vrednost dodeli rezultat izvršavanja određenog programa, a takođe postoji mogućnost učitavanja (sadržaja) datoteka u promenljive ....
Učitavanje datoteka i prosleđivanje rezultata izvršavanja komandi u promenljive
Za početak, razmotrićemo jednostavan primer.
Komanda cat
- za koju smo više puta pomenuli da se uglavnom koristi za ispis sadržaja tekstualnih datoteka u konzoli - ovoga puta biće upotrebljena shodno osnovnoj nameni (spajanje sadržaja datoteka), ali - izlaz komande biće učitan u promenljivu:
Kao malo komplikovaniji primer, prikazaćemo pajpovanje komande date
u komandu awk
, zarad izdvajanja trenutnog vremena (pri čemu se rezultat takođe čuva preko promenljive):
Osvrnimo se i na nekoliko praktičnih primera (koje smo ranije najavili), koji dodatno prikazuju korišćenje UNIX komandi u shell skriptama:
Upis u datoteke i čitanje iz datoteka
Po potrebi, sadržaj određene promenljive se lako može proslediti u datoteku, na način koji je čitaocima (verujemo), dobro poznat od ranije.
U pitanju je 'redirekcija' (tehnika sa kojom smo se susreli još u uvodnom članku).
Učitavanje datoteka se takođe može obavti na idejno sličan način, ali, uz korišćenje nešto drugačije sintakse (koja je, srećom, vrlo jednostavna za razumevanje):
Gornji primer (budući da smo ionako u odeljku koji se tiče promenljivih u shell skriptama) - može se uopštiti:
Nizovi (liste)
Nizovi u shell skriptama najčešće se definišu metodom implicitne deklaracije (pogotovo u situacijama kada korisnici sami kreiraju nizove) ....
.... ali, naravno, postoje i drugi načini, kao što je eksplicitna deklaracija ....
.... ili, metoda složene dodele, koja podrazumeva istovremenu deklaraciju i inicijalizaciju celog niza:
Pristup pojedinačnom elementu obavlja se na prepoznatljiv način (ali, uz korišćenje 'vitičastih' zagrada u određenim okolnostima):
Očitavanje dužine niza obavlja se na sledeći način:
Uklanjanje pojedinačnog elementa, ili celog niza, obavlja se preko komande unset
:
Obilazak strukture niza tipično se obavlja preko for
petlje (što ćemo sagledati na primerima u nastavku, u odeljku koji je posvećen for
petljama).
Shell promenljive (environment variables)
Pored promenljivih koje korisnici mogu sami definisati u okviru skripte, postoje i promenljive koje se ('globalno') prepoznaju u okviru određenog shell okruženja, a mogu se koristiti i u korisničkim skriptama (koje se pokreću u datom shell okruženju).
Identifikatori shell promenljivih - budući da su shell promenljive idejno slične globalnim promenljivama u C-u (i drugim sličnim jezicima) - tipično se pišu uz upotrebu tzv. "allcaps" notacije (svako slovo u identifikatoru je veliko slovo):
Gornja skripta je "samoobjašnjujuća" (uz svaku 'globalnu' promenljivu iz shell okruženja stoji odgovarajući opis), a što se tiče konkretnih vrednosti koje se odnose na vašu instalaciju Linux-a (ili nekog drugog "nix" sistema) - napišite skriptu i pokrenite je. :)
Kontrola toka
U shell skriptama (baš kao i u C-olikim i drugim programskim jezicima), pojavljuju se sledeći tokovi izvršavanja instrukcija:
- linearno (tj. uzastopno) navođenje naredbi
- grananja
- petlje
Prvo ćemo se upoznati sa linearnim izvršavanjem naredbi.
Linearno izvršavanje naredbi
Uzmimo za primer da određeni program (koji koristimo), ostavlja konfiguracione datoteke u home direktorijumu * - zbog čega postoji potreba za povremenim uklanjanjem datoteka ("koje nismo tražili"), pri čemu, naravno, želimo da uklanjanje datoteka izvedemo na što jednostavniji način.
U situaciji koju smo opisali, shell skripte tipično predstavljaju pravo rešenje. **
Ukoliko su preduzete sve mere predostrožnosti, možemo napisati sledeću jednostavnu skriptu:
Kao što vidimo:
- u skriptama se mogu pokretati komande (u gornjem primeru, komanda je
rm
) - preko komande
echo
, obavlja se ispis u konzoli
(Nastavljamo dalje ....)
Grananja (if, case)
Pošto smo se ranije upoznali sa time "odakle 'ćoškaste zagrade' u if-ovima u shell okruženju", upoznaćemo se detaljnije sa sintaksom za zapisivanje grananja u shell skriptama (u sledećem odeljku upoznaćemo se i sa petljama), uz "povlačenje paralela" u odnosu na C i Python.
if-else-elif-fi grananje
Za početak, pogledajmo primer 'običnog' if grananja (ispituje se da li je vrednost a
veća od 10):
Ukoliko else
blok postoji, 'true grana' počinje rezervisanom reči then
i završava se pojavom rezervisane reči else
, dok se u suprotnom 'true grana' završava rezervisanom reči fi
(koja u svakom slučaju stoji na kraju if
bloka).
Višestruka grananja (slično kao u Python-u), tipično se izvode preko rezervisane reči elif
(ponovo ćemo se poslužiti jednostavnim primerom koji predstavlja 'ispis dana u nedelji, shodno rednom broju'):
U pitanju je kod koji je sam po sebi krajnje adekvatan, međutim, budući da u shell skriptama (za razliku od Python-a), postoji i konstrukcija nalik na switch
grananja u C-olikim jezicima (doduše, pod drugačijim - ali dobro poznatim nazivom), grananja se često mogu zapisati na elegantniji način (što ćemo razmotriti na primeru istog algoritma) ....
case-esac grananje
Slično kao i switch
u C-olikim jezicima, case
grananje u shell skriptama omogućava pregledniju organizaciju višestrukih uslova - onda kada je(ste) moguće navesti višestruke uslove preko konstrukcije case
.
Uzmimo (ponovo) primer sa ispisom dana u nedelji (shodno unetom rednom broju):
Na prethodnoj slici smo prikazali kod koji se može realizovati preko case
grananja, ali (kao što smo nagovestili), postoje i situacije u kojima se case
grananje ne može izvesti.
Nije moguće (recimo), koristiti višestruke uslove ....
.... i, u situacijama kakve prikazuje primer sa gornje slike, potrebno je koristiti if-else-elif-fi
konstrukcije.
Praktičan primer skripte koja koristi grananje
Sa grananjem u shell skriptama još bolje ćemo se upoznati kroz konkretan primer (iz svakodnevne prakse), koji može dobro poslužiti korisnicima laptop računara (a verujemo da tematika može biti zanimljiva i ostalim čitaocima).
Linux čuva podatke o statusu i kapacitetu baterije, * preko sledeće dve datoteke:
/sys/class/power_supply/BAT0/status
- status baterije (navešćemo u nastavku šta predstavljaju različiti statusi)/sys/class/power_supply/BAT0/capacity
- kapacitet baterije u procentima (s tim da znak%
nije zapisan u datoteci)
Podaci jesu dostupni, ali - ako je potrebno da sve bude formatirano i prikazano na pregledan i dopadljiv način - moramo se dodatno potruditi.
Da objasnimo prvo šta označavaju različiti statusi baterije (koji se mogu pročitati iz datoteke status
):
"Not charging"
- ispravljač nije priključen i baterija se prazni"Charging"
- ispravljač je priključen i baterija se puni"Full"
- ispravljač je priključen i baterija je u potpunosti napunjena"Not Charging"
- ispravljač je priključen, ali, baterija se ne puni (česta situacija do koje dolazi kada se ispravljač priključi onda kada je baterija, koja se do tada praznila, napunjena na preko 90% kapaciteta)
Pojam kapaciteta (u trenutnom kontekstu), lakši je za razumevanje: u datoteci capacity
, uvek je zapisana određena vrednost, bez obzira na to da li je ispravljač priključen ili nije (tj. bez obzira na to da li se baterija prazni ili puni i sl).
Pošto smo upoznati sa tehnikalijama, možemo napisati skriptu u celosti:
Primer koji smo prikazali je dovoljno zanimljiv sam po sebi (verujemo da je tako), međutim, svakako se može staviti u kontekst u kome postaje - još zanimljiviji.
U uvodnom članku o GNU/Linux distribucijama pomenuli smo da različita desktop okruženja i (pogotovo) menadžeri prozora, mogu, zarad prikaza sistemskih parametara, koristiti skripte i programe koje korisnici kreiraju sami.
Osnovna ideja je da se parametri očitavaju periodično (recimo, na svakih 1s) i, u takvim okolnostima, jedan od parametara za prikaz može biti - formatirani ispis u vezi sa stanjem baterije.
Međutim, s obzirom na to da je ispis koji smo spremili u prethodnom primeru prilično opsežan (prosto rečeno - mnogo teksta za statusnu liniju :)), ispis se može stilizovati upotrebom specijalnih znakova koji - umesto tekstualne poruke - prikazuju simbol za bateriju (kada je ispravljač isključen), ili simbol za utikač (kada je ispravljač uključen), a mogu se koristiti i različite boje za označavanje elemenata:
Ali - "to nije sve" - može se osmisliti i mehanizam za čuvanje prethodnog stanja, što omogućava da se pri svakom očitavanju, trenutno stanje poredi sa prethodnim, a poređenje trenutnog i prethodnog stanja omogućava da se registruju trenuci kada korisnik uključi ili isključi ispravljač (na šta se dalje može odreagovati ispisivanjem odgovarajuće poruke preko posebnih programa za ispis tekstualnih poruka u prozorima). *
Petlje (while, for)
Petlje u shell skriptama su idejno slične petljama u 'uobičajenijim' programskim jezicima, sintaksa podseća na if
grananja u shell skriptama, a 'dodatak' je specifična konstrukcija do-done
, koja predstavlja graničnik tela petlje.
while petlja
Osnovni princip funkcionisanja while
petlji u shell skriptama, možemo sagledati preko sledećeg primera (ispis brojeva od 1 do 10):
While petlje takođe omogućavaju i ugnežđavanje (konkretan primer sa donje slike, predstavlja petlju koja prolazi kroz vrednosti od 1 do 10 i, za svaku vrednost, ispituje (i ispisuje) da li je broj paran ili neparan):
Osvrnućemo se takođe na to da se u while
petljama (koje se koriste u shell skriptama), prilično često javlja "motiv" beskonačnih petlji.
U pitanju je praktičan (iako ne baš previše elegantan), način da se realizuje "program koji se ne isključuje", tipično uz upotrebu sledeća dva mehanizma:
- funkcija
sleep
zaustavlja izvršavanja skripte na određeno vreme, u svakom koraku petlje (čime se praktično definiše interval u kome se petlja ponavlja) if
grananje, uz funkcijubreak
, zaustavlja petlju (to jest, celu skriptu), ukoliko se pojave okolnosti u kojima treba zaustaviti izvršavanje skripte
U novom primeru, prepoznaje se trenutak kada kapacitet baterije (laptop računara), 'padne' ispod 25%:
Skripta se pokreće na svakih 1s, i zaustavlja se ukoliko promenljiva uslov
dobije vrednost true
(ako se ustanovi da ispravljač nije priključen - pri čemu je kapacitet baterije manji od 25%).
U ovom trenutku, verovatno se pitate da li će se kod izvršavati pravilno?!
Umesto "zapitanosti" - probajte sami: napravite datoteku kapacitet
(u istom direktorijumu u kom je skripta), u prvu liniju unesite brojčanu vrednost, prepravite (privremeno) glavnu skriptu na sledeći način ....
.... pokrenite skriptu - i potom menjajte vrednosti u datoteci kapacitet
(u nekom trenutku, unesite vrednost <= 25).
Ako skriptu pokrećete preko menadžera prozora ....
.... moći ćete da sagledate ceo proces na jednoj radnoj površini. *
Opsežniji i zanimljiviji primer while
petlje koja se "vrti ukrug" (beskonačna petlja), videli smo još u uvodnom članku (link vodi direktno do primera while
petlje za ispis sistemskih parametara na naslovnoj traci menadžera prozora). **
for petlja
For petlje u shell skriptama koriste princip "iteracije kroz listu" (slično kao for
petlje u Python-u):
.... ali, vidimo da je potrebno biti vrlo precizan sa sintaksom (za razliku od JS-a i Python-a, koji omogućavaju vrlo jednostavan način obraćanja nizovima).
Za 'najuobičajenije' for
petlje, koje se izvršavaju u rasponu "od-do", postoji i skraćeni način definisanja 'liste koju for
petlja treba da obiđe':
Dodatni primeri shell skripti (iz svakodnevne prakse)
Uvodno izlaganje o shell skriptama, završićemo * uz osvrt na još nekoliko zanimljivih tehnikalija ("koje dobro dođu").
Obrada korisničkog unosa
Ukoliko je potrebno da korisnik ("ručno") unese tekst u skriptu, tekst se može uneti preko terminala (pogledajmo primer):
Kroz primer, upoznali smo se sa komandom read
, koja omogućava unos podataka u okviru shell okruženja.
Koristili smo argument -p
(prompt), a inače se po potrebi može navesti i argument -s
("silent"), * što je korisno pri učitavanju lozinki.
U prvom primeru pojavljuje se i komanda za očitavanje trenutnog direktorijuma (pwd
), koju smo iskoristili za formiranje apsolutne putanje novog direktorijuma (putanja se predaje kao argument komande mkdir
).
Formatiranje izlaza preko komande printf
Ukoliko je potrebno formatirati tekst na precizan način, moguće je (u shell okruženjima Bash i Zsh), koristiti dobro poznatu komandu printf
(doduše, uz ponešto izmenjenu sintaksu):
Automatsko pokretanje procesa preko komande watch
Komanda watch
je jednostavniji od dva mehanizma za automatsko pokretanje procesa u UNIX-olikim okruženjima, a opšti obrazac za pozivanje komande je sledeći:
interval
- interval za pokretanje ("unutrašnje") komandekomanda
- komanda koja se pokreće periodičnoargumenti
- argumenti koji se mogu predavati unutrašnjoj komandi (nije obavezno predavati argumente i nije obavezno da određena interna komanda prepoznaje dodatne argumente)
Razmotrićemo (krajnje tipičan) primer upotrebe komande watch
:
Komanda free
* pokreće se u intervalu od jedne sekunde (-n 1
) ** i svaki put kada se komanda pokrene - automatski se osvežava prikaz.
Ukoliko se komanda watch
navede uz predavanje argumenta -d
("differences"):
.... u prikazu će biti označene razlike (tj. promene), između dva 'očitavanja':
Periodično/redovno pokretanje procesa (cron, crontab)
Pored komande watch
, na UNIX-olikim operativnim sistemima postoji i pozadinski proces cron
, čija je namena - pokretanje procesa (tj. programa, skripti i drugih pozadinskih procesa), u pravilnim intervalima koje korisnici mogu sami definisati.
Cron tabele se najčešće koriste za ozbiljnije zadatke kao što je redovno pokretanje skripti za administraciju sistema (ažuriranje, bekap i sl), pri čemu se pristup tabelama obavlja preko sledeće komande: *
Svaki red u cron tabeli - koji predstavlja periodično pokretanje pojedinačnog procesa (eng. "cron job") - definisan je po sledećem obrascu:
Osvrnimo se na konkretan primer:
Prema navedenim podešavanjima, operativni sistem (preko korisničkog naloga korisnik_1
), na svaki pun sat pokreće skriptu glavni_bekap.sh
(koja se nalazi na navedenoj putanji).
Pretpostavićemo da (možda) "ne deluje da tako piše u gornjoj tabeli", i stoga ćemo pojasniti (na jednostavan način, preko dodatnih primera), kako se zadaju vremenski intervali za pokretanje cron zadataka.
Sledeći kod (gotovo istovetan) ....
.... definiše da se skripta pokreće na svaki pun minut (programe ili skripte moguće je pokretati samo kada je broj sekundi na sistemskom časovniku "00"), ili, preciznije - skripta se pokreće:
- svakog meseca
- svakog dana u mesecu
- svakog dana u nedelji
- svakog sata
- na svaki pun minut
Sa druge strane ....
Bilo koja vrednost koja nije *
, predstavlja (praktično) faktor ograničenja, i stoga (na primer), sledeći "cron job": 0 * * * * korisnik_1 /home/korisnik_1/.skripte/glavni_bekap.sh
- definiše da se navedena skripta pokreće samo kada minut (na sistemskom časovniku), ima vrednost 0, što praktično znači - navedena skripta se pokreće na svaki pun sat (upravo onako kako smo videli u prvom primeru).
U nastavku, upoznaćemo se sa time kako se može udesiti da se određena skripta pokreće u određeno vreme, svakog dana, ili, samo određenim danima.
Ako je potrebno da se određena skripta pokrene svakog dana u određeno vreme, dovoljno je navesti (samo) vreme izvršavanja (za primer ćemo uzeti skriptu koja odjavljuje prijavljene korisnike, nakon isteka radnog vremena).
Drugi primer biće cron job koji pokreće bekap podataka na nedeljnom nivou (jednom nedeljno, na primer, petkom):
Međutim, ako je potrebno da se bekap obavlja svakog radnog dana, cron job koji je podešen po uzoru na cron job sa slike #73, neće biti od pomoći (budući da je u pitanju pokretanje procesa svakog dana - ne samo radnim danima), a u drugim situacijama može postojati i potreba za preciznijim definisanjem intervala (recimo, nije potrebno da se određena skripta izvršava svakog minuta, ali, potrebno je da se izvršava "češće od svakog sata" i sl), i stoga je uputno da se upoznamo sa sintaksom koja omogućava (upravo) - dodatno preciziranje.
Definisanje raspona
Ukoliko se umesto jedne vrednosti navede raspon, u obliku početak-kraj
(sa crticom između), cron job će se pokretati za svaku celobrojnu vrednost iz raspona.
Na primer, sledeći cron job ....
.... izvršava se u 16:31, svakog dana koji odgovara rasponu od 1 (ponedeljak), do 5 (petak).
Nabrajanje
Ukoliko se umesto jedne vrednosti, nabroje dve ili više vrednosti, u obliku v1, v2, v3 .... vn
(broj vrednosti praktično je proizvoljan), cron job će biti pokrenut za svaku od nabrojanih vrednosti (umesto samo jedanput).
U gornjem primeru, skripta se pokreće tačno u podne, prvog dana u mesecu, 12. dana u mesecu, kao i 21. i 27. dana u mesecu (to jest - u opštijem smislu - skripta se pokreće samo navedenim danima u mesecu).
Podela intervala na korake
Bilo koji od pet pojedinačnih argumenata (koji definišu intervale), može se podeliti na 'korake', preko sintakse: */korak
.
Da pojasnimo preko primera: sledeći cron job .....
.... pokreće se svakog dana (sa početkom u 00:00
) - svakoga sata - na svakih 10 min (tj. pokreće se u sledećim trenucima: 00:00:00
, 00:10:00
, 00:20:00
.... 23:59:40
, 23:59:50
).
Kombinacije
Parametri koji se koriste za preciziranje intervala - mogu se takođe i kombinovati.
U prvom primeru ....
- skripta se izvršava (samo) radnim danima
- prvo izvršavanje je u 08:00, a poslednje u 15:50
- u periodu od 08:00 do 15:50 (uključujući i 15:50), skripta se izvršava na svakih 10 minuta
Sledeći primer (poslednji u članku), nešto je složeniji:
Skripta koja je navedena kao izvršna komanda, izvršava se "na svakih sedam sati, počevši od 08:00
, zaključno sa 15:45
", međutim, šta navedene vrednosti zapravo znače?
Mogući "započeti" sati, određeni su preko II kolone: 8-15/7
.
Za svaki započeti sat (pri čemu su jedine moguće vrednosti 8
i 15
), skripta se može izvršavati na svakih 1 min
- ukoliko nije drugačije definisano.
Međutim, budući da je u prvoj koloni navedeno ograničenje 0/15
, skripta se neće izvršavati na svaki minut između 08:00 i 08:59
i između 15:00 i 15:59
, već - na svakih 15 minuta.
U praktičnom smislu, navedena skripta se izvršava: u prvih i poslednjih 1h radnog vremena - na svakih 15 min.
Ograničenja (parametri koji se ne mogu zadati direktno)
Opcije koje smo do sada prikazali, deluju (zaista) sveobuhvatno, ali, postoje i intervali (u toku dana), koji se ne mogu obuhvatiti preko prethodno opisane sintakse.
Recimo, moguće je definisati cron job koji se pokreće na svakih 15 min, od 8h do 16h (s tim da se za poslednji sat piše 15) ....
.... ali, nije moguće (npr. korišćenjem decimalnih vrednosti, ili na drugi način), definisati raspon koji ne počinje i ne završava se 'na pun sat' (recimo, raspon od 07:30 do 17:30):
Ukoliko je potrebno da izvršavanje započne (i/ili da se završi), između dva puna sata, na scenu stupa 'snalaženje' (što najčešće podrazumeva navođenje jednog ili dva 'pomoćna cron job-a' (uz osnovni)):
U gornjem primeru, "srednji" cron job je "glavni", dok su prvi cron job (koji važi samo od 07:30 do 08:00), i treći (koji važi samo od 17:00 do 17:30) - "pomoćni".
Za kraj ....
Na ovom mestu, završavamo uvodni serijal posvećen GNU/Linux distribucijama i slobodnom softveru, u kome smo 'pretresli' osnovne postavke i osnovne 'tehnikalije'.
U člancima koje pripremamo, bavićemo se temama koje smo na početku najavili (instalacija i podešavanje 'minimalističke' GNU/Linux distribucije, naprednije opcije shell skripti i sl), a bavićemo se i ostalim temama koje smo 'načinjali' usput, kao i mnogim drugim temama i sadržajima koji su vezani za više nego zanimljivu tematiku FOSS softvera ....