A hurkok és egyéb megoldásokat már megismertük. A mai részben egy érdemesebb hurok megoldást ismerünk meg, ahol már egy lista elemeire alkalmazhatjuk a „csináld végig a munkádat, amíg az elemek el nem fogynak” feladatot. Ez elég hasznos tud lenni, hiszen egy listát megadhatunk akár scriptben, de akár egy külön fájlban is, de a lista lehet egy folyamat eredménye is. Én – na megint kezdi… – szeretek rendet tartani a gépemen, és vannak bizonyos munkák, ahol egy megadott könyvtárszerkezet vált be. Legyen a példa egy Magyar Linux Videó csatornára felkerülő videó. Én szeretem az adott témát tartalmazó mappát standard sablon könyvárakkal ellátni: alap, nyers, kész, pict, audio, script. A nyersbe kerül a vágatlan klipek, a készbe a kész videó, az audioban a hanganyag, a pict-ben a képanyag, a scriptbe az előre megírt témavázlatok, alapban minden videóban szereplő részek, pld. intro. Ma ezt a könyvtár-szerkezetet hozzuk létre script segítségével.
Ehhez a „for loops” szerkezetet használjuk.
A „for loops” szerkezetet alapjai
list=felsorolás for var in list do commands done
Ennek a szerkezetnek az adott lista minden elemére végre kell hajtania az adott parancskészletet. Itt már előkerülhet az első hiba: ha a lista nincs jól elkészítve, akkor gondok lehetnek. Bár a példában szerelő egyszerű felsorolásnál nem lehet túl nagy hiba, de ha már egy fájlból veszi a listát, vagy egy script részlet generálja le, akkor lehtnek problémák.
A for hurok veszi a lista minden elemét (egymás után, sorban), hozzárendeli ezt az elemet a var változó értékéhez, majd végrehajtja a parancsokat a do és a done között. Majd visszatér a tetejére, megkeresi a következő elemet a listában, és megismétli újra a ciklust. Amikor elfogynak az elemek, akkor a done utáni részre lép.
A listát karakterláncok sorozataként definiáljuk, szóközzel elválasztva. Szóközökkel!
#!/bin/bash lista='alap nyers kész pict audio script' for i in $lista do mkdir $i done echo "Kész vagyok!"
Ha mindent jól írtál le, akkor már kész is standard könyvtárszerkezet. Már a feladatot megoldhattuk volna úgy is, hogy egy sima felsorolást teszünk: mkdir alap mkdir nyers stb. de azért ez így elegánsabb. Illetve az ilyen szerkezetek mellett szól, ha többször, többféle műveletet kell elvégezni az adott halmazzal, akkor csak egyszer kell megadni a felsorolást.
Egy hasonló megoldást már használtunk
Bár már hasonlóval foglalkoztunk, más formában, de érdemes megismerni a tartományokat is. Azaz egy adott tartományban lévő összes értéket egyszerűen meg tudunk adni az alábbi módszerrel:
#!/bin/bash for value in {1..5} do echo $value done
A szekvenciákról már volt szó, egyes esetekben ki is kerülhetjük a shell script írását ezekkel, mert az echo {1..5} a parancssorban ugyanezt végzi el.
Fontos tudni, hogy ha az első érték nagyobb, mint a második, akkor visszafele számol. Itt is kiemelem, hogy nagyon kötött a formátuma! A zárójeleknél nem lehet space, illetve pontosan KÉT pont az elválasztó, és itt sem lehet space!
A szekvencia megadásánál van lehetőség lépésközt is megadni, ami azt jelenti, hogy a formátuma {kezdő..végső..lépésköz} esetén már nem egyesével léptet, hanem a megadott lépésenként.
for value in {0..10..2} do echo $value done
Egyértelművé teszi a kérdést 🙂
Bár jellemzően ennél kicsit komolyabb megoldásokat használunk, de érdemes tudni, hogy fájlok feldolgozására is alkalmas ez a szerkezet. Azaz nem csak az általunk megadott felsorolásból tud fogadni bemenő adatot, hanem önállóan is beszerezheti a listát. A példa egyszerű: készítsünk a megadott könyvtárban lévő összes .sh fájlról egy másolatot .old kiterjesztéssel. Jó lesz biztonsági másolatnak!
for i in $1/*.sh do cp $i $1/$( basename -s .sh $i ).old done
Az első sorban látjuk, hogy a scriptnek meg kell adnunk az elérési utat, ami a $1 paraméter lesz. Majd abban könyvtárban megkeresi az összes .sh kiterjesztést. Itt, és természetesen az összes shell scriptnél használhatod a szabályos kifejezéseket, a reg.ex-eket. Ismerd meg őket és jó hasznát veszed majd!
A harmadik sorban a szabályos cp parancsot használjuk átnevezésre, ahol az első paramétert a $i, ami egy változó, hiszen amíg talál a fájlok közt .sh végűt, addig sorban, mindegyikre elvégzi a műveletet. A második paramétere a cp-nek egy sima basename megoldás, aminél levágjuk a régi kiterjesztést, vagyis megkeressük a fájl nevét, és a végére az old-ot tesszük.
Bár ennél komolyabb mentő scripteket is össze lehet dobni, de alapesetben ez nem rossz megoldás.
Break a hurkokban – megszakítjuk a müködést
A hurkok egy automatizált folyamatok, jellemzően le is futnak rendesen. De az automatizálásnak van hátránya: automatikusan működnek, beavatkozás nélkül. De lehetnek olyan helyzetek, amikor a környezet megváltozik, és jó lenne, ha megszakadna, leállna a folyamat. Például fájlokat másolunk/mentünk, de ha a szabad lemezterület egy bizonyos szint alá esik, akkor a másolást le kellene állítanunk.
#!/bin/bash # Mentés, ellenőrzéssel mkdir $1/backup for i in $1/* do used=$( df $1 | tail -1 | awk '{ print $5 }' | sed 's/%//' ) if [ $used -gt 90 ] then echo Kevés a hely 1>&2 break fi cp $i $1/backup/ done
A mkdir sorban a mentési könyvtárat létrehozzuk, majd a for i sorban elkezdjük a munkát. Az used= egy nem túl egyszerű, de jól használható lemezfoglalást mérő megoldás, ahol elkészül a $1 azaz a script paramétereként megadott könyvtár mérete. Az if azaz „ha” sorban megadott feltételt vizsgálja, sima beágyazott if-then-fi megoldással. Itt van ennek a résznek a fő motívuma: break. Ami arra utasítja a scriptet, hogy ha a feltétel él, akkor lépjen ki, nem kell megvárni, hogy a ciklus lefusson teljesen. Ez egy nagyon jó megoldás egy-egy plusz feltétel, hibalehetőség kezelésre. A fi lezárja ezt a részt, majd jön a fő feladat elvégzése és amikor kész a done.
Azaz a break arra való, hogy ha egy feltétel teljesül, akkor a ciklust megszakítva léphess tovább.
Kis kitérő: mkdir? Igen, érdemes végignézni, hogy milyen könyvtárakra van szükségünk, és azokat előre létrehozni. Bár sok program, mentésre, tükrözésre használt automatikusan létre tudná hozni, de jobb, ha mi az elején ezt megtesszük. Így nem lesz gond, nem siránkozik semelyik program sem, hogy nincs ilyen könyvtár.
Continue, azaz folytasd!
Vannak olyan élethelyzetek, amikor egy-egy hibajelenség után nem megszakítani akarom a ciklust, hanem azt kezelni egy másfajta mód, majd folytatni a soron következővel a ciklikus munkát, amíg a feldolgozására váró feladat el nem fogy. Ez egy nagyon jó megoldást kínál olyan hibák esetén, amikor vagy leállna a script futása, vagy kézi beavatkozásra várna, esetleg a hiba miatt teleírná a terminált, és nem lépne tovább.
Ilyen lehet, hogy ha másolásos script munkakönyvtárában van olyan fájl, amire nincs read, azaz olvasási jogunk. Ezt nem akarjuk menteni.
#!/bin/bash for value in $1/* do if [ ! -r $value ] then echo $value Nem olvasható! 1>&2 continue fi cp $value $1/backup/ done
Ilyenkor az if sorban adjuk meg a feltételt – ami most a ! -r „nem olvasható” – esetén legyen szíves kiírni, hogy mi a gondja és lépjen tovább, folytassa munkát. A többi része már érthető.
Ha a continue helyett breaket írnánk, akkor az első általunk nem olvasható fájlnál megszakadna a működése, és a done utáni részre lépne.
Select – választási lista a shell scriptben
Bár ez talán az egyik legegyszerűbb interaktivitást adó megoldás, de időnként hasznos lehet.
A select lehetővé tesz egy egyszerű menürendszer létrehozását. A következő formátumban kell megadni:
select var in lista do parancsok done
Egy igencsak semmire se jó példa, de ehhez hasonlót szoktak betenni, mert jól mutatja a listázást.
#!/bin/bash # Egyszerű választási menü names='Laci Kata Senki' PS3='Ki legyen a fönök: ' select name in $names do if [ $name == 'Senki' ] then break fi echo "Én $name jelöltet választom!" done echo "Nem választottál!"
Azaz itt a names listából nem az összes tagot járja végig, hanem Te választasz, mert kilistázza az összeset, elé írva egy számot. A számot beütve választhatsz.
PS3=’Ki legyen a fönök: ‘ pedig egy egyszerű prompt kiíratás, ami egy belső változója a shellnek.
Ahogy írtam ez a legegyszerűbb választási lista pár olyan tulajdonsággal, ami nem igazán teszi a kedvencemmé:
- Nincs hibakezelés, azaz ha olyan számot írsz be, ami nincs a listában, akkor…, na ezt ellenőrizd, és gondold át miért így van.
- Választás után alapesetben nem lép tovább, hanem visszalép a ciklus elejére, és kéri a következő választást.
- A hurok akkor ér véget, amikor kézzel lezárjuk a ciklust.
- Megváltoztathatja a PS3 rendszerváltozót, így későbbiekben ez gondot okozhat, ha nem állítjuk vissza az alapállapotra. Ami legtöbb esetben nem okoz gondot, mert viszonyalg ritkán íratjuk ki a PS3-as promptot.
Összefoglalva:
while do done – Amig igaz az állítás, fut
until do done – Addig fut, amíg igaz nem lett az állítás.
for do done – Minden elemen futtassa a parancsot.
break – Lépjen ki a futó ciklusból.
continue – Végezze ez a meghatározottat, majd folytassa a ciklust
select do done – Választás, egyszerű, de használgató