Sormet skripteihin

ITKA203 Käyttöjärjestelmät -kurssin Demo 3 kesällä 2011. "Pikaintro shell-skripteihin"

Paavo Nieminen, paavo.j.nieminen@jyu.fi

Jyväskylän yliopiston tietotekniikan laitos. Suurenmoiset kiitokset Joel Lehtoselle hyödyllisistä täsmennyksistä!

Contents

Mistä tässä harjoitteessa on kyse

Tässä julkaistaan Bourne Shellin pikaohje, joka tulee tentissä liitteeksi. Skripteistä on vapaaehtoinen kahden pisteen arvoinen palautustehtävä.

Tavoitteet:

  • demossa 1 nähty interaktiivisen shellin käyttö laajennetaan skriptien tekemiseen
  • näet ja jopa jonkin verran teet skriptiohjelmointia, mikä toivottavasti valaisee mahdollisuuksia ja käyttötarkoituksia moiselle.

Skriptit

Tuolla on lisätietoa; hyvän näköinen intro:

Tuolla on myös; kattavan oloinen saitti:

Ja Googlella löytyy lisää.

Aloita tästä

Tämän kertaiset esimerkkikoodit ovat paketissa demo4.zip kurssin kotisivulla. Siis jalavassa saat paketin esim. näin:

cd kj11kesa/demo3  # eli siirry omaan demo3:n hakemistoon.
wget http://www.cc.jyu.fi/~nieminen/kj11kesa/demo3/demo3.zip
unzip demo3.zip

Ohjeet:

  • Paketista löytyvä skripti shrakenteita.sh demonstroi ohjelmointirakenteita Bourne Shellissä, ja se on samalla pieni esimerkki ympäristömuuttujasta (environment variable). Ajatus on, että ensiksi tätä esimerkkiä tutkimalla opit, kuinka bashissä eli Bourne Again Shellissä voi käyttää alkeellisia ohjelmointirakenteita.

  • Palautustehtävässä hyödynnät opittua ja muokkaat sen avulla skriptiä demo3.sh.

  • Skriptit ajetaan kuin ne olisivat tavallisia ohjelmia. Niinpä voit ajaa esim. skriptin shrakenteita.sh kirjoittamalla komennon:

    ./shrakenteita.sh
    
  • Skripteillä täytyy tällöin olla suoritusoikeus (nyt haetussa paketissa ne on valmiiksi asennettu; oikeuksienhallintaa ylipäätään käsitellään ensi viikon luennoilla)

  • Skriptin ensimmäinen rivi alkaa #! kertoen, millä tulkkiohjelmalla skripti on ajettava. Tässä demossa tulkiksi ladataan aina ohjelmatiedosto nimeltä /bin/bash eli Bourne Again Shell -- lisäksi kannattaa bashiä käyttäessä laittaa "turvaverkoksi" sen ymmärtämä argumentti -eu, jolloin skriptin ajo loppuu välittömästi, jos jokin sen komennoista päättyy virheeseen tai jos jokin muuttuja ei ole asetettu. Tämä on yleensä turvallisempaa kuin skriptin suorittaminen eteenpäin epäonnistuneen operaation jälkeen.

  • Skriptejä voi kirjoittaa siis millä tahansa tulkattavalla kielellä, esim. Pythonilla ja monen suosimalla Perlillä. Käytämme tässä demossa bashiä, koska se on melko stabiili ja hyväksi todettu sekä paljon käytetty.

Selaa lämmittelyn vuoksi huvikseen läpi tiedostoa /etc/rc.sysinit Jalavassa. Ei tarvitse ymmärtää kaikkea; katsele vähän kommentteja ja yleiskuvaa, miltä oikea shell-skripti näyttää "tositoimissa". Kyseinen skripti ajetaan kerran siinä vaiheessa kun Jalavan Linux-käyttöjärjestelmä käynnistyy (ydin on silloin jo toiminnassa, mutta skripti ajaa ylös useita muita käyttöjärjestelmän palveluita).

Usein skripteissä ei kannata yrittääkään tehdä kaikkea shellin rajallisilla ohjelmointiominaisuuksilla, vaan kannattaa käyttää apuohjelmia. Täytyy tosin muistaa myös, että kaikkia apuohjelmia ei ole asennettu kaikkialle, ja niiden eri versiot voivat erota toisistaan esim. argumenttien muodon suhteen. Yhteensopivien ja alustariippumattomien skriptien tekeminen vaatii pientä tarkkuutta. Katsotaan kuitenkin tässä yhteydessä kahta kätevää työkalua: find ja grep. Yhdessä nämä ohjelmat tarjoavat samanlaista toiminnallisuutta kuin esim. Windowsin etsintätyökalu. Mm. näitä komentorivityökaluja voi käyttää skripteissä.

Esim: apuohjelma find

Find etsii tiedostoja argumenttina annettujen ehtojen mukaisesti. Se on asennettu useisiin unixeihin. Tässä henkilökohtaiseen tietoturvaan liittyvä esimerkki:

#!/bin/bash -eu
find $HOME -maxdepth 1 -type d ! -name ".*" | \
while read a
do
  chmod go-rwx $a
done

Tämän skriptin find -komento etsii kotihakemistossa ($HOME) olevat hakemistot (-type d), joiden nimi ei ala pisteellä (! -name ".*", jättää siis piilotiedostot huomioimatta), ja syöttää tulosteensa putkella sh-shellin while -toistorakenteelle. Jokaisen findin löytämän hakemiston kohdalla poistetaan hakemistolta kaikki oikeudet kaikilta muilta kuin käyttäjältä itseltään (chmod go-rwx). Find rajoittaa tiedostojen etsimisen kotihakemiston sisältämiin hakemistoihin menemättä alihakemistoihin (-maxdepth 1). Tämä nyt oli vain yksi esimerkki...

Esim: apuohjelma grep

Grep on toinen kätevä, aika usein saatavilla oleva apuohjelma. Sillä etsitään tiedostoista rivejä, joilla on tietynlaista tekstiä. Esimerkiksi seuraava rivi voisi liittyä tekstitiedostona palautetun harjoitustyön tarkastamiseen jollakin kurssilla:

# Tulostetaan opiskelijan tuottamat vastausrivit:
grep -n -B2 -A3 "HARKKA:" $1

Eli grepillä voi etsiä tekstitiedostoista rivejä, joilla on merkkijono. Hyödylliseksi grepin käyttö muuttuu siinä vaiheessa, kun ymmärretään, että sillä voi etsiä täsmällisen merkkijonon sijasta sanarunkoja. Esimerkiksi rivit, joilla on HAR, sitten mitä tahansa merkkejä ja ainakin yksi kaksoispiste:

grep "HAR.*:" fname.txt

Tai itse asiassa mitä tahansa ns. säännöllisten lausekkeiden mukaisia merkkijonovastaavuuksia:

grep "[Hh]a[r]*joitus[t T]*yö[A-Za-zöäåÖÄÅ]*:" fname.txt

Em. komento löytäisi rivit, joilla on esimerkiksi jokin seuraavista merkkijonoista:

Harjoitustyö:
harjoitustyö:
Hajoitustyö:
harrrrrrjoitus yöllä:
Harjoitus työläs:
...

[reunahuomautus: Säännölliset lausekkeet (regular expressions, "RegEx") kannattaa jossain vaiheessa opetella, jos ei vielä ole tulleet vastaan. Niitä käytetään mm. Javan merkkijonoluokkien match -metodeissa ja ylipäätään aika monessa paikassa. Ne muuttuvat vielä tehokkaammiksi siinä vaiheessa, kun oppii käyttämään ns. ryhmiä (groups) joiden avulla voi korvata regexpin osien sisältöjä uudella tekstillä, toisillaan tai vaihtaa niitä keskenään, tai ylipäätään ottaa niitä irti tietorakenteisiin käsittelyä varten. Säännöllisten lausekkeiden teoriaa ja koneiston toteutusta meillä sisältyy kurssiin nimeltä Automaatit ja kieliopit. Niiden käyttötaito kuuluu joka tapauksessa IT-yleissivistykseen.]

Palautustehtävä

Kahden pisteen arvoisena palautustehtävänä voit muokata grafiikkaskriptiä demo3.sh seuraavin tavoin:

  • skriptin pitää tarkistaa, onko komento muotoa ./demo3.sh --removetemps ja siinä tapauksessa poistaa kaikki väliaikaiset tiedostot eikä tehdä mitään muuta.
  • Muutoin skriptin pitää vaatia kaksi argumenttia, joista ensimmäistä käytetään taustakuvan nimenä ja toista tulostiedoston nimenä (alkuperäisessä taustakuva on kovakoodattu nimelle kurssi.jpg ja tulostiedosto nimelle otsikkorantu.jpg). Tämähän on yleensä mukavaa, jos käyttäjä saa valita käsiteltävät tiedostot komentoriviltä.
  • Skriptin pitää tarkistaa, onko kukin väliaikainen tiedosto jo olemassa eikä tehdä sitä uudelleen enää, jos se löytyy. Eli joitain iffejä pitäisi saada kentälle.

Skriptin tulosteena on JPG-muotoinen kuva, jota voi tarkastella esim. Windowsin kuvannäyttö-ohjelmilla, jos tekee mikroluokassa (jos Jalavan kotihakemisto ei näy U-asemana, kyseessä on kuulemma tilapäinen ongelma, johon voimme koettaa miettiä ratkaisua yhdessä THK:n kanssa). Jos tekee kotona, kuvan voi halutessaan siirtää scp-yhteysohjelmalla kotikoneelle tai siirtää jalavassa omaan WWW-hakemistoon (näihin ohjeita pyydettäessä). Kuvan tarkastelu ei kuitenkaan ole sinänsä tarpeen demon tekemiseksi. Näet kyllä esim. ls -l -komennolla että kuva on syntynyt eli että skripti toimii.

Rajauksia:

  • Kovin syvälle tässä ei nyt mennä. Meillä on ajoittain (vai satunnaisin väliajoin?) luennoitava jatkokurssi shell-ohjelmoinnista. Asiaa voi myös opiskella lisää itsenäisesti, kuten mitä tahansa...
  • Ei tarvitse tehdä mitään ihmeellisiä muutoksia mun höperöön esimerkkiskriptiin, eli esim. väliaikaiset tiedostot käsitellään nyt työskentelyhakemistossa. Oikeasti ne pitäisi laittaa /tmp/ -nimiseen hakemistoon (ja ne pitäisi nimetä niin että yhdenaikaisesta suorituksesta ei tule synkronointiongelmaa eli samaa skriptiä pitäisi voida ajaa eri käyttäjien toimesta yhtäaikaa... eli ei pidä joutua vahingossa käyttämään väliaikaisia tiedostoja jaettuna resurssina) ja täytyisi huolehtia, että ne aina poistetaan esim. jos skripti lopetetaan kesken antamalla sille signaali... Nyt ei tarvitse näistä huolehtia... "Oikeissa töissä" kuitenkin sitten tehdään asiat kunnolla, eikö niin... :-)

Tässä siis käytellään hieman Bourne (Again) Shellin perusrakenteita. Ohjausta saa tilaisuuksissa tarvittaessa, ja netti on pullollaan materiaalia skriptiohjelmoinnista. Varaudu tentissä ymmärtämään ja/tai tekemään noin 5-12 rivin mittainen skripti, jossa käytetään mitä tahansa liitteessä esiteltyä ohjelmointirakennetta (liite siis on mukana myös tentissä, joten siitä voi muistella).

Palauta mail-ohjelmalla jalavasta muokkaamasi skripti demo3.sh siten kuin aiemmatkin harkat; sähköpostin otsikoksi tällä kertaa "kolmas vastaukseni". Katso mallia esim. demo1:n ohjeesta; tarkkana postikomennon merkkien kanssa ja varmista, että ohjelma antaa lopussa positiivisen näköisen ilmoituksen.

Liite: Yhden sivun sh-luntti

Alla on tentinkin liitteenä jaettava lunttilappu, jossa esimerkkien kautta muistutetaan joistakin Bourne Shellin piirteistä. HUOM: Tarkoituksella tenttiluntissa ei tulla mainitsemaan sitä, että skriptin ensimmäisellä rivillä pitää kertoa, millä nimenomaisella shellillä se ajetaan... Se pitää kuitenkin vastauksessa olla, oikealla syntaksilla ja siitä paikasta, josta yleensä Bourne Again Shell -ohjelma löytyy eli #!/bin/bash. Siitä tietää, onko aihetta yhtään opeteltu, mietitty ja tehty (tai edes nähty tämä demopaperi tai skripteistä kertonut luento...) vai yritetäänkö vaan sommitella palikoita paikoilleen kylmiltään... Parhaimmillaan laitamme tosiaan argumenttien -eu kanssa (ks. grep-esimerkki yllä) eli ensimmäiseksi riviksi aina #!/bin/bash -eu

Joitakin ohjelmointirakenteita:
    muuttuja=57               # tarkka syntaksista: ei välilyöntejä!
    muuttuja="eki"; muuttuja="$muuttuja$muuttuja"

    if EHTO                   # Myös:  if EHTO; then ...; \
    then                      #        elif EHTO; then ...; fi
      ...                     #
    fi                        #

    for muuttuja in LISTA     # muttujan "esittelyssä" /asetuksessa ei $
    do
      ... jotakin $muuttuja jotakin ... # muuttujan käytössä $muuttuja
    done

    while EHTO; do ... ; ... ; done     # käskyerotin rivinvaihto tai ;

    # Tee jotain syötteen kaikille riveille:
    while read rivi; do echo "luin: $rivi" ; done < rivit.txt

    case $hanen_nimensa in
      kirsimarja)
          echo "Hei Kippe" ;;
      eskomatias)
          echo "Moi E.M." ;;
    esac

    Aliohjelmat mahdollisia&hyödyllisiä, mutta ei käsitellä ITKA203:lla.

Joidenkin ehtojen käyttöä (Välilyönnit merkityksellisiä! "[" on itse
asiassa komento ja loppuosa on sen argumenttilista!):
    [[ -d TIEDOSTONIMI ]]      # tosi, jos on hakemisto
    [[ -f TIEDOSTONIMI ]]      # tosi, jos on tavallinen tiedosto
    [[ ! -f TIEDOSTONIMI ]]    # tosi, jos ei olemassa tai ei tavallinen
    [[ "$muuttuja" -le "57" ]] # tosi, jos muuttuja <= 57 (myös lt,gt,ge)

Argumenttien käyttö:   (yli 9 arg mahd., mutta ei käsitellä ITKA203:lla)
    echo "Tämän skriptin nimi on $0. Eka argumentti $1, neljäs $4"

Joitakin sisäänrakennettuja toimintoja:
    exit VIRHEKOODI              # koodi 0 tarkoittaa onnistumista
    cd                           # vaihtaa skriptin työhakemistoa
    echo                         # kaiuttaa tekstiä
    let muuttuja=$muuttuja+3     # (perusaritmetiikkaa)

Erikoismerkkejä (tuttuja interaktiivisesta käytöstä; toimii skripteissä):
    <   >   >>   |   `KOMENTO`