Ero sivun ”Bash-skriptaus” versioiden välillä
(→While) |
p (maininta POSIX:sta lisätty) |
||
(50 välissä olevaa versiota 23 käyttäjän tekeminä ei näytetä) | |||
Rivi 1: | Rivi 1: | ||
Komentorivi on jo yksinään [[Komentorivin perusteet|monipuolinen]] työkalu, mutta skriptien avulla on mahdollista toteuttaa huomattavasti monipuolisempia prosesseja pienellä vaivalla. Tässä oppaassa käsitellään skriptien kirjoittamista [[ | Komentorivi on jo yksinään [[Komentorivin perusteet|monipuolinen]] työkalu, mutta skriptien avulla on mahdollista toteuttaa huomattavasti monipuolisempia prosesseja pienellä vaivalla. Tässä oppaassa käsitellään skriptien kirjoittamista [[Bash]]-[[komentotulkki|komentotulkille]]. | ||
Kirjoittaaksesi käyttöjärjestelmäriippumattomia skriptejä on syytä kirjoittaa skriptit [[POSIX]]-yhteensopivina [[POSIX-skriptaus|POSIX-skripteinä]]. | |||
== Alkuun == | == Alkuun == | ||
Skripti aloitetaan rivillä | Skripti aloitetaan rivillä | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
</syntaxhighlight> | |||
Joka kertoo, että skripti suoritetaan käyttäen bashia riippumatta siitä, mitä komentotulkkia käyttäjä käyttää ajaessaan skriptin. Huomaa, että vaikka rivi alkaa #-merkillä, se ei ole kommentti. Muualla skriptissä #:llä alkavat rivit ovat kommentteja, eikä niitä huomioida. | Joka kertoo, että skripti suoritetaan käyttäen bashia riippumatta siitä, mitä komentotulkkia käyttäjä käyttää ajaessaan skriptin. Huomaa, että vaikka rivi alkaa #-merkillä, se ei ole kommentti. Muualla skriptissä #:llä alkavat rivit ovat kommentteja, eikä niitä huomioida. | ||
Rivi 10: | Rivi 14: | ||
=== Ensimmäinen skripti === | === Ensimmäinen skripti === | ||
Tehdään aluksi yksinkertainen skripti, joka esittelee muutamia perustoimintoja: | Tehdään aluksi yksinkertainen skripti, joka esittelee muutamia perustoimintoja: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
echo "Hei, $(whoami), mitä kuuluu" | |||
echo "Olet hakemistossa $(pwd), tiedostolistaus:" | |||
ls | |||
</syntaxhighlight> | |||
Kuten huomaat, skripteissä käytetään tavallisia komentorivikomentoja, jotka sitten suoritetaan järjestyksessä. Toisaalta kuten myöhemmin huomaamme, skriptit tarjoavat paljon enemmän mahdollisuuksia kuin pelkkä komentojen näpyttely konsoliin. | Kuten huomaat, skripteissä käytetään tavallisia komentorivikomentoja, jotka sitten suoritetaan järjestyksessä. Toisaalta kuten myöhemmin huomaamme, skriptit tarjoavat paljon enemmän mahdollisuuksia kuin pelkkä komentojen näpyttely konsoliin. | ||
Rivi 20: | Rivi 26: | ||
Olet hakemistossa /home/kayttaja/ohjelmointi/skriptit, tiedostolistaus: | Olet hakemistossa /home/kayttaja/ohjelmointi/skriptit, tiedostolistaus: | ||
eka.sh | eka.sh | ||
Esimerkistä nähdään heti muutama perusasia. Ensinnäkin toisella rivillä käytetään ohjelmaa [[whoami]], joka tulostaa käyttäjänimen. Kun ohjelma on | Esimerkistä nähdään heti muutama perusasia. Ensinnäkin toisella rivillä käytetään ohjelmaa [[whoami]], joka tulostaa käyttäjänimen. Kun ohjelma on $()-merkinnän sisällä, kyseiseen kohtaan "kopioidaan" ajetun ohjelman tuloste. | ||
Toisella rivillä käytetään muuttujaa. Muuttujien edessä on dollarimerkki ($), ja niitä voidaan tunkea vaikka merkkijonojen (jotka erotetaan lainausmerkillä) sisälle. Muuttujia käsitellään tarkemmin myöhemmin. Esimerkissä käyttämämme muuttuja <tt>$PWD</tt> on [[ympäristömuuttuja]], jonka arvona on aina se hakemisto, jossa käyttäjä on. Voit kokeilla tätä esimerkiksi kirjoittamalla komentoriville komennon <tt>echo $PWD</tt>. | Toisella rivillä käytetään muuttujaa. Muuttujien edessä on dollarimerkki ($), ja niitä voidaan tunkea vaikka merkkijonojen (jotka erotetaan lainausmerkillä) sisälle. Muuttujia käsitellään tarkemmin myöhemmin. Esimerkissä käyttämämme muuttuja <tt>$PWD</tt> on [[ympäristömuuttuja]], jonka arvona on aina se hakemisto, jossa käyttäjä on. Voit kokeilla tätä esimerkiksi kirjoittamalla komentoriville komennon <tt>echo $PWD</tt>. | ||
Viimeisellä rivillä ajetaan normaalisti komentoriviohjelma [[ls]]. Voit myös kokeilla korvata tämän rivin rivillä | Viimeisellä rivillä ajetaan normaalisti komentoriviohjelma [[ls]]. Voit myös kokeilla korvata tämän rivin rivillä | ||
<syntaxhighlight lang="bash"> | |||
echo "`ls`" | |||
</syntaxhighlight> | |||
Merkintä $() voidaan kirjoittaa myös ``, kuten alla esimerkissä. Tällöin kuitenkaan ei voi käyttää sisäkkäisiä komentoja. | |||
Skripti voitaisiin myös kirjoittaa suoraan komentoriville muodossa | |||
<syntaxhighlight lang="bash"> | |||
echo "Hei, `whoami`, mitä kuuluu"; echo "Olet hakemistossa $PWD, tiedostolistaus:"; ls; | |||
</syntaxhighlight> | |||
== Muuttujat == | == Muuttujat == | ||
Muuttujat määritellään syntaksilla | Muuttujat määritellään syntaksilla | ||
<syntaxhighlight lang="bash"> | |||
nimi=arvo | |||
</syntaxhighlight> | |||
ja niihin viitataan laittamalla muuttujan eteen $ | ja niihin viitataan laittamalla muuttujan eteen $ | ||
<syntaxhighlight lang="bash"> | |||
$nimi | |||
</syntaxhighlight> | |||
Siinä tapauksessa mikäli halutaan, että komennon tulos on muuttuja asetetaan muuttuja tyyliin: | |||
<syntaxhighlight lang="bash"> | |||
nimi=("komennon nimi ja parametrit") | |||
</syntaxhighlight> | |||
Tapauksia, joissa käyttäjän tarkoittama muuttuja on epäselvä, tulee käyttää aaltosulkuja muuttujan ympärillä | Tapauksia, joissa käyttäjän tarkoittama muuttuja on epäselvä, tulee käyttää aaltosulkuja muuttujan ympärillä | ||
<syntaxhighlight lang="bash"> | |||
${foo}bar | |||
</syntaxhighlight> | |||
kun halutaan tulostaa teksti <tt>bar</tt> muuttujan <tt>foo</tt> jälkeen. | kun halutaan tulostaa teksti <tt>bar</tt> muuttujan <tt>foo</tt> jälkeen. | ||
Ensimmäisen skriptimme tapauksessa käytimme jo ympäristömuuttujaa $PWD echo:n kanssa. Tehdäänpä samantyyppinen skripti nyt käyttäen itse määrittelemäämme muuttujaa: | Ensimmäisen skriptimme tapauksessa käytimme jo ympäristömuuttujaa $PWD echo:n kanssa. Tehdäänpä samantyyppinen skripti nyt käyttäen itse määrittelemäämme muuttujaa: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
HEI="Hei, `whoami`, mitä kuuluu?" | |||
echo $HEI | |||
</syntaxhighlight> | |||
Kuten huomaat, kun muuttujaan sijoitetaan arvo, voidaan käyttää samoja "kikkoja" kuin muuallakin skriptissä, esimerkiksi tässä tapauksessa sijoitamme muuttujaan merkkijonon jonka sisällä on ohjelman <tt>whoami</tt> tuloste. | Kuten huomaat, kun muuttujaan sijoitetaan arvo, voidaan käyttää samoja "kikkoja" kuin muuallakin skriptissä, esimerkiksi tässä tapauksessa sijoitamme muuttujaan merkkijonon jonka sisällä on ohjelman <tt>whoami</tt> tuloste. | ||
Rivi 49: | Rivi 73: | ||
MUUTTUJA= "moi" | MUUTTUJA= "moi" | ||
MUUTTUJA ="moi" | MUUTTUJA ="moi" | ||
Lisäksi muuttujaan sijoitettavan arvon on oltava lainausmerkkien sisällä, jos siihen kuuluu välilyönti. Yksittäisen sanan tai numeron voi sijoittaa ilman lainausmerkkejä: | Lisäksi muuttujaan sijoitettavan arvon on oltava lainausmerkkien sisällä, jos siihen kuuluu välilyönti. Yksittäisen sanan tai numeron voi sijoittaa ilman lainausmerkkejä: | ||
<syntaxhighlight lang="bash"> | |||
numero=43 | |||
</syntaxhighlight> | |||
== Ehtolauseet == | == Ehtolauseet == | ||
Ehdollisia rakenteita luodaan bashille if, then, else ja fi -avainsanoilla. Rakenteen syntaksi on seuraava: | Ehdollisia rakenteita luodaan bashille if, then, else ja fi -avainsanoilla. Rakenteen syntaksi on seuraava: | ||
<syntaxhighlight lang="bash"> | |||
if [ ehto ] | |||
then | |||
lauseita | lauseita | ||
elif [ toinen ehto ] | |||
then | |||
lauseita | lauseita | ||
else | |||
muita lauseita | muita lauseita | ||
fi | |||
</syntaxhighlight> | |||
Ehdot merkitään hakasulkeiden sisään esimerkiksi seuraavalla tavalla | Ehdot merkitään hakasulkeiden sisään esimerkiksi seuraavalla tavalla | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
if [ $PWD = "/" ] | |||
then | |||
echo "Tämä skripti suoritetaan juurihakemistossa"; | |||
else | |||
echo "Skripti suoritetaan hakemistossa $PWD"; | |||
fi | |||
</syntaxhighlight> | |||
=== Yhden rivin ehtolauseet === | |||
Lyhyiden skriptien kanssa. | |||
<syntaxhighlight lang="bash"> | |||
[ ehto ] && lauseita || [ toinen ehto ] && lauseita || muita lauseita | |||
[ $PWD = "/" ] && printf "\nTämä skripti suoritetaan juurihakemistossa\n" || printf "\nSkripti suoritetaan hakemistossa %s\n" "$PWD" | |||
</syntaxhighlight> | |||
Tässä käytetään vertailuoperaattoria =, joka on tosi, jos merkkijonot ovat samat. Muita vertailuoperaattoreita ovat | Tässä käytetään vertailuoperaattoria =, joka on tosi, jos merkkijonot ovat samat. Muita vertailuoperaattoreita ovat | ||
Rivi 80: | Rivi 119: | ||
|- | |- | ||
<!-- taulukko alkaa tästä --> | <!-- taulukko alkaa tästä --> | ||
| ! || Negaatio-operaattori, "Onko seuraava ehto epätosi?" | |||
|- | |||
| -n || Pituus ei ole 0 | | -n || Pituus ei ole 0 | ||
|- | |- | ||
Rivi 112: | Rivi 153: | ||
Esimerkiksi seuraavat ehdot ovat tosia | Esimerkiksi seuraavat ehdot ovat tosia | ||
<syntaxhighlight lang="bash"> | |||
[ 2 -lt 5 ] | |||
[ "moi" != "linux" ] | |||
[ 54 -ge 53 ] | |||
[ -f "/etc/fstab" ] | |||
</syntaxhighlight> | |||
== Silmukat == | == Silmukat == | ||
Rivi 121: | Rivi 164: | ||
For-silmukka on näppärä monessa tapauksessa, tässä esimerkkinä hakemiston kaikkien tiedostojen läpikäynti. | For-silmukka on näppärä monessa tapauksessa, tässä esimerkkinä hakemiston kaikkien tiedostojen läpikäynti. | ||
<syntaxhighlight lang="bash"> | |||
for muuttuja in `ls` | |||
do | |||
echo $muuttuja | |||
Silmukassa suoritettavat rivit laitetaan siis sanojen <tt>do</tt> ja <tt>done</tt> väliin. Laskurimuuttujaan sijoitetaan vuorotellen <tt>in</tt>:n jälkeen olevat sanat, esimerkiksi tässä tapauksessa | done | ||
</syntaxhighlight> | |||
Silmukassa suoritettavat rivit laitetaan siis sanojen <tt>do</tt> ja <tt>done</tt> väliin. Laskurimuuttujaan sijoitetaan vuorotellen <tt>in</tt>:n jälkeen olevat sanat, esimerkiksi tässä tapauksessa komennon [[ls]] tuloste (eli hakemistossa olevien tiedostojen nimet). Ohjelman tuloste voidaan merkitä myös seuraavasti: <tt>$(komento)</tt>. Jos silmukalla on käytävä läpi kaikki kokonaisluvut tietyltä väliltä, voi käyttää komentoa [[seq]], joka tulostaa kokonaisluvut väliltä 1-annettu parametri. Esimerkiksi | |||
<syntaxhighlight lang="bash"> | |||
echo "Lasketaan kymmeneen:" | |||
for luku in $(seq 10) | |||
do | |||
echo $luku | |||
done | |||
</syntaxhighlight> | |||
Läpi käytävä joukko voidaan merkitä myös syntaksilla <tt>{alku..loppu}</tt>. Jos esimerkiksi halutaan käydä läpi kaikki merkit c-o, kokeile vaikka kirjoittaa | Läpi käytävä joukko voidaan merkitä myös syntaksilla <tt>{alku..loppu}</tt>. Jos esimerkiksi halutaan käydä läpi kaikki merkit c-o, kokeile vaikka kirjoittaa | ||
<syntaxhighlight lang="bash">for muuttuja in {c..o}; { echo -n "$muuttuja "; sleep 1;}</syntaxhighlight> | |||
=== While === | === While === | ||
While suorittaa lausekkeita niin kauan | While suorittaa lausekkeita niin kauan kuin ehto on tosi. Kun ehto on epätosi poistutaan loopista jolloin skriptin suoritus jatkuu seuraavalta loogiselta riviltä (while-lausekkeen jälkeiseltä riviltä). | ||
<syntaxhighlight lang="bash"> | |||
while [ ehto ] | |||
do | |||
lauseita | |||
done | |||
Esimerkki: | Esimerkki: | ||
while [ -f "testi" ] | |||
do | |||
echo "Tiedosto on olemassa." | |||
sleep 1 | |||
done | |||
</syntaxhighlight> | |||
Edellä oleva skripti tarkistaa, onko samassa hakemistossa oleva tiedosto <tt>testi</tt> olemassa. Tätä jatketaan niin kauan kunnes tiedostoa ei enää ole. | Edellä oleva skripti tarkistaa, onko samassa hakemistossa oleva tiedosto <tt>testi</tt> olemassa. Tätä jatketaan niin kauan kunnes tiedostoa ei enää ole. | ||
=== Until === | === Until === | ||
Until on while:n vastakohta. Until-lauseke jatkaa suoritusta niin kauan kunnes ehto on tosi. | Until on while:n vastakohta. Until-lauseke jatkaa suoritusta niin kauan kunnes ehto on tosi. | ||
<syntaxhighlight lang="bash"> | |||
until [ ehto ] | |||
do | |||
lauseita | |||
done | |||
</syntaxhighlight> | |||
== Funktiot == | == Funktiot == | ||
Kuten "oikeissa" ohjelmointikielissä, myös bash-skripteissä on mahdollista käyttää funktioita seuraavalla tavalla | Kuten "oikeissa" ohjelmointikielissä, myös bash-skripteissä on mahdollista käyttää funktioita seuraavalla tavalla | ||
< | |||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
function Käyttis { | function Käyttis { | ||
Rivi 166: | Rivi 220: | ||
Käyttis | Käyttis | ||
echo $os | echo $os | ||
</ | </syntaxhighlight> | ||
Tulostaisi "Linux". | Tulostaisi "Linux". | ||
Funktio määritellään siis syntaksilla | Funktio määritellään siis syntaksilla | ||
< | <syntaxhighlight lang="bash"> | ||
function Funktio { | function Funktio { | ||
#koodi | #koodi | ||
} | } | ||
</ | </syntaxhighlight> | ||
Jos halutaan skriptin toimivan myös [[Sh]]-komentotulkkia käytettäessä, funktion kanssa on käytettävä syntaksia | Jos halutaan skriptin toimivan myös [[Sh]]-komentotulkkia käytettäessä, funktion kanssa on käytettävä syntaksia | ||
< | <syntaxhighlight lang="bash"> | ||
Funktio() { ... } | Funktio() { ... } | ||
</ | </syntaxhighlight> | ||
Jos muuttujia ei funktiossa erikseen määritellä yksityisiksi, ne ovat julkisia. Eli kun esimerkkiskriptissämme muutimme muuttujan $os arvoa funktiossa, tämä muutos näkyi myös funktion ulkopuolella. Muuttuja voidaan määritellä yksityiseksi avainsanalla <tt>local</tt>: | Jos muuttujia ei funktiossa erikseen määritellä yksityisiksi, ne ovat julkisia. Eli kun esimerkkiskriptissämme muutimme muuttujan $os arvoa funktiossa, tämä muutos näkyi myös funktion ulkopuolella. Muuttuja voidaan määritellä yksityiseksi avainsanalla <tt>local</tt>: | ||
< | |||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
function Käyttis { | function Käyttis { | ||
Rivi 191: | Rivi 248: | ||
Käyttis | Käyttis | ||
echo $os | echo $os | ||
</ | </syntaxhighlight> | ||
Tulostaisi "Debian". | Tulostaisi "Debian". | ||
Rivi 197: | Rivi 255: | ||
=== Joukot === | === Joukot === | ||
Käytimme jo aiemmin merkintätapaa <tt>{1..4}</tt> merkitsemään joukkoa, johon kuuluu kaikki välin merkit (esimerkissä 1,2,3,4). Tämä ominaisuus on kuitenkin monipuolisempi. Esimerkiksi jos haluamme luoda Matille, Martalle ja Jormalle työ- ja kotihakemistot, voisimme kirjoittaa lyhyesti näin | Käytimme jo aiemmin merkintätapaa <tt>{1..4}</tt> merkitsemään joukkoa, johon kuuluu kaikki välin merkit (esimerkissä 1,2,3,4). Tämä ominaisuus on kuitenkin monipuolisempi. Esimerkiksi jos haluamme luoda Matille, Martalle ja Jormalle työ- ja kotihakemistot, voisimme kirjoittaa lyhyesti näin | ||
mkdir {ma{ti, | mkdir {ma{ti,rta},jorma}n_{työ,koti}hakemisto | ||
Jolloin luotaisiin hakemistot matin_työhakemisto, matin_kotihakemisto, martan_työhakemisto, martan_kotihakemisto, jorman_työhakemisto ja jorman_kotihakemisto. | Jolloin luotaisiin hakemistot matin_työhakemisto, matin_kotihakemisto, martan_työhakemisto, martan_kotihakemisto, jorman_työhakemisto ja jorman_kotihakemisto. | ||
Puretaanpa hieman tuota komentoa. <tt>ma{ti,rta}</tt> tuottaa sanat mati ja marta. Kun joukon merkinnän ulkopuolella on n, liitetään jokaisen joukon sisällä olevan alkion perään n, jolloin alaviivaa edellä oleva osuus tuottaa sanat matin, martan ja jorman. Jälkimmäinen osuus toimii samalla tavalla, sanojen työ ja koti perään liitetään sana hakemisto. Näin saamme lyhyellä komennolla aikaan paljon. | Puretaanpa hieman tuota komentoa. <tt>ma{ti,rta}</tt> tuottaa sanat mati ja marta. Kun joukon merkinnän ulkopuolella on n, liitetään jokaisen joukon sisällä olevan alkion perään n, jolloin alaviivaa edellä oleva osuus tuottaa sanat matin, martan ja jorman. Jälkimmäinen osuus toimii samalla tavalla, sanojen työ ja koti perään liitetään sana hakemisto. Näin saamme lyhyellä komennolla aikaan paljon. | ||
Tärkeä huomio on, että <tt>{}</tt>-joukon tulkitsee bash-komentotulkki eikä ohjelma, jonka yhteydessä <tt>{}</tt>-joukkoa käytetään. Käytännössä tämä tarkoittaa, että ensin bash ikään kuin avaa joukon ja vasta sen jälkeen suorittaa komentorivin. Tässä esimerkissä <tt>mkdir</tt>-ohjelma saa parametreikseen vain listan luotavista hakemistoista eikä <tt>mkdir</tt> edes tiedä, että käytettiin <tt>{}</tt>-joukkoa: | |||
mkdir matin_työhakemisto matin_kotihakemisto martan_työhakemisto martan_kotihakemisto jorman_työhakemisto jorman_kotihakemisto | |||
Tässä huomataan myös, että komentoja voi käyttää aivan normaalien [[Komentorivikomennot|komentoriviohjelmien]] kanssa, sillä bash käsittelee syötteen, ja suorittaa skriptit antaen ohjelmalle syötteenä skriptin tulosteen. | Tässä huomataan myös, että komentoja voi käyttää aivan normaalien [[Komentorivikomennot|komentoriviohjelmien]] kanssa, sillä bash käsittelee syötteen, ja suorittaa skriptit antaen ohjelmalle syötteenä skriptin tulosteen. | ||
Rivi 207: | Rivi 268: | ||
Unixin filosofian mukaisesti Linuxissa on lukuisia ohjelmia, jotka hoitavat jonkin yksittäisen tehtävän ja tekevät sen hyvin. Näitä pikkuohjelmia käytetäänkin usein skripteissä hoitamaan erilaisia tehtäviä. | Unixin filosofian mukaisesti Linuxissa on lukuisia ohjelmia, jotka hoitavat jonkin yksittäisen tehtävän ja tekevät sen hyvin. Näitä pikkuohjelmia käytetäänkin usein skripteissä hoitamaan erilaisia tehtäviä. | ||
Kaikista Linux-järjestelmistä löytyviä ohjelmia on listattu luokkaan [[:Luokka:Komentorivin perustyökalut|Komentorivin perustyökalut]]. Näitä ohjelmia kannattaa selailla. Komentoriviohjelmia on listattu käyttötarkoituksen mukaan myös artikkelissa [[komentorivikomennot]] | Kaikista Linux-järjestelmistä löytyviä ohjelmia on listattu luokkaan [[:Luokka:Komentorivin perustyökalut|Komentorivin perustyökalut]]. Näitä ohjelmia kannattaa selailla. Komentoriviohjelmia on listattu käyttötarkoituksen mukaan myös artikkelissa [[komentorivikomennot]]. | ||
Esimerkkinä ladataan kaikki [[LinuCast]]-podcastin [[ogg]]-muotoiset jaksot käyttämällä jokaisesta järjestelmästä löytyviä peruskomentoja [[seq]] ja [[wget]]: | |||
(esimerkin sivusto on kuollut ja kuopattu, joten koodi ei toimi) | |||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
# linucast.sh - LinuCast-podcastin latausskripti | |||
for i in `seq -f"%03g" 1 104` | |||
do | |||
wget -c "http://koskisuomi.pp.fi/linucast/LinuCast$i.ogg" | |||
done | |||
</syntaxhighlight> | |||
Esimerkkiohjelma voidaan tallentaa vaikka nimelle <tt>linuxcast.sh</tt> ja sille voidaan antaa [[chmod|suoritusoikeudet]] komennolla | |||
chmod u+rx linucast.sh | |||
Tämän jälkeen komentojono ajettaisiin komennolla | |||
./linucast.sh | |||
*1 tarkoittaa että mistä numerosta aloitetaan | |||
*104 tarkoittaa kuinka monta jaksoa haetaan, lukua voi kasvattaa jaksojen lisääntyessä. | |||
Esimerkki ohjelmana 2. Ladataan kaikki Full Circle magazinen englanninkieliset pdf-tiedostot palvelimelta. Esimerkissä on käytetty Bash skriptaus koodeja joita löytyy esimerkiksi tältä sivulta. | |||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | |||
# issueLataaja.sh - Ubuntu-aiheisen verkkojulkaisu Full Circle:n artikkelilataaja, kerää ne kaikki! | |||
# Tyhjennetään ruutu | |||
clear | |||
# Haetaan kaikki Full Circlen artikkelit ensimmäisestä beta-julkaisusta (0) kaikkein uusimpaan asti. | |||
# Listataan julkaisut verkkosivulta, joista viimeisin tallennetaan muuttujaksi. | |||
viimeisin_julkaisu=$(curl -s https://fullcirclemagazine.org/downloads/ | grep -oP '(?<=Issue )[0-9]+' | tail -1); | |||
for luku in $(seq 0 $viimeisin_julkaisu) | |||
do | |||
FILE="issue"$luku"_en.pdf" | |||
# Tarkistetaan onko tiedosto jo olemassa, ennen kuin ladataan se. | |||
if [ -f $FILE ]; then | |||
echo " '$FILE' tiedosto on olemassa." | |||
else | |||
echo " '$FILE' tiedostoa ei ole olemassa. Ladataan se..." | |||
wget "https://dl.fullcirclemagazine.org/$FILE" | |||
# Pidetään 5 sekunnin tauko latausten välissä, mukana laskuri | |||
for (( i=5; i>0; i--)); do | |||
sleep 1 & | |||
printf "\b\b$i" | |||
wait | |||
done | |||
fi | |||
done | |||
echo "Lataus valmis" | |||
</syntaxhighlight> | |||
Esimerkkiohjelma voidaan tallentaa vaikka nimelle <tt>issueLataaja.sh</tt> ja sille voidaan antaa [[chmod|suoritusoikeudet]] komennolla | |||
chmod u+rx issueLataaja.sh | |||
Tämän jälkeen komentojono ajettaisiin komennolla | |||
./issueLataaja.sh | |||
== Katso myös == | == Katso myös == | ||
*[[Komentorivin perusteet]] | *[[Komentorivin perusteet]] | ||
*[[Komentorivivinkkejä]] | *[[Komentorivivinkkejä]] | ||
*[[ShellCheck]] on ohjelma, joka havaitsee kaikki [[sh]] ja [[bash]]-skriptien ongelmat ja antaa niille parannusehdotukset. | |||
==Aiheesta muualla== | ==Aiheesta muualla== | ||
*[ | *[https://tldp.org/LDP/abs/html/ Advanced Bash-Scripting Guide] - laaja englanninkielinen opas bash-skriptaukseen | ||
*[ | *[https://www.panix.com/~elflord/unix/bash-tute.html Pikaopas bash-skriptaukseen] (englanniksi) | ||
*[https://web.archive.org/web/20170610153335/https://gd.tuwien.ac.at/linuxcommand.org/writing_shell_scripts.php Writing shell scripts] - toinen laaja englanninkielinen opas bash-skriptaukseen - {{InternetArchive}} | |||
*[https://www.shellcheck.net/ ShellCheck] on www-ohjelma, joka havaitsee kaikki [[sh]] ja [[bash]]-skriptien ongelmat ja antaa niille parannusehdotukset. | |||
*[https://explainshell.com/ ExplainShell] on www-ohjelma, joka antaa ohjetekstin annetuille komennoille. | |||
*[https://mywiki.wooledge.org/BashPitfalls Bash Pitfalls] yleisimmät bash-skriptauksen sudenkuopat. | |||
[[Luokka:Komentorivi]] | [[Luokka:Komentorivi]] | ||
[[Luokka:Ohjeet]] | [[Luokka:Ohjeet]] |
Versio 29. tammikuuta 2023 kello 08.19
Komentorivi on jo yksinään monipuolinen työkalu, mutta skriptien avulla on mahdollista toteuttaa huomattavasti monipuolisempia prosesseja pienellä vaivalla. Tässä oppaassa käsitellään skriptien kirjoittamista Bash-komentotulkille.
Kirjoittaaksesi käyttöjärjestelmäriippumattomia skriptejä on syytä kirjoittaa skriptit POSIX-yhteensopivina POSIX-skripteinä.
Alkuun
Skripti aloitetaan rivillä
#!/bin/bash
Joka kertoo, että skripti suoritetaan käyttäen bashia riippumatta siitä, mitä komentotulkkia käyttäjä käyttää ajaessaan skriptin. Huomaa, että vaikka rivi alkaa #-merkillä, se ei ole kommentti. Muualla skriptissä #:llä alkavat rivit ovat kommentteja, eikä niitä huomioida.
Skriptejä ei kuitenkaan ole pakko kirjoittaa erilliseen tiedostoon, vaan ne voi kirjoittaa myös suoraan komentoriville. Tällöin eri komentojen väliin laitetaan erottimeksi puolipiste (;).
Ensimmäinen skripti
Tehdään aluksi yksinkertainen skripti, joka esittelee muutamia perustoimintoja:
#!/bin/bash
echo "Hei, $(whoami), mitä kuuluu"
echo "Olet hakemistossa $(pwd), tiedostolistaus:"
ls
Kuten huomaat, skripteissä käytetään tavallisia komentorivikomentoja, jotka sitten suoritetaan järjestyksessä. Toisaalta kuten myöhemmin huomaamme, skriptit tarjoavat paljon enemmän mahdollisuuksia kuin pelkkä komentojen näpyttely konsoliin.
Tallenna skripti vaikka nimellä eka.sh (sh-päätettä käytetään yleensä skriptien kanssa), anna sille suoritusoikeudet ja aja skripti, jolloin tulostus on suunnilleen seuraavanlainen:
Hei, käyttäjä, mitä kuuluu Olet hakemistossa /home/kayttaja/ohjelmointi/skriptit, tiedostolistaus: eka.sh
Esimerkistä nähdään heti muutama perusasia. Ensinnäkin toisella rivillä käytetään ohjelmaa whoami, joka tulostaa käyttäjänimen. Kun ohjelma on $()-merkinnän sisällä, kyseiseen kohtaan "kopioidaan" ajetun ohjelman tuloste.
Toisella rivillä käytetään muuttujaa. Muuttujien edessä on dollarimerkki ($), ja niitä voidaan tunkea vaikka merkkijonojen (jotka erotetaan lainausmerkillä) sisälle. Muuttujia käsitellään tarkemmin myöhemmin. Esimerkissä käyttämämme muuttuja $PWD on ympäristömuuttuja, jonka arvona on aina se hakemisto, jossa käyttäjä on. Voit kokeilla tätä esimerkiksi kirjoittamalla komentoriville komennon echo $PWD.
Viimeisellä rivillä ajetaan normaalisti komentoriviohjelma ls. Voit myös kokeilla korvata tämän rivin rivillä
echo "`ls`"
Merkintä $() voidaan kirjoittaa myös ``, kuten alla esimerkissä. Tällöin kuitenkaan ei voi käyttää sisäkkäisiä komentoja.
Skripti voitaisiin myös kirjoittaa suoraan komentoriville muodossa
echo "Hei, `whoami`, mitä kuuluu"; echo "Olet hakemistossa $PWD, tiedostolistaus:"; ls;
Muuttujat
Muuttujat määritellään syntaksilla
nimi=arvo
ja niihin viitataan laittamalla muuttujan eteen $
$nimi
Siinä tapauksessa mikäli halutaan, että komennon tulos on muuttuja asetetaan muuttuja tyyliin:
nimi=("komennon nimi ja parametrit")
Tapauksia, joissa käyttäjän tarkoittama muuttuja on epäselvä, tulee käyttää aaltosulkuja muuttujan ympärillä
${foo}bar
kun halutaan tulostaa teksti bar muuttujan foo jälkeen.
Ensimmäisen skriptimme tapauksessa käytimme jo ympäristömuuttujaa $PWD echo:n kanssa. Tehdäänpä samantyyppinen skripti nyt käyttäen itse määrittelemäämme muuttujaa:
#!/bin/bash
HEI="Hei, `whoami`, mitä kuuluu?"
echo $HEI
Kuten huomaat, kun muuttujaan sijoitetaan arvo, voidaan käyttää samoja "kikkoja" kuin muuallakin skriptissä, esimerkiksi tässä tapauksessa sijoitamme muuttujaan merkkijonon jonka sisällä on ohjelman whoami tuloste.
Huomaa, että yhtäsuuruusmerkin on oltava kiinni muuttujan nimessä ja arvossa, eli seuraavat muodot eivät käy:
MUUTTUJA = "terve vaan" MUUTTUJA= "moi" MUUTTUJA ="moi"
Lisäksi muuttujaan sijoitettavan arvon on oltava lainausmerkkien sisällä, jos siihen kuuluu välilyönti. Yksittäisen sanan tai numeron voi sijoittaa ilman lainausmerkkejä:
numero=43
Ehtolauseet
Ehdollisia rakenteita luodaan bashille if, then, else ja fi -avainsanoilla. Rakenteen syntaksi on seuraava:
if [ ehto ]
then
lauseita
elif [ toinen ehto ]
then
lauseita
else
muita lauseita
fi
Ehdot merkitään hakasulkeiden sisään esimerkiksi seuraavalla tavalla
#!/bin/bash
if [ $PWD = "/" ]
then
echo "Tämä skripti suoritetaan juurihakemistossa";
else
echo "Skripti suoritetaan hakemistossa $PWD";
fi
Yhden rivin ehtolauseet
Lyhyiden skriptien kanssa.
[ ehto ] && lauseita || [ toinen ehto ] && lauseita || muita lauseita
[ $PWD = "/" ] && printf "\nTämä skripti suoritetaan juurihakemistossa\n" || printf "\nSkripti suoritetaan hakemistossa %s\n" "$PWD"
Tässä käytetään vertailuoperaattoria =, joka on tosi, jos merkkijonot ovat samat. Muita vertailuoperaattoreita ovat
Operaattori | Kuvaus |
---|---|
! | Negaatio-operaattori, "Onko seuraava ehto epätosi?" |
-n | Pituus ei ole 0 |
-z | Pituus on 0 |
-d | Hakemisto on olemassa |
-f | Tavallinen tiedosto on olemassa |
-r | Onko tiedostoon lukuoikeus |
-w | Onko tiedostoon kirjoitusoikeus |
-x | Onko tiedostoon ajo-oikeus |
-eq | Kokonaisluvut ovat yhtäsuuret (a = b) |
-ne | Kokonaisluvut eivät ole yhtä suuret (a != b) |
= | Merkkijonot ovat samat (myös ==) |
!= | Merkkijonot eivät ole samat |
-lt | Kokonaisluku on pienempi kuin toinen |
-gt | Kokonaisluku on suurempi kuin toinen (a > b) |
-le | Kokonaisluku on pienempi tai yhtäsuuri kuin toinen (a <= b) |
-ge | Kokonaisluku on suurempi tai yhtäsuuri kuin toinen (a >= b) |
Esimerkiksi seuraavat ehdot ovat tosia
[ 2 -lt 5 ]
[ "moi" != "linux" ]
[ 54 -ge 53 ]
[ -f "/etc/fstab" ]
Silmukat
For
For-silmukka on näppärä monessa tapauksessa, tässä esimerkkinä hakemiston kaikkien tiedostojen läpikäynti.
for muuttuja in `ls`
do
echo $muuttuja
done
Silmukassa suoritettavat rivit laitetaan siis sanojen do ja done väliin. Laskurimuuttujaan sijoitetaan vuorotellen in:n jälkeen olevat sanat, esimerkiksi tässä tapauksessa komennon ls tuloste (eli hakemistossa olevien tiedostojen nimet). Ohjelman tuloste voidaan merkitä myös seuraavasti: $(komento). Jos silmukalla on käytävä läpi kaikki kokonaisluvut tietyltä väliltä, voi käyttää komentoa seq, joka tulostaa kokonaisluvut väliltä 1-annettu parametri. Esimerkiksi
echo "Lasketaan kymmeneen:"
for luku in $(seq 10)
do
echo $luku
done
Läpi käytävä joukko voidaan merkitä myös syntaksilla {alku..loppu}. Jos esimerkiksi halutaan käydä läpi kaikki merkit c-o, kokeile vaikka kirjoittaa
for muuttuja in {c..o}; { echo -n "$muuttuja "; sleep 1;}
While
While suorittaa lausekkeita niin kauan kuin ehto on tosi. Kun ehto on epätosi poistutaan loopista jolloin skriptin suoritus jatkuu seuraavalta loogiselta riviltä (while-lausekkeen jälkeiseltä riviltä).
while [ ehto ]
do
lauseita
done
Esimerkki:
while [ -f "testi" ]
do
echo "Tiedosto on olemassa."
sleep 1
done
Edellä oleva skripti tarkistaa, onko samassa hakemistossa oleva tiedosto testi olemassa. Tätä jatketaan niin kauan kunnes tiedostoa ei enää ole.
Until
Until on while:n vastakohta. Until-lauseke jatkaa suoritusta niin kauan kunnes ehto on tosi.
until [ ehto ]
do
lauseita
done
Funktiot
Kuten "oikeissa" ohjelmointikielissä, myös bash-skripteissä on mahdollista käyttää funktioita seuraavalla tavalla
#!/bin/bash
function Käyttis {
os="Linux"
}
os="Windows"
#Kutsutaan funktiota
Käyttis
echo $os
Tulostaisi "Linux".
Funktio määritellään siis syntaksilla
function Funktio {
#koodi
}
Jos halutaan skriptin toimivan myös Sh-komentotulkkia käytettäessä, funktion kanssa on käytettävä syntaksia
Funktio() { ... }
Jos muuttujia ei funktiossa erikseen määritellä yksityisiksi, ne ovat julkisia. Eli kun esimerkkiskriptissämme muutimme muuttujan $os arvoa funktiossa, tämä muutos näkyi myös funktion ulkopuolella. Muuttuja voidaan määritellä yksityiseksi avainsanalla local:
#!/bin/bash
function Käyttis {
local os # os paikallinen muuttuja
os="Gentoo"
}
os="Debian"
#Kutsutaan funktiota
Käyttis
echo $os
Tulostaisi "Debian".
Vinkkejä
Joukot
Käytimme jo aiemmin merkintätapaa {1..4} merkitsemään joukkoa, johon kuuluu kaikki välin merkit (esimerkissä 1,2,3,4). Tämä ominaisuus on kuitenkin monipuolisempi. Esimerkiksi jos haluamme luoda Matille, Martalle ja Jormalle työ- ja kotihakemistot, voisimme kirjoittaa lyhyesti näin
mkdir {ma{ti,rta},jorma}n_{työ,koti}hakemisto
Jolloin luotaisiin hakemistot matin_työhakemisto, matin_kotihakemisto, martan_työhakemisto, martan_kotihakemisto, jorman_työhakemisto ja jorman_kotihakemisto.
Puretaanpa hieman tuota komentoa. ma{ti,rta} tuottaa sanat mati ja marta. Kun joukon merkinnän ulkopuolella on n, liitetään jokaisen joukon sisällä olevan alkion perään n, jolloin alaviivaa edellä oleva osuus tuottaa sanat matin, martan ja jorman. Jälkimmäinen osuus toimii samalla tavalla, sanojen työ ja koti perään liitetään sana hakemisto. Näin saamme lyhyellä komennolla aikaan paljon.
Tärkeä huomio on, että {}-joukon tulkitsee bash-komentotulkki eikä ohjelma, jonka yhteydessä {}-joukkoa käytetään. Käytännössä tämä tarkoittaa, että ensin bash ikään kuin avaa joukon ja vasta sen jälkeen suorittaa komentorivin. Tässä esimerkissä mkdir-ohjelma saa parametreikseen vain listan luotavista hakemistoista eikä mkdir edes tiedä, että käytettiin {}-joukkoa:
mkdir matin_työhakemisto matin_kotihakemisto martan_työhakemisto martan_kotihakemisto jorman_työhakemisto jorman_kotihakemisto
Tässä huomataan myös, että komentoja voi käyttää aivan normaalien komentoriviohjelmien kanssa, sillä bash käsittelee syötteen, ja suorittaa skriptit antaen ohjelmalle syötteenä skriptin tulosteen.
Hyödyllisiä ohjelmia
Unixin filosofian mukaisesti Linuxissa on lukuisia ohjelmia, jotka hoitavat jonkin yksittäisen tehtävän ja tekevät sen hyvin. Näitä pikkuohjelmia käytetäänkin usein skripteissä hoitamaan erilaisia tehtäviä.
Kaikista Linux-järjestelmistä löytyviä ohjelmia on listattu luokkaan Komentorivin perustyökalut. Näitä ohjelmia kannattaa selailla. Komentoriviohjelmia on listattu käyttötarkoituksen mukaan myös artikkelissa komentorivikomennot.
Esimerkkinä ladataan kaikki LinuCast-podcastin ogg-muotoiset jaksot käyttämällä jokaisesta järjestelmästä löytyviä peruskomentoja seq ja wget:
(esimerkin sivusto on kuollut ja kuopattu, joten koodi ei toimi)
#!/bin/bash
# linucast.sh - LinuCast-podcastin latausskripti
for i in `seq -f"%03g" 1 104`
do
wget -c "http://koskisuomi.pp.fi/linucast/LinuCast$i.ogg"
done
Esimerkkiohjelma voidaan tallentaa vaikka nimelle linuxcast.sh ja sille voidaan antaa suoritusoikeudet komennolla
chmod u+rx linucast.sh
Tämän jälkeen komentojono ajettaisiin komennolla
./linucast.sh
- 1 tarkoittaa että mistä numerosta aloitetaan
- 104 tarkoittaa kuinka monta jaksoa haetaan, lukua voi kasvattaa jaksojen lisääntyessä.
Esimerkki ohjelmana 2. Ladataan kaikki Full Circle magazinen englanninkieliset pdf-tiedostot palvelimelta. Esimerkissä on käytetty Bash skriptaus koodeja joita löytyy esimerkiksi tältä sivulta.
#!/bin/bash
# issueLataaja.sh - Ubuntu-aiheisen verkkojulkaisu Full Circle:n artikkelilataaja, kerää ne kaikki!
# Tyhjennetään ruutu
clear
# Haetaan kaikki Full Circlen artikkelit ensimmäisestä beta-julkaisusta (0) kaikkein uusimpaan asti.
# Listataan julkaisut verkkosivulta, joista viimeisin tallennetaan muuttujaksi.
viimeisin_julkaisu=$(curl -s https://fullcirclemagazine.org/downloads/ | grep -oP '(?<=Issue )[0-9]+' | tail -1);
for luku in $(seq 0 $viimeisin_julkaisu)
do
FILE="issue"$luku"_en.pdf"
# Tarkistetaan onko tiedosto jo olemassa, ennen kuin ladataan se.
if [ -f $FILE ]; then
echo " '$FILE' tiedosto on olemassa."
else
echo " '$FILE' tiedostoa ei ole olemassa. Ladataan se..."
wget "https://dl.fullcirclemagazine.org/$FILE"
# Pidetään 5 sekunnin tauko latausten välissä, mukana laskuri
for (( i=5; i>0; i--)); do
sleep 1 &
printf "\b\b$i"
wait
done
fi
done
echo "Lataus valmis"
Esimerkkiohjelma voidaan tallentaa vaikka nimelle issueLataaja.sh ja sille voidaan antaa suoritusoikeudet komennolla
chmod u+rx issueLataaja.sh
Tämän jälkeen komentojono ajettaisiin komennolla
./issueLataaja.sh
Katso myös
- Komentorivin perusteet
- Komentorivivinkkejä
- ShellCheck on ohjelma, joka havaitsee kaikki sh ja bash-skriptien ongelmat ja antaa niille parannusehdotukset.
Aiheesta muualla
- Advanced Bash-Scripting Guide - laaja englanninkielinen opas bash-skriptaukseen
- Pikaopas bash-skriptaukseen (englanniksi)
- Writing shell scripts - toinen laaja englanninkielinen opas bash-skriptaukseen - [Wayback Machine]
- ShellCheck on www-ohjelma, joka havaitsee kaikki sh ja bash-skriptien ongelmat ja antaa niille parannusehdotukset.
- ExplainShell on www-ohjelma, joka antaa ohjetekstin annetuille komennoille.
- Bash Pitfalls yleisimmät bash-skriptauksen sudenkuopat.