-
Linuxin bash-skriptit
Linuxin bash-skriptit
Yksi tapa hyödyntää Linuxin joustavuutta on käyttää komentokomentosarjoja. Komentosarja on yksinkertaisesti tiedosto, joka sisältää joukon tavallisia Linux-komentoja, jotka komentotulkki suorittaa automaattisesti annetussa järjestyksessä. Verrattuna varsinaisiin ohjelmointikieliin, kuten Pythoniin, Perliin tai C:hen, ohjelmointi Linuxin komentotulkeilla (bash, tcsh, csh tai sh) on laskennallisesti melko tehotonta. Käytännöllisiä Linux-skriptejä voidaan kuitenkin usein rakentaa muutamassa minuutissa. Komentosarjoista ei tarvitse tietää kovin paljon, jotta voi kirjoittaa yksinkertaisia ohjelmia, jotka säästävät paljon aikaa.
Skriptitiedoston laatiminen
Skriptitiedosto on tavallinen tekstitiedosto, joka voidaan tehdä tavallisilla tekstieditoreilla, kuten nano, Emacs tai vi. Luo uusi skriptitiedosto esimerkiksi komennolla:
Skriptitiedosto alkaa yleensä rivillä, joka määrittää käytettävän komentotulkin. Tässä oppaassa käytämme bash-tulkkia, joka on CSC:n oletuskomentotulkki. Bashin määrittävä rivi on:
Sen jälkeen lisäät Linux-komennot, jotka haluat suorittaa. Käytännössä kirjoitat tiedostoon ne komennot, joita normaalisti käyttäisit tehtävän suorittamiseen interaktiivisessa komentotulkissa. Esimerkiksi seuraavaa skriptiä voidaan käyttää alihakemiston mapfiles luomiseen ja kaikkien .map-tiedostojen kopioimiseen sinne:
Jos skriptin rivi alkaa merkillä #, se ohitetaan, ja loppuriviä pidetään kommenttina (paitsi ensimmäistä riviä, joka alkaa merkeillä #!).
Kun olet tallentanut skriptitiedoston ja sulkenut editorin, voit suorittaa skriptitiedoston komennot antamalla komennon:
Vaihtoehtoisesti voit antaa skriptitiedostolle suoritusoikeudet komennolla
ja suorittaa skriptin sitten komennolla:
Muuttujat ja taulukot
Skripteissä voi käyttää muuttujia, silmukoita ja ehtolauseita. Muuttujia voidaan asettaa syntaksilla:
Huomaa, että yhtäsuuruusmerkin ympärillä ei ole välilyöntejä. Muuttujan arvo haetaan merkillä $,
tai
Esimerkiksi komento
kirjoittaa muuttujan arvon tulosteeseen. Huomaa, että bash-skripteissä muuttujia käsitellään joko merkkijonoina (eli tekstinä) tai kokonaislukuina. Tämä tarkoittaa, että desimaalilukuja ei voi käyttää bash-skripteissä matemaattisiin operaatioihin.
Esimerkki merkkijonomuuttujien käytöstä:
$ name=Veikko
$ familyname=Salo
$ address="CSC Espoo"
$ echo "Person: ${name} ${familyname} works at ${address}."
Person: Veikko Salo works at CSC Espoo.
Kokonaislukumuuttujille voi tehdä yksinkertaista aritmetiikkaa syntaksilla
((expression)). Yleisesti käytetyt aritmeettiset operaatiot on lueteltu alla olevassa taulukossa:
| Operator | Function |
|---|---|
+ |
addition |
- |
subtraction |
* |
multiplication |
/ |
division |
% |
division remainder |
** |
exponentiation |
Yksinkertaisia kokonaislukuaritmetiikan esimerkkejä:
$ a=5
$ b=3
$ ((c = a + b))
$ echo $a plus $b is equal to $c
5 plus 3 is equal to 8
$ ((d = a / b))
$ ((e = a % b))
$ echo "$a divided by $b results $d and remainder $e"
5 divided by 3 results 1 and remainder 2
Bashissa voi käyttää myös yksiulotteisia taulukkomuuttujia eli muuttujia, jotka sisältävät luettelon alkioita. Määritetty taulukon alkio voidaan hakea käyttämällä indeksinumeroa sulkeissa taulukkomuuttujan nimen kanssa (${variable[index]}). Esimerkiksi voimme määrittää yksinkertaisen kolmen alkion taulukon komennolla:
Voimme nyt hakea joko koko taulukon tai vain yhden alkion siitä komennolla
Tämä tulostaa
kun taas komento
tulostaa
Huomaa, että taulukossa indeksointi alkaa nollasta, joten yllä oleva esimerkkikomento tulostaa taulukon kolmannen alkion. Voit tarkistaa taulukon alkioiden määrän lisäämällä merkin # muuttujan nimen alkuun. Tässä tapauksessa esimerkiksi komento
tulostaa arvon
Skriptin komentoriviargumentteihin viitataan sijaintiparametrien kautta.
Parametri $0 viittaa skriptin nimeen, $1 ensimmäiseen argumenttiin, $2 toiseen argumenttiin ja niin edelleen. $# viittaa argumenttien määrään ja $@ koko argumenttiluetteloon.
Alla on esimerkkiskripti, joka havainnollistaa $-taulukkomuuttujan käyttöä:
Jos suoritamme nyt tämän skriptin, jonka nimi on esimerkiksi my_script2.sh, komennolle on annettava kaksi argumenttia. Ensimmäistä argumenttia käytetään tässä tapauksessa määrittämään kopiointikomennon lähdehakemisto, kun taas toinen argumentti on kohdehakemisto. Esimerkiksi komento
kopioisi kaikki tiedostot, joiden pääte on .map, hakemistosta nimeltä source_data uuteen hakemistoon nimeltä map_files.
Lainausmerkit
Bashissa käytetään kolmea erilaista lainausmerkkiä. Lainausmerkkejä tarvitaan usein muuttujien ja suoritettavien komentojen määrittämiseen. Seuraavia lainausmerkkejä voidaan käyttää:
""Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti muuttujien korvaamisen jälkeen''Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti``Käsittele lainausmerkkien sisällä oleva teksti komentona, suorita komento ja korvaa se sitten komennon tulosteella lainausmerkkien kohdalla
Alla on joitakin esimerkkejä havainnollistamaan eri lainausmerkkien toiminnallisia eroja. Lainausmerkkejä voidaan käyttää muuttujien ja argumenttien käsittelyyn. Kun käytetään kaksinkertaisia tai yksinkertaisia lainausmerkkejä, kaikki lainausmerkkien sisällä oleva teksti käsitellään yhtenä argumenttina. Näiden kahden lainausmerkkityypin ero on se, että kaksinkertaisia lainausmerkkejä käytettäessä muuttujat korvataan arvoillaan, kun taas yksinkertaisilla lainausmerkeillä kaikki teksti käytetään sellaisenaan. Jos suoritat komennot
tulos on
Mutta jos käytät sen sijaan yksinkertaisia lainausmerkkejä
saat tulosteeksi
Linux-komennoissa ja skripteissä lainausmerkkejä käytetään tyypillisesti määrittämään argumentteja, jotka sisältävät välilyöntejä tai muita erikoismerkkejä. Oletetaan, että haluaisimme käyttää komentoa grep poimimaan kaikki rivit, jotka sisältävät merkkijonon file size, tiedostosta nimeltä files.txt. Seuraava komento ei toimisi:
Jos suoritat yllä olevan komennon, saat virheilmoituksen, koska sana size tulkitaan nyt toiseksi argumentiksi, joka määrittää syötetiedoston. Voimme korjata tilanteen käyttämällä lainausmerkkejä:
Nyt ensimmäinen argumentti, joka määrittää haettavan merkkijonon, on file
size (mukaan lukien sanojen välinen välilyönti), ja toinen argumentti,
joka määrittää syötetiedoston, on nyt files.txt kuten alun perin oli tarkoitus.
Kolmannella lainausmerkkityypillä `` on erityinen merkitys. Näillä lainausmerkeillä voit saada yhden Linux-komennon tuottamaan argumentin toiselle Linux-komennolle. Perussyntaksi `` on:
missä command1 käyttää komennon command2 tuotosta argumenttina. Bash-skriptissä sama toiminnallisuus voidaan tehdä myös syntaksilla
Silmukat ja ehtolauseet
Silmukoita ja ehtolauseita käytetään harvoin interaktiivisessa komentorivikäytössä. Skripteissä niitä kuitenkin käytetään usein samankaltaisten komentojen suorittamiseen useita kertoja ja suoritettavien komentojen ohjaamiseen. Bash tarjoaa laajan valikoiman silmukoita, ehtolauseita ja muita ohjausrakenteita. Tässä osiossa näytämme esimerkkejä joistakin yleisimmin käytetyistä ohjausrakenteista.
for-silmukka suorittaa määritetyt komennot iteratiivisesti siten, että jokaisella kierroksella silmukkamuuttuja asetetaan yhtä suureksi kuin jokin annetun alkioluettelon alkioista. Bashissa for-silmukka tehdään komentorakenteella:
Esimerkiksi silmukka
tulostaisi
Tyypillisesti argumenttiluettelo sisältää käsiteltäviä tiedostonimiä, mutta se voi sisältää myös mitä tahansa muita parametreja. Oletetaan esimerkiksi, että meillä on hakemisto nimeltä project_3, joka sisältää yhdeksän tiedostoa nimeltä sample1.txt, sample2.txt, ..., sample9.txt. Hakemiston sisällön näkemiseen voimme käyttää komentoa ls:
$ ls project_3/
sample1.txt sample3.txt sample5.txt sample7.txt sample9.txt
sample2.txt sample4.txt sample6.txt sample8.txt
Jos haluaisimme nimetä jokaisen näistä tiedostoista uudelleen niin, että niiden päätteeksi tulee .old, voisimme suorittaa komennon mv yhdeksän kertaa tai käyttää for-silmukkaa:
for filename in sample1.txt sample2.txt sample3.txt sample4.txt \
sample5.txt sample6.txt sample7.txt sample8.txt sample9.txt
do
echo "Renaming file: ${filename}"
mv project_3/${filename} project_3/${filename}.old
done
Yllä oleva for-silmukka on silti melko kömpelö, koska meidän täytyy kirjoittaa kaikki tiedostonimet alkioluetteloon. Voimme välttää tämän käyttämällä jokerimerkkikuviota::
for filename in project_3/*.txt
do
echo "Moving file: $filename"
mv "$filename" "$filename.old"
done
Bashissa voit myös luoda for-silmukan, jossa numeerista indeksimuuttujaa kasvatetaan automaattisesti tietyllä askelkoolla jokaisella kierroksella. Tässä tapauksessa syntaksi on:
Alla on for-silmukka, joka suorittaa saman uudelleennimeämistoiminnon kuin yllä, mutta käyttää alkioina vain numeroita.
for ((number=1; number<=9; number++))
do
echo "Moving file: sample${number}.txt"
mv project_3/sample${number}.txt project_3/sample${number}.txt.old
done
while-silmukassa silmukka jatkaa suorittamista niin kauan kuin määritetty ehtolause on tosi. Bashissa while-silmukka voidaan tehdä syntaksilla:
Yllä for-silmukalla tehty uudelleennimeämistoiminto voitaisiin tehdä myös while-silmukalla:
number=1
while [[ $number -le 9 ]]
do
echo "Moving file: sample${number}.txt"
mv project_3/sample${number}.txt project_3/sample${number}.txt.old
((number = number + 1))
done
Yllä olevassa esimerkissä muuttuja nimeltä number asetetaan ensin arvoon 1. Tämän muuttujan arvoa kasvatetaan sitten yhdellä jokaisen iteraatiokierroksen lopussa. Iteraatioita jatketaan, kunnes muuttuja saavuttaa arvon 10.
Ehtolauseet (if) voidaan tehdä seuraavasti:
Voit käyttää alla olevassa taulukossa lueteltuja operandeja komentojen if ja while ehtolauseissa. Huomaa, että bash käyttää eri ehtolauseita merkkijonoille ja kokonaisluvuille. Esimerkiksi merkkijonojen yhtäsuuruus testataan operaattorilla ==, kun taas kokonaislukujen yhtäsuuruus testataan operaattorilla -eq.
Syntaksi on myös tarkka sulkeiden välisten välilyöntien suhteen, eikä ehtolauseen määritelmä [[a == b]] toimi, vaan se pitää korjata muotoon [[ a == b ]].
Yleisesti käytetyt merkkijono-, kokonaisluku- ja tiedosto-operaatiot if- ja while-lauseissa on lueteltu alla.
| Statement | Operation |
|---|---|
[[ a == b ]] |
True if strings a and b are equal |
[[ a != b ]] |
True if strings a and b are not equal |
[[ a =~ b ]] |
True if strings a and b are similar (allows wildcards) |
[[ a < b ]] |
True if string a is alphabetically before string b |
[[ a > b ]] |
True if string a is alphabetically after string b |
[[ a -eq b ]] |
True if integers a and b are equal |
[[ a -ne b ]] |
True if integers a and b are not equal |
[[ a -lt b ]] |
True if integer a is less than b |
[[ a -gt b ]] |
True if integer a is greater than b |
[[ a -le b ]] |
True if integer a is less or equal to b |
[[ a -ge b ]] |
True if integer a is greater or equal to b |
[[ -e name ]] |
True if file exists |
[[ -n a ]] |
True if string a has non-zero length |
[[ A || B ]] |
True if condition A or condition B is true (logical OR) |
[[ A && B ]] |
True if condition A and condition B is true (logical AND) |
[[ ! A ]] |
True if condition A is not true |
Alla on joitakin esimerkkejä if-komentorakenteista.
Tarkista, onko kokonaislukumuuttuja x suurempi kuin 10:
Tarkista, onko muuttuja x suurempi kuin 10 mutta pienempi kuin 20:
if [[ $x -gt 10 && $x -lt 20 ]]
then
echo "The value of variable x is more than 10 but less than 20"
else
echo "The value of x is out of range"
fi
Voit vertailla myös tekstiä sisältäviä muuttujia (merkkijonoja):
if [[ $answer == "yes" ]]
then
echo "Your answer was: yes"
elif [[ $answer == "no" ]]
then
echo "Your answer was no"
else
echo "You didn't answer yes or no"
fi
Kun käytät pienempi kuin ja suurempi kuin -vertailuja, sinun tulee olla varovainen, ettet sekoita merkkijono- ja kokonaislukuvertailuja. Esimerkiksi seuraava ehto
on FALSE, koska merkkijono 123 on aakkosjärjestyksessä ennen merkkijonoa 3. Numeerinen vertailu
on kuitenkin TRUE.
On olemassa useita operaattoreita, joilla voidaan testata tiedoston eri ominaisuuksia. Yleisimmin käytetty operaattori on -e, joka tarkistaa, onko tiedosto olemassa. Oletetaan esimerkkinä, että meillä on yksinkertainen tiedostonimiluettelo nimeltä checklist.txt. Haluaisimme tarkistaa, mitkä näistä tiedostoista löytyvät nykyisestä hakemistosta. Voimme käyttää for-silmukkaa kaikkien tiedostonimien tutkimiseen ja if-komentoa yhdessä -e-ehdon kanssa tiedoston olemassaolon testaamiseen:
for file_name in $(cat checklist.txt)
do
if [[ -e $file_name ]]
then
echo "File $file_name was found"
else
echo "File $file_name was not found"
fi
done
Tulosteen kirjoittaminen
Edellisissä esimerkeissä olemme jo käyttäneet komentoa echo tekstin ja muuttujien kirjoittamiseen vakiotulosteeseen (eli näytölle tai tiedostoon vakiotulosteen uudelleenohjauksella). Esimerkiksi komento
tulostaa
echo-komentoa voidaan käyttää tulostamiseen monissa tapauksissa, mutta se ei tarjoa hyviä työkaluja hyvin muotoillun tulosteen tekemiseen määritellyillä sarakkeilla. Tilanteissa, joissa tarvitaan hyvin jäsenneltyä tekstimuotoista tulostetta, kannattaa käyttää komentoa printf komennon echo sijaan. Komennon printf syntaksi on:
format definition määrittää, millaista tulostetta tulostetaan.
Yleisiä tyyppejä ovat teksti (%s), kokonaisluvut (%i) ja liukuluvut (%f). Muotomäärittelyt voivat myös määrittää, kuinka paljon tilaa kullekin argumentille varataan ja miten se sijoitetaan sarakkeeseen. Alla on joitakin yksinkertaisia esimerkkejä komennon printf käytön havainnollistamiseksi:
tulostaa
Tässä muotomäärittely määrittää, että ensimmäinen argumentti käsitellään kokonaislukuna, toinen ja kolmas merkkijonoina ja neljäs argumentti liukulukuna. Huomaa, että oletusarvoisesti printf ei lisää rivinvaihtomerkkiä tulosteen loppuun. Tätä varten muotomäärittelyn loppuun tulee lisätä määrittely \n.
Seuraavassa esimerkissä määritämme, kuinka monta merkkiä kullekin argumentille varataan. Komento
tulostaa
Tässä varaamme ensimmäiselle kokonaisluvulle neljä merkkiä ja sen jälkeen kummallekin merkkijonolle kymmenen merkkiä. Liukuluku esitetään kuudella merkillä, joista kaksi on desimaalipisteen jälkeen.
Voit myös lisätä muotomäärittelyyn tekstiä ja ohjausmerkkejä, kuten sarkaimen (\t). Komento
tulostaa
Linux-skripteissä printf-komentoa käytetään tyypillisesti muuttujissa tallennettujen arvojen tulostamiseen. Esimerkiksi komennot
tulostavat