Loop Through the Lines of a File: Bash For Loop Explained
Egy fájl sorain szeretnék végighaladni egy Bash script segítségével, és ennek egyik módja a for loop használata.
Mi az a for ciklus?
A for ciklus az egyik legelterjedtebb programozási konstrukció, és arra szolgál, hogy egy adott kódblokkot futtassunk le egy lista elemeinek egy halmaza alapján. Tegyük fel például, hogy olyan programot szeretnénk írni, amely kiírja a 10 legnagyobb európai városban élők számát. A program egy for ciklus segítségével végigmehet a listában szereplő minden egyes városon, és kiírhatja az adott városhoz tartozó emberek számát.
A végrehajtott logika minden alkalommal ugyanaz, és az egyetlen dolog, ami változik, az a város.
Az alábbiakban láthatjuk a Bash for ciklus általános szintaxisát:
for item in do command1 command2 ... commandNdone
LIST lehet például:
- számtartomány.
- szóközökkel elválasztott karakterláncok sorozata.
- egy Linux-parancs kimenete (pl. az ls parancs).
A do és done közötti N parancsot a lista minden egyes elemére végrehajtjuk.
For ciklus a Bashben
Ezzel a cikkel megtanulod, hogyan használhatod a for hurkot a Bashben, konkrétan egy fájl sorainak végigjárására.
De miért tennéd ezt? Végigmenni egy fájl sorain?
Ezekre például akkor lehet szükséged, ha egy alkalmazásból adatokat exportáltál egy fájlba, és ezeket az adatokat valahogy ki akarod dolgozni.
Ebben a példában egy egyszerű .txt fájlt fogunk használni, amelyben minden sor tartalmazza:
- egy város nevét
- az abban a városban élő emberek számát.
Az alábbiakban láthatjuk a szöveges fájl formátumát, kettőspontot használunk, hogy elválasszuk az egyes városokat az adott városban élő emberek számától:
Istanbul:15,067,724Moscow:12,615,279London:9,126,366...
Hogyan tudunk tehát egy Bash for ciklus segítségével végigmenni ennek a fájlnak a tartalmán?
Először a fájl nevét tároljuk egy változóban
FILENAME="european-cities.txt"
Ezután egy másik változóval és a cat paranccsal megszerezzük a fájl összes sorát:
LINES=$(cat $FILENAME)
Itt parancshelyettesítést használunk, hogy a cat parancs kimenetét a LINES változókhoz rendeljük.
Végül a for ciklus lehetővé teszi, hogy végigmenjünk a fájl minden egyes során:
for LINE in $LINESdo echo "$LINE"done
A for ciklus minden egyes iterációjánál végrehajtandó parancsok definiálására használjuk a do és done kifejezéseket.
Egy 10 soros fájl esetén például a for ciklus 10 iteráción fog végigmenni, és minden egyes iterációnál beolvas egy sort a fájlból.
Az echo parancsot tetszőleges parancssorozattal helyettesíthetjük annak alapján, hogy mit akarunk csinálni a fájl egyes soraival.
Íme a végleges szkript:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINESdo echo "$LINE"done
A szkript kimenete pedig…
./cities.sh Istanbul:15,067,724Moscow:12,615,279London:9,126,366Saint-Petersburg:5,383,890Berlin:3,748,148Kyiv:3,703,100Madrid:3,223,334Rome:2,857,321Paris:2,140,526Bucharest:2,106,144
A listát a cat parancs segítségével adjuk át a for ciklusnak.
Ez azt jelenti, hogy bármilyen parancsot használhatunk a for ciklusnak átadandó LIST létrehozására.
Valami más lehetséges parancsra gondolsz?
Azért a for ciklus nem az egyetlen lehetőség egy ciklus létrehozására egy Bash szkriptben, egy másik lehetőség a while ciklus.
Mi a számláló a Bash for ciklusban?
A for ciklusban definiálhatunk egy számláló nevű változót is. A számlálót a ciklus minden egyes iterációjának nyomon követésére használhatjuk.
A számláló használata nagyon gyakori minden programozási nyelvben. Arra is használható, hogy a cikluson belül hozzáférjünk egy adatstruktúra elemeihez (a mi példánkban nem ez a helyzet).
Módosítsuk az előző programot, és definiáljunk egy számlálót, amelynek értéke minden iterációnál kiírásra kerül:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Mint láthatjuk, a for cikluson kívül definiáltam egy COUNTER nevű változót, amelynek kezdeti értéke 0-ra van állítva.
Aztán minden iterációnál kiírom a számláló értékét a fájlból származó sorral együtt.
Azt követően a Bash aritmetikai operátorral növelem a COUNTER változó értékét 1-gyel.
És itt van a szkript kimenete:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 3: Saint-Petersburg:5,383,890Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Break és Continue egy Bash for ciklusban
A for ciklus normális menetét a Bashben is meg lehet változtatni.
A két utasítás, ami ezt lehetővé teszi, a break és a continue:
- break: megszakítja a for ciklus végrehajtását és a for ciklus utáni első sorra ugrik.
- continue: a for ciklus következő iterációjára ugrik.
A számláló definiálása segít megnézni, hogy mi történik, ha break-et vagy continue-t adunk a meglévő szkriptünkhöz.
Kezdjük a break-el…
A számláló értéke alapján hozzáadok egy if utasítást. Az if-en belüli break utasítás megszakítja a ciklus végrehajtását, ha a számláló értéke 3:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then break fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
A kimenet pedig:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Mint látjuk, a break utasítás megállítja a for ciklus végrehajtását, mielőtt elérnénk az echo parancsot, mert a COUNTER értéke 3.
Ezek után cseréljük a break-et continue-ra, és nézzük meg, mi történik. A kód többi részét változatlanul hagyom.
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
És itt van a szkript kimenete:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Fura… a kimenet ugyanaz. Miért?
Ez azért van, mert amikor a COUNTER értéke 3, a continue utasítás átugrik a ciklus következő iterációjára, de nem növeli a számláló értékét.
A következő iterációnál tehát a COUNTER értéke még mindig 3, és a continue utasítás ismét végrehajtódik, és így tovább az összes többi iterációnál.
Az if utasításon belül növelnünk kell a COUNTER változó értékét:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo if ; then COUNTER=$((COUNTER+1)) continue fi echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Ezúttal a helyes kimenetet látjuk:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366Counter 4: Berlin:3,748,148Counter 5: Kyiv:3,703,100Counter 6: Madrid:3,223,334Counter 7: Rome:2,857,321Counter 8: Paris:2,140,526Counter 9: Bucharest:2,106,144
Mint látható, a terminálban nem jelenik meg a “Counter 3: ….”.
For ciklus írása egy sorban
Mielőtt befejeznénk ezt a bemutatót, nézzük meg, hogyan írhatunk for hurkot egy sorban.
Ez nem javasolt gyakorlat, tekintve, hogy kevésbé olvashatóvá teszi a kódunkat.
De jó tudni, hogyan írjunk egy hurkot egy sorban, mélyebbé teszi a Bash tudásunkat.
A Bash egysoros for ciklus általános szintaxisa a következő:
for i in ; do ; done
Nyomtassuk ki a szövegfájlunk tartalmát egysoros for hurokkal:
#!/bin/bashFILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINES; do echo $LINE; done
Az egyszerűsítés érdekében eltávolítottam a COUNTER és az if utasítást. Ha ezek ott lennének, az egysoros for ciklus sokkal nehezebben olvasható lenne.
Próbáljunk meg távol maradni az egysorosoktól, ha megnehezítik a kódunk olvashatóságát.
Következtetés
Végeredményben ebben a bemutatóban megtanultuk, hogyan:
- Tároljuk egy fájl sorait egy változóban
- Egy for ciklus segítségével menjünk végig minden egyes soron.
- Számlálót használjon egy for ciklusban.
- Változtassa meg a ciklus menetét break és continue segítségével.
- Egy for hurkot írjon egy sorban.
Van értelme?
Hogyan fogod ezt használni?
Mondd el nekem 🙂
Ha többet szeretnél megtudni a ciklusokról a Bash szkriptelésben, nézd meg ezt a bemutatót.
Kapcsolódó INGYENES tanfolyam: Bash szkriptelés megfejtése