-
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 pystyy kirjoittamaan yksinkertaisia ohjelmia, jotka säästävät paljon aikaa.
Skriptitiedoston laatiminen
Skriptitiedosto on yksinkertainen tekstitiedosto, joka voidaan tehdä tavallisilla tekstieditoreilla, kuten nano, Emacs tai vi. Luo uusi skriptitiedosto kirjoittamalla esimerkiksi:
Skriptitiedosto alkaa yleensä rivillä, joka määrittää käytettävän komentotulkin. Tässä ohjeessa käytämme bash-komentotulkkia, 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 skriptitiedostollesi suoritusoikeudet komennolla
ja suorittaa skriptin sitten komennolla:
Muuttujat ja taulukot
Skripteissä voi käyttää muuttujia, silmukoita ja ehtolauseita. Muuttujat voidaan asettaa syntaksilla:
Huomaa, että yhtäsuuruusmerkin ympärillä ei ole välilyöntejä. Muuttujan arvoon viitataan
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:
| Operaattori | Toiminto |
|---|---|
+ |
yhteenlasku |
- |
vähennyslasku |
* |
kertolasku |
/ |
jakolasku |
% |
jakojäännös |
** |
potenssiin korotus |
Yksinkertaisia kokonaislukuaritmetiikan esimerkkejä:
$ a=5
$ c=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 reminder $e"
5 divided by 3 results 1 and reminder 2
Bashissa voidaan käyttää myös yksiulotteisia taulukkomuuttujia eli muuttujia, jotka sisältävät
luettelon alkioita. Tiettyyn taulukon alkioon voidaan viitata käyttämällä
indeksinumeroa hakasulkeissa taulukkomuuttujan nimen yhteydessä (${variable[index]}). Esimerkiksi
voimme määrittää yksinkertaisen kolmen alkion taulukon komennolla:
Voimme nyt tulostaa joko koko taulukon tai vain yhden sen alkioista 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. Esimerkiksi tässä tapauksessa komento
tulostaa arvon
Taulukkomuuttujien erikoistapaus on $, joka sisältää komentoriviargumentit,
eli alkiot, jotka voit antaa skriptillesi syöteparametreina.
Tässä tapauksessa taulukon $0 arvo viittaa varsinaisen skriptin nimeen, $1
ensimmäiseen argumenttiin, $2 toiseen 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, meidän on
annettava komennolle kaksi argumenttia. Ensimmäistä argumenttia käytetään tässä tapauksessa
kopiointikomennon lähdehakemiston määrittämiseen, 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 määrittämiseen ja suoritettavien komentojen kirjoittamiseen. Seuraavia lainausmerkkejä voidaan käyttää:
""Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti sen jälkeen, kun mahdolliset muuttujat on korvattu arvoillaan''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 kohdassa
Alla on joitakin esimerkkejä havainnollistamaan eri lainausmerkkien toiminnallisia eroja. Lainausmerkkejä voidaan käyttää muuttujien ja argumenttien kanssa. Kun käytetään kaksin- tai yksinkertaisia lainausmerkkejä, kaikki lainausmerkkien sisällä oleva teksti käsitellään yhtenä argumenttina. Näiden kahden lainausmerkkityypin ero on se, että kun käytetään kaksinkertaisia lainausmerkkejä, muuttujat korvataan niiden arvoilla, kun taas yksinkertaisilla lainausmerkeillä kaikki teksti käytetään sellaisenaan. Jos ajat komennot
tulos on
Mutta jos käytät sen sijaan yksinkertaisia lainausmerkkejä
saat tulosteen
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ä
haluamme käyttää grep-komentoa poimimaan kaikki rivit, jotka sisältävät merkkijonon
file size, tiedostosta nimeltä files.txt. Seuraava komento ei
toimisi:
Jos ajat 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ää command2:n tuottamaa tulosta argumenttina. Bash-
skriptissä sama toiminnallisuus voidaan tehdä myös syntaksilla
Silmukat ja ehtolauseet
Silmukoita ja ehtolauseita käytetään harvoin interaktiivisessa komentorivikäytössä. Niitä käytetään kuitenkin usein skripteissä suorittamaan samankaltaisia komentoja useita kertoja ja ohjaamaan suoritettavia komentoja. 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 toistuvasti siten, että jokaisella iteraatiolla silmukkamuuttuja asetetaan yhtä suureksi kuin yksi 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ä niillä on
pääte .old, voisimme ajaa komennon mv yhdeksän kertaa, tai voisimme 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 korvaamalla
alkioluettelon ilmaisulla $(ls project_3/). Nyt komentoa ls project3
käytetään tuottamaan luettelo käsiteltävistä tiedostonimistä:
for filename in $(ls project_3/)
do
echo "Moving file: $filename"
mv project_3/$filename project_3/"$filename".old
done
Bashissa voit myös luoda for-silmukan, jossa numeerisen indeksimuuttujan arvoa kasvatetaan automaattisesti tietyllä askelkoolla jokaisella iteraatiolla. Tässä tapauksessa syntaksi on:
Alla on for-silmukka, joka suorittaa saman uudelleennimeämistoiminnon kuin yllä, mutta käyttää alkioina pelkkiä 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 toimintaansa 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 operaattoreita if- ja while-
komentojen ehtolauseissa. Huomaa, että bash käyttää eri ehtolauseita
merkkijonoille ja kokonaisluvuille. Esimerkiksi merkkijonojen yhtäsuuruutta
testataan operaattorilla ==, kun taas kokonaislukujen yhtäsuuruutta testataan operaattorilla -eq.
Syntaksi on myös tarkka hakasulkeiden välisten välilyöntien suhteen, eikä
ehtolauseen määrittely [[a == b]] toimi, vaan se pitää
korjata muotoon [[ a == b ]].
Yleisesti käytetyt merkkijono-, kokonaisluku- ja tiedosto-operaattorit if- ja while-
lauseissa on lueteltu alla.
| Lauseke | Toiminto |
|---|---|
[[ a == b ]] |
Tosi, jos merkkijonot a ja b ovat samat |
[[ a != b ]] |
Tosi, jos merkkijonot a ja b eivät ole samat |
[[ a =~ b ]] |
Tosi, jos merkkijonot a ja b ovat samankaltaiset (sallii jokerimerkit) |
[[ a < b ]] |
Tosi, jos merkkijono a on aakkosjärjestyksessä ennen merkkijonoa b |
[[ a > b ]] |
Tosi, jos merkkijono a on aakkosjärjestyksessä merkkijonon b jälkeen |
[[ a -eq b ]] |
Tosi, jos kokonaisluvut a ja b ovat samat |
[[ a -ne b ]] |
Tosi, jos kokonaisluvut a ja b eivät ole samat |
[[ a -lt b ]] |
Tosi, jos kokonaisluku a on pienempi kuin b |
[[ a -gt b ]] |
Tosi, jos kokonaisluku a on suurempi kuin b |
[[ a -le b ]] |
Tosi, jos kokonaisluku a on pienempi tai yhtä suuri kuin b |
[[ a -ge b ]] |
Tosi, jos kokonaisluku a on suurempi tai yhtä suuri kuin b |
[[ -e name ]] |
Tosi, jos tiedosto on olemassa |
[[ -n a ]] |
Tosi, jos merkkijonon a pituus on suurempi kuin nolla |
[[ A || B ]] |
Tosi, jos ehto A tai ehto B on tosi (looginen OR) |
[[ A && B ]] |
Tosi, jos ehto A ja ehto B ovat tosia (looginen AND) |
[[ ! A ]] |
Tosi, jos ehto A ei ole tosi |
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ä (merkkijonoja) sisältäviä muuttujia:
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 vertailuja pienempi kuin ja suurempi kuin, sinun tulee olla varovainen, ettet sekoita merkkijono- ja kokonaislukuvertailuja. Esimerkiksi seuraava ehto
on FALSE, koska merkkijono 123 on aakkosjärjestyksessä ennen merkkijonoa 3. Sen sijaan
numeerinen vertailu
on TRUE.
On olemassa useita operaattoreita, joilla voit testata tiedoston eri ominaisuuksia.
Yleisimmin käytetty operaattori on -e, joka tarkistaa, onko tiedosto
olemassa. Oletetaan esimerkiksi, 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
testaamaan, onko tiedosto olemassa:
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 echo-komentoa
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,
tulisi käyttää printf-komentoa echo:n sijaan. printf-komennon
syntaksi on:
format definition määrittää, minkä tyyppistä tulostetta kirjoitetaan.
Yleisiä tyyppejä ovat teksti (%s), kokonaisluvut (%i) ja
liukuluvut (%f). Muotoilumäärittelyt voivat myös määrittää, kuinka paljon
tilaa kullekin argumentille varataan ja miten se sijoitetaan sarakkeeseen.
Alla on joitakin yksinkertaisia esimerkkejä havainnollistamaan printf-
komennon käyttöä:
tulostaa
Tässä muotoilumäärittely määrittää, että ensimmäistä argumenttia käsitellään
kokonaislukuna, toista ja kolmatta merkkijonoina ja neljättä argumenttia
liukulukuna. Huomaa, että oletusarvoisesti printf ei lisää
rivinvaihtomerkkiä tulosteen loppuun. Jotta näin tapahtuisi, muotoilumäärittelyn
tulee päättyä määrittelyyn \n.
Seuraavassa esimerkissä määritämme, kuinka monta merkkiä kullekin argumentille varataan. Komento
tulostaa
Tässä varaamme ensimmäiselle kokonaisluvulle neljä merkkiä ja sitten kymmenen merkkiä kummallekin merkkijonolle. Liukuluku esitetään kuudella merkillä, joista kaksi on desimaalipisteen jälkeen.
Voit myös lisätä muotoilumäärittelyyn tekstiä ja ohjausmerkkejä, kuten sarkaimen (\t).
Komento
tulostaa
Linux-skripteissä printf-komentoa käytetään tyypillisesti muuttujissa
tallennettujen arvojen tulostamiseen. Esimerkiksi komennot
tulostavat