Shell script – ciklusok folytatás

Linux kezdő

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ó

Related Posts