Loop Through the Lines of a File: Bash For Loop Explained
Haluan läpikäydä tiedoston rivejä Bash-skriptillä ja yksi tapa tehdä se on käyttää for-silmukkaa.
Mikä on for-silmukka?
For-silmukka on yksi yleisimmistä ohjelmointikonstruktioista, ja sitä käytetään tietyn koodilohkon suorittamiseen listan kohtien joukon perusteella. Oletetaan esimerkiksi, että haluat kirjoittaa ohjelman, joka tulostaa Euroopan 10 suurimmassa kaupungissa asuvien ihmisten määrän. Ohjelma voi käyttää for-silmukkaa käydäkseen läpi jokaisen luettelossa olevan kaupungin ja tulostaakseen kyseisen kaupungin ihmisten määrän.
Logiikka, joka suoritetaan, on joka kerta sama, ja ainoa asia, joka muuttuu, on kaupunki.
Alhaalla näet Bashin for-silmukan yleisen syntaksin:
for item in do command1 command2 ... commandNdone
LIST voi olla esimerkiksi:
- lukujen alue.
- jakso merkkijonoja, jotka on erotettu välilyönneillä.
- Linux-komennon (esim. komennon ls) tuloste.
Listan jokaisen kohteen kohdalla suoritetaan do:n ja done:n välissä olevat N komentoa.
For-silmukka Bashissa
Tässä artikkelissa opit käyttämään for-silmukkaa Bashissa ja erityisesti käymään läpi tiedoston rivejä.
Mutta miksi tekisit niin? Tiedoston rivien läpikäyminen?
Se voi olla tarpeen esimerkiksi silloin, jos olet vienyt tietoja sovelluksesta tiedostoon ja haluat käsitellä näitä tietoja jotenkin.
Tässä esimerkissä käytämme yksinkertaista .txt-tiedostoa, jossa jokainen rivi sisältää:
- kaupungin nimi
- kaupungissa asuvien ihmisten määrä.
Alhaalla näet tekstitiedoston muodon, kaksoispistettä käytetään erottamaan kukin kaupunki kyseisessä kaupungissa asuvien ihmisten lukumäärästä:
Istanbul:15,067,724Moscow:12,615,279London:9,126,366...
Miten voimme siis käyttää Bashin for-silmukkaa tämän tiedoston sisällön läpikäymiseen?
Ensin tallennamme tiedoston nimen muuttujaan
FILENAME="european-cities.txt"
Sen jälkeen käytämme toista muuttujaa ja cat-komentoa saadaksemme kaikki tiedoston rivit:
LINES=$(cat $FILENAME)
Tässä käytämme komennon korvaamista osoittaaksemme cat-komennon tulosteen LINES-muuttujiin.
Loppujen lopuksi for-silmukan avulla voidaan käydä läpi tiedoston jokainen rivi:
for LINE in $LINESdo echo "$LINE"done
Do ja done käytetään määrittelemään komennot, jotka suoritetaan jokaisella for-silmukan iteraatiokerralla.
Jos sinulla on esimerkiksi tiedosto, jossa on 10 riviä, for-silmukka käy läpi 10 iteraatiota ja lukee jokaisella iteraatiokerralla yhden rivin tiedostosta.
Echo-komento voidaan korvata millä tahansa komentosarjalla, joka perustuu siihen, mitä tiedoston jokaisella rivillä halutaan tehdä.
Tässä on lopullinen skripti:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINESdo echo "$LINE"done
Ja skriptin ulostulo on…
./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
Välitämme listan for-silmukkaan cat-komennolla.
Tämä tarkoittaa, että voimme käyttää mitä tahansa haluamiamme komentoja luodaksemme for-silmukkaan välitettävän LISTAN.
Onko mielessäsi muita mahdollisia komentoja?
Ei for-silmukka ole myöskään ainoa vaihtoehto luoda silmukka Bash-skriptissä, toinen vaihtoehto on while-silmukka.
Mikä on laskuri Bashin for-silmukassa?
For-silmukassa voit myös määritellä muuttujan nimeltä counter. Laskurilla voit seurata silmukan jokaista iteraatiota.
Laskurin käyttö on hyvin yleistä kaikissa ohjelmointikielissä. Sitä voidaan käyttää myös tietorakenteen elementtien käyttämiseen silmukan sisällä (näin ei ole esimerkissämme).
Muutetaan edellistä ohjelmaa ja määritellään laskuri, jonka arvo tulostetaan jokaisella iteraatiokerralla:
#!/bin/bash FILENAME="european-cities.txt"LINES=$(cat $FILENAME)COUNTER=0for LINE in $LINESdo echo "Counter $COUNTER: $LINE" COUNTER=$((COUNTER+1))done
Kuten näet, olen määritellyt for-silmukan ulkopuolelle muuttujan nimeltä COUNTER, jonka alkuarvoksi on asetettu 0.
Sitten jokaisella iteraatiokerralla tulostan laskurin arvon yhdessä tiedoston rivin kanssa.
Sen jälkeen käytän Bashin aritmeettista operaattoria kasvattaakseni muuttujan COUNTER arvoa 1:llä.
Ja tässä on skriptin tuloste:
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 ja Continue Bashin for-silmukassa
On olemassa tapoja muuttaa for-silmukan normaalia kulkua Bashissa.
Kaksi lauseketta, joiden avulla tämä on mahdollista, ovat break ja continue:
- break: Keskeyttää for-silmukan suorituksen ja hyppää ensimmäiselle for-silmukan jälkeiselle riville.
- continue: hyppää for-silmukan seuraavaan iteraatioon.
Kun olemme määritelleet laskurin, voimme nähdä, mitä tapahtuu, kun lisäämme break- tai continue-lausekkeen olemassa olevaan skriptiimme.
Aloitetaan break-lausekkeella…
Lisään if-lausekkeen laskurin arvon perusteella. Ifin sisällä oleva break-lause keskeyttää silmukan suorituksen, jos laskuri on 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
Ja tuloste on:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Kuten näet, break-lause pysäyttää for-silmukan suorituksen ennen kuin saavutamme echo-komennon, koska COUNTER on 3.
Korvataan break-lauseke continue-lausekkeella ja katsotaan mitä tapahtuu. Jätän muun koodin ennalleen.
#!/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
Ja tässä on skriptin tuloste:
Counter 0: Istanbul:15,067,724Counter 1: Moscow:12,615,279Counter 2: London:9,126,366
Kumma…tuloste on sama. Miksi?
Se johtuu siitä, että kun COUNTERin arvo on 3, continue-lause hyppää silmukan seuraavaan iteraatioon, mutta se ei kasvata laskurin arvoa.
Siten seuraavassa iteraatiossa COUNTERin arvo on edelleen 3 ja continue-lause suoritetaan uudelleen, ja niin edelleen kaikkien muiden iteraatioiden kohdalla.
Korjataksemme tämän meidän on kasvatettava COUNTER-muuttujan arvoa if-lauseen sisällä:
#!/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
Tällä kertaa näemme oikean tulosteen:
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
Kuten huomaatte ”Counter 3: ….” ei tulostu terminaaliin.
For-silmukan kirjoittaminen yhdellä rivillä
Katsotaan ennen tämän opetusohjelman päättämistä, miten voimme kirjoittaa for-silmukan yhdellä rivillä.
Tämä ei ole suositeltava käytäntö, kun otetaan huomioon, että se tekee koodistasi huonommin luettavaa.
Mutta on hyvä osata kirjoittaa silmukka yhdellä rivillä, se antaa lisää syvyyttä Bash-osaamiseesi.
Yleinen syntaksi Bashin for-silmukalle yhdellä rivillä on seuraava:
for i in ; do ; done
Tulostetaan tekstitiedostomme sisältö yksirivisellä for-silmukalla:
#!/bin/bashFILENAME="european-cities.txt"LINES=$(cat $FILENAME)for LINE in $LINES; do echo $LINE; done
Yksi yksinkertaistaakseni olen poistanut COUNTER- ja if-lauseen. Jos ne olisivat siinä, yksirivinen for-silmukka olisi paljon vaikeampi lukea.
Yritä pysyä erossa yksirivisistä lauseista, jos ne tekevät koodistasi vaikealukuista.
Loppupäätelmä
Loppupäätelmänä tässä opetusohjelmassa olet oppinut:
- Tallentamaan tiedoston rivit muuttujaan
- Käyttämään for-silmukkaa jokaisen rivin läpikäymiseen.
- Käyttää laskuria for-silmukassa.
- Muuttaa silmukan kulkua break- ja continue-toiminnoilla.
- Kirjoittaa for-silmukan yhdelle riville.
Onko järkeä?
Miten aiot käyttää tätä?
Kertokaa minulle 🙂
Jos haluat oppia lisää silmukoista Bash-skriptauksessa, katso tämä opetusohjelma.
Seuraava ILMAINEN kurssi: Decipher Bash Scripting