FLOSS Manuals

 English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Komentorivin perusteet

Skriptien huoltaminen

Skriptien ylläpitäminen

Siirryt vähitellen ohjelmoinnin puolelle shelliskriptauksen kautta. Nyt on paras aika oppia hyvän ohjelmoijan tapoja. Koska tämä kirja on vain johdanto komentoriviin, käsittelemme muutamia tärkeitä vinkkejä, jotka pyörivät ylläpidettävyyden ympärillä. 

Kun ohjelmoijat puhuvat ylläpidettävyydestä, he puhuvat helppoudesta, jolla ohjelmaa voidaan muutella, olipa sitten kyse virheiden korjauksesta, uusien toimintojen lisäämisestä, tai toimivuuden parantamisesta. Heikosti huollettavat ohjelmat on helppo huomata: niistä puuttuu rakenne, joten toiminnallisuus on levinnyt ympäriinsä. Kun painat tästä, se sekoaakin tuolta, mikä on todellinen painajainen. Yleisesti ottaen niitä on todella vaikea lukea. Ajattele vaikkapa tätä: 

#!/bin/sh
identify `find ~/Kuvat/Loma/2008 -name \*.jpg` | cut -d ' ' -f 3 | sort | uniq -c

Käytä suosikkieditoriasi tallentaaksesi tämän tiedoston nimellä foo, sitten:

$ chmod +x foo
$ ./foo
     11 2304x3072
     12 3072x2304

Tämä pieni hirviö etsii tietyn hakemiston tiedostot, jotka loppuvat päätteeseen ".jpg", ajaa komennon identify niille kaikille, ja raportoi sellaista tietoa, joka on jonkun mielestä ollut jossain vaiheessa erittäin hyödyllistä. Jos ohjelmoija olisi vain lisännyt jotain vihjeitä ohjelman suorittamista toimenpiteistä...

Älä käytä pitkiä rivejä

Ensinnäkin huomaat, että esimerkissämme heikosti ylläpidettävä ohjelma on yksi pitkä rivi. Näin ei tarvitse olla. Entäpä, jos ohjelma näyttäisikin tältä:

#!/bin/sh
identify `find ~/Kuvat/Loma/2008 -name \*.jpg` |
cut -d ' ' -f 3 |
sort |
uniq -c

On hieman helpompaa havaita missä kukin komento alkaa ja loppuu. Se on edelleenkin sama kokoelma putkitettuja ohjelmia, mutta ne on esitetty eri tavalla. Voit katkaista pitkät rivit putkien kohdalta, mutta niiden toiminnallisuus on silti sama.

Voit myös jakaa yhden komennon useampaan riviin käyttämällä \ -merkkiä rivin lopussa liittämään sen seuraavaan riviin: 

#!/bin/sh
echo Tämä \
     on \
     oikeasti \
     yksi \
     pitkä \
     komento.

Nimeä skriptisi kuvaavilla nimillä

Toinen asia, jonka voit havaita, on skriptin nimi "foo". Tämä on lyhyt ja kätevä, mutta se ei tarjoa mitään vinkkiä ohjelman käyttötarkoitukseen. Entäpä tämä:

$ mv foo listaa_kuvien_koot

Nyt nimi auttaa käyttäjää ymmärtämään mitä skripti tekee. Paljon parempi, vai mitä?

Käytä muuttujia

Eräs ongelma ohjelmassa on sen tarkemerkkien (`) käyttö. Se toki toimii, mutta siinä on myös ongelmia. Ehkäpä suurin ongelma on se, jota ei ole helppo huomata: muista, että tarkemerkit laittavat sisältämänsä komennon ulostulon siihen kohtaan ohjelmassa, jossa ne ovat. Joissain ohjelmissa on rajoitettu komentorivin pituus. Tässä tapauksessa määritellyssä hakemistossa voi olla paljon kuvia, jolloin komentorivistä voi tulla epätavallisen pitkä. Tämä aiheuttaa omituisen virheviestin, kun ajat ohjelman. On monia tapoja korjata tämä ongelma, mutta tässä voimme kokeilla seuraavaa:

#!/bin/sh
find ~/Kuvat/Loma/2008 -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Nyt find toimii samoin kuin ennen, mutta sen ulostulo, tiedostonimien lista, putkitetaan while-silmukkaan. Tämän silmukan ehto on read imageread on funktio, joka lukee rivin kerrallaan, jakaa sen sisääntulon kenttiin ja laittaa jokaisen kentän muuttujaan, joka on tässä tapauksessa image.  Nyt identify toimii kuva kerrallaan.

Huomaa, kuinka muuttujan lisääminen tekee ohjelmasta helpommin luettavan: se sanoo kirjaimellisesti, että tahdot identifioida kuvan. Huomaa lisäksi, kuinka vaikutus tuleviin ohjelmoijiin ei olisi ollut sama, jos muuttujan nimi olisi ollut vaikkapa ovi tai cdrom.  Nimet ovat tärkeitä!

Mutta tässä ohjelmassa on vieläkin jotain häiritsevää: hakemiston nimi hohtaa kuin kipeä peukalo. Mitä jos muutataisimme ohjelman tällaiseksi:

#!/bin/sh
ALOITUS_HAKEMISTO=~/Kuvat/Loma/2008

find $ALOITUS_HAKEMISTO-name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Tämä on hieman parempi: nyt voit muokata skriptiäsi ja muuttaa hakemistoa joka kerta, kun tahdot käsitellä jonkun toisen.

Käytä parametreja

Loppu ei tuntunut aivan oikealta. Kuitenkaan et muuta komentoa ls aina, kun tahdot listata eri hakemiston sisällön, ethän? Tehdäänpä ohjelmastamme yhtä joustava:

#!/bin/sh
ALOITUS_HAKEMISTO=$1

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Muuttuja $1 on ensimmäinen parametri, jonka annat skriptillesi ($0 on ajamasi skriptin nimi).  Nyt voit kutsua skriptiäsi näin:

$ ./listaa_kuvien_koot ~/Kuvat/Loma/2008

Tai voit listata vuoden 2007 kuvat, jos tahdot: 

$ ./listaa_kuvien_koot ~/Kuvat/Loma/2007

Tiedä mistä aloittaa

Ajattele, mitä tapahtuu, jos ajat skriptin näin:

$ ./listaa_kuvien_koot

Ehkäpä tahdot tämän, mutta ehkäpä et. Tapahtuu niin, että $1 on tyhjä, joten $ALOITUS_HAKEMISTO on myös tyhjä ja ensimmäinen etsittävä parametri on myös tyhjä. Tämä merkitsee, että find etsii nykyisen työhakemistosi. Tahdot ehkä tehdä tästä käytöksestä näkyvää:

#!/bin/sh
if test -n "$1" ; then
    ALOITUS_HAKEMISTO=$1
else
    LOPETUS_HAKEMISTO=.
fi

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Ohjelma toimii täsmälleen samoin kuin ennenkin, erona on vain se, että kun katsot ohjelmaa seuraavan kerran kuukauden päästä, et joudu arvailemaan, miksi se antaa tuloksia, vaikka et anna sille hakemistoa parametrinä.

Katso ensin

Entäpä jos annat skriptille parametrin, mutta parametri ei ole hakemisto, tai sitä ei edes ole olemassa? Kokeile.

Ei kovinkaan kaunista?

Mitäs jos teemme näin:

 

#!/bin/sh
if test -n "$1" ; then
    ALOITUS_HAKEMISTO=$1
else
    ALOITUS_HAKEMISTO=.
fi

if ! test -d $ALOITUS_HAKEMISTO ; then
    exit
fi

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Toimii paremmin. Nyt skripti ei edes yritä toimia, jos sen saama parametri ei ole hakemisto. Se ei ole kovin kohteliasta, se poistuu hiljaisesti ilman aavistustakaan siitä, mikä meni mynkään. 

Valita tarvittaessa

Se korjataan helposti:

#!/bin/sh
if test -n "$1" ; then
    ALOITUS_HAKEMISTO=$1
else
    ALOITUS_HAKEMISTO=.
fi

if ! test -d $ALOITUS_HAKEMISTO; then
    echo \"$ALOITUS_HAKEMISTO\" ei ole hakemisto tai sitä ei ole olemassa. Loppu."
    exit
fi

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Ole varovainen poistuessasi

Ohjelma tuottaa nyt virheviestin, jos et anna sille parametrinä olemassaolevaa hakemistoa, ja poistuu ilman jatkotoimenpiteitä. Olisi mukavaa, jos antaisit muiden ohjelmien, jotka ehkä jatkossa kutsuvat ohjelmaasi, tietää että tuloksena oli virheviesti. Ohjelman olisi siis hyvä poistua virhekoodilla. Tähän malliin:

 

#!/bin/sh
if test -n "$1" ; then
    ALOITUS_HAKEMISTO=$1
else
    ALOITUS_HAKEMISTO=.
fi

if ! test -d $ALOITUS_HAKEMISTO; then
    echo \"$ALOITUS_HAKEMISTO\" ei ole hakemisto tai sitä ei ole olemassa. Loppu.
    exit 1
fi

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

Nyt, jos kohdataan virheviesti, skriptisi poistumiskoodi on 1. Jos ohjelma poistuu normaalisti, poistumiskoodi on 0.

Käytä kommentteja

Mikä tahansa merkkiä # samalla rivillä seuraava jätetään huomiotta, joten voit kommentoida skriptisi toimintaa. Esimerkiksi: 

#!/bin/sh
# Tämä skripti raportoi kaikkien JPEG-tiedostojen koot nykyisessä hakemistossa
# tai parametrinä annetussa hakemistossa, sekä jokaisen koon kuvien lukumäärän.

if test -n "$1" ; then
    ALOITUS_HAKEMISTO =$1
else
    ALOITUS_HAKEMISTO=.
fi

if ! test -d $ALOITUS_HAKEMISTO ; then
    echo \"$ALOITUS_HAKEMISTO\" ei ole hakemisto tai sitä ei ole olemassa. Loppu.
    exit 1
fi

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f 3 |
sort |
uniq -c

 

Kommentit ovat hyviä, mutta älä kirjoita niitä liikaa. Yritä kirjoittaa ohjelma niin, että koodi itsessään on selkeää. Syy tähän on yksinkertainen: ensi vuonna, kun joku muu muuttaa skriptiäsi, tuo toinen henkilö voisi hyvinkin muuttaa komentoja ja unohtaa kommentit, mikä tekee jälkimmäisesti harhaanjohtavaa. Ajattele tätä: 

# laske kolmeen
for n in `seq 1 4` ; do echo $n ; done

Kumpi se on? Kolme vai neljä? Ilmeisesti ohjelma laskee neljään, mutta kommentin mukaan se laskee kolmeen. Voisit ottaa sen kannan, että ohjelma on oikeassa ja kommentti on väärässä. Mutta entäpä, jos ohjelman kirjoittanut henkilö tahtoi laskea kolmeen ja tämä on syy siihen, miksi kommentti on olemassa? Yritetäänpä näin:

# On kolme pikku porsasta
for n in `seq 1 3` ; do echo $n ; done

Kommentti dokumentoi syyn, jonka vuoksi ohjelma laskee kolmeen: se ei kuvaa ohjelman toimintaa, se kuvaa, mitä ohjelman tulisi tehdä. Kokeillaanpa erilaista lähestymistapaa: 

PORSAITA =3
for pig in `seq 1 $PORSAITA` ; do echo $porsas ; done

Sama tulos, hieman erilainen ohjelma. Jos uudelleenmuotoilet ohjelmasi, voit tehdä niin ilman kommentteja (sivuhuomautuksena hieno sana tämän kaltaisille muutoksille, joita olemme tehneet, on uudelleenfaktorointi, mutta se on tämän kirjan laajuuden ulkopuolella). 

Varo taikanumeroita

Nykyisessä ohjelmassamme on taikanumero, numero joka saa ohjelman toimimaan, mutta kukaan ei tiedä miksi sen täytyy olla juuri tuo numero. Se on taikuutta! 

...
cut -d ' ' -f 3 |
...

Sinulla on kaksi vaihtoehtoa: kirjoita kommentti ja dokumentoi miksi sen täytyy olla "3" eikä "2" tai "4", tai lisää muuttuja, joka selittää sen nimellään. Kokeillaanpa jälkimmäistä:

#!/bin/sh
# Tämä skripti raportoi kaikkien nykyisen hakemiston tai parametrinä annetun
# hakemiston alla olevien JPEG-tieodstojen koon ja jokaisen koon kuvien määrän.

if test -n "$1" ; then
    ALOITUS_HAKEMISTO=$1
else
    ALOITUS_HAKEMISTO=.
fi

if ! test -d $ALOITUS_HAKEMISTO ; then
    echo \"$ALOITUS_HAKEMISTO\" ei ole hakemisto tai sitä ei ole olemassa. Loppu.
    exit 1
fi

KUVAN_KOKO=3

find $ALOITUS_HAKEMISTO -name \*.jpg |
while read image ; do identify $image ; done |
cut -d ' ' -f $KUVAN_KOKO |
sort |
uniq -c

Se parantaa noita asioita hieman; ainakin nyt tiedämme mistä 3 tulee. Jos ImageMagick muuttaa joskus ulostulonsa formaattia, voimme päivittää skriptiä vastaavasti.

Toimiko se?

Lopuksi muttei viimeiseksi, tarkasta ajamiesi komentojen poistumisstatus. Nykyisessä esimerkissämme ei ole paljon mahdollisuuksia epäonnistumiseen. Kokeillaanpa vielä viimeistä esimerkkiä:

#!/bin/sh
# Kopioi kaikki HTML- ja kuvatiedostot lähdehakemistosta annettuun kohdehakemistoon.

SRC=$1
DST=$2

if test -z "$SRC" -o -z "$DST" ; then
    cat<

Huomaa, että tässä esimerkissä käytetään monia asioita, jotka olet oppinut tässä kirjassa. Se ei yritä olla täydellinen; voit harjoitella parantelemalla sitä!

Sinun tulisi huomata, että ohjelma kiinnittää huomiota virhetiloihin, joita eri ohjelmat voivat tuottaa. Esimerkiksi se ei vain kutsu komentoa mkdir nähdäkseen toimiko ohjelma, vaan se tekee näin:

if ! mkdir -p "$DST" ; then
    echo Ei voi luoda päämäärätiedostoa \"$DST\".  Stop.
    exit 1
fi

Se kutsuu komentoa mkdir ehtona komennolle if.  Jos mkdir kohtaa virheen, se poistuu statuksella, joka on jotain muuta kuin 0, ja if -ehto tulkitsee sen epätodeksi tilaksi. "!" on negaatio-operaattori, ja muuttaa sen epätoden todeksi (tai päinvastoin). Niinpä rivi sanoo periaatteesa "aja komento mkdir, muuta virhe arvoksi tosi operaattorilla '!', toimi mikäli virhe on olemassa." Lyhyesti sanottuna, mikäli mkdir kohtaa virheen, ohjelmavirta menee if-lauseen vartaloon. Tämä voisi tapahtua esimerkiksi mikäli käyttäjällä, joka ajaa skriptin, ei ole oikeuksia luoda pyydettyä hakemistoa.

Huomaa myös merkkien "&&" käyttö varmistamaan virhetilat:

mkdir -p "$DST/$dir" && cp -a "$filename" "$DST/$filename"

Jos mkdir epäonnistuu, komentoa cp ei kutsuta. Edelleenkin, mikäli joko mkdir tai cp epäonnistuu, poistumisstatus on jotain muuta kuin 0. Tuo ehto tarkastetaan seuraavalla rivillä:  

if test $? -ne 0 ; then

Koska tämä voisi osoittaa jonkin epäonnistuvan hirvittävällä tavalla (esimerkiksi levy on täynnä), meidän on paras luovuttaa ja lopettaa ohjelma.

Yhteenveto

Skriptien kirjoittaminen on taidetta. Sinusta tulee parempi taiteilija tarkastelemalla edeltäjiesi työtä ja tekemällä paljon itse. Toisin sanottuna: lue paljon skriptejä ja kirjoita itse paljon skriptejä. 

Hauskaa hakkerointia!

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.