Unixin yhteydessä puhutaan usein työkaluajattelusta. Tällä tarkoitetaan suurin piirtein sellaista lähestymistapaa, että komentoja ja ohjelmia tarkastellaan työkaluina (tools), joista kullakin hoidetaan yksi tehtävä; kun halutaan tehdä monimutkaisempi asia, ei tehdä sitä varten uutta ohjelmaa, vaan pyritään ensisijaisesti hoitamaan se olemassaolevia työkaluja yhdistelemällä.
Syötön ja tulostuksen uudelleenohjaus ja erityisesti putkitus ovat keskeisiä välineitä työkalujen käytön yhdistämisessä. Putkituksella voidaan ikäänkuin muodostaa liukuhihna, jonka läpi käsiteltävä tietoaineisto kulkee. Kussakin vaiheessa sitä muokkaa jokin työkalu, jota voidaan verrata liukuhihnan äärellä työskentelevään robottiin. Kukin robotti saa raaka-aineensa (syötteensä) liukuhihnan edelliseltä robotilta, ja tuotteet (tulosteet) menevät raaka-aineiksi seuraavalle robotille.
Varsin usein, mutta ei välttämättä, työkalu on filtteri. Unix-komentojen ja -ohjelmien joukossa ei ole mitään erityistä työkalujen luokkaa, vaan lähes kaikkia niistä voidaan käyttää työkaluina - toisia paremmin, toisia huonommin.
Seuraava hyvin yksinkertainen
esimerkki
havainnollistanee työkaluajattelua.
Oletetaan, että meillä on tekstitiedosto puhlu
, joka sisältää
puhelinluettelon. Tiedoston kukin rivi sisältää yhden henkilön
tiedot siten, että tiedot ovat kiinteästi samoissa sarakkeissa
seuraavaan tapaan:
Hiiri, Mikki 4040 555 5555 Ankka, Roope 4300 123 4567 Ankka, Aku 4301 333 3333 ...Tässä sarakkeet 1 - 20 sisältävät nimen ja sarakkeet 21 - 24 sisäisen puhelinnumeron, ja sarakkeesta 25 alkaa kotipuhelinnumero. Oletetaan, että haluamme tuottaa uuden tiedoston, joka sisältää tiedot ilman kotipuhelinnumeroa nimen mukaan aakkosjärjestyksessä. Voisimme tehdä jollain ohjelmointikielellä (esim. C tai Fortran) ohjelman tähän tarkoitukseen, mutta se olisi tarpeettoman työlästä, varsinkin jos tehtävä on pieni ja kertaluonteinen. Ilman ohjelmointitaitoakin tehtävä onnistuu Unixin työkaluilla näin:
colrm 25 < puhlu | sort > puhlu2Tällöin tiedosto
puhlu
ensin annetaan syötteeksi
colrm
-ohjelmalle, jonka argumentilla käsketään sitä
poistamaan joka rivin loppu 25. merkistä alkaen. Tulos putkitetaan
sort
-ohjelmalle, jonka käyttö yleisesti ottaen on
aika hankalaa mutta tällaisessa yksinkertaisimmassa tapauksessa
hyvin helppoa. Tulos ohjataan tiedostoon puhlu2
,
jonka sisällöksi näin muodostuu seuraava:
Ankka, Aku 4301 Ankka, Roope 4300 Hiiri, Mikki 4040Putkituksella muodostetut tiedon käsittelyn "liukuhihnat" voivat olla varsin pitkiäkin; edellisessä esimerkissähän oli vain kaksi "robottia"
colrm
ja sort
.
Seuraavassa on esimerkki putkituksesta, jonka yksityiskohtiin emme
paneudu
mutta joka hyvin kuvastaa työkaluajattelua:
tar -cf foo.tar goo | compress | uuencode foo.tar.Z | mail joeTässä alkuperäinen tietoaineisto (hakemisto
goo
) annetaan
syötteeksi tar
-ohjelmalle, joka paketoi hakemiston
sisällön yhdeksi tiedostoksi. Tulos menee syötteeksi
compress
-ohjelmalle, joka eräällä menetelmällä
tiivistää tiedon pienempään tilaan. Sen tulos taas menee
uuencode
-ohjelmalle, joka muuntaa tiedoston sellaiseen
esitysmuotoon, että se voidaan ongelmitta lähettää meilitse.
Ja tämä muunnettu tulos lopuksi meneekin syötteeksi
mail
-ohjelmalle, joka on yksinkertainen mutta
tällaisissa tapauksissa näppärä meiliohjelma.
Edellä on jo käsitelty lyhyesti muutamia työkaluohjelmia.
Seuraavassa kuvataan muutaman muun yksinkertaisen työkaluohjelman
peruskäyttöä. Tarkempia tietoja
kunkin ohjelman tarjoamista mahdollisuuksista ja niiden käytöstä
saa
man
-komennolla.
Tässä käsiteltävät työkaluohjelmat ovat sellaisia, että niitä
voidaan käyttää filttereinä.
grep-ohjelmalla voidaan poimia tietoaineistosta määrätyt ehdot täyttävät rivit. Yksinkertaisimmassa tapauksessa ehto on sellainen, että rivillä tulee esiintyä jokin määrätty merkkijono, joka annetaan ohjelmalle argumenttina. Esimerkiksi komento
grep Unix <foo
tulostaa (standarditulostusvirtaan) ne ja vain ne foo
-tiedoston
rivit, joilla esiintyy merkkijono Unix
(juuri näin
kirjoitettuna, mutta mahdollisesti sanan osana, esim.
sanassa Unixissa
).
Esimerkki grep
in käytöstä putkituksessa on seuraava,
missä who
-komennon tulostuksesta
(joka voi olla varsin pitkä) poimitaan vain ne rivit, joilla
esiintyy merkkijono dio
:
epsilon ~ 51 % who | grep dio dio ttyb7 Feb 20 20:27 dio ttya5 Feb 20 08:30 dio ttyb5 Feb 20 13:58 dio ttybd Feb 20 22:47 epsilon ~ 52 %Ohjelmalla
grep
on myös "serkut" fgrep
,
joka rajoittuneempi mutta usein tehokkaampi kuin grep
,
ja egrep
, joka on monipuolisempi kuin grep
.
diff-ohjelmalla voi vertailla
tiedostoja ja tulostaa niiden erot (differences). Tiedostojen
nimet annetaan argumentteina.
Jos tiedostojen sisällöt ovat
täsmälleen samat,
diff
ei tulosta mitään. Jos eroja on, niin
ja diff
tarkastelee
ensimmäistä tiedostoa perustiedostona tulostaen tiedot siitä, miten
toinen tiedosto poikkeaa siitä.
Tulostuksen muoto lienee paras selostaa esimerkin avulla:
epsilon ~/tmp 52 % diff tlo.old tlo 19c19 < Yksi pysyvä ongelma on muutosten nopeus. Tänään suositeltava ohjelma on --- > Yksi pysyvä ongelma on muutosten nopeus, sillä tänään suositeltava ohjelma on 82d81 < POP-palvelu 119a119,120 > > 27.2.1996 epsilon ~/tmp 53 %Tässä tapauksessa löytyi tiedostosta
tlo
kolme eroa
tiedostoon tlo.old
verrattuna:
19c19
kertoo muutoksesta (change)
rivillä, tarkemmin sanoen, että
ensimmäisen tiedoston 19. rivi poikkeaa toisen tiedoston
vastaavasta rivistä. Kummatkin rivit näkyvät tulosteessa
siten, että ensimmäisen edessä on <-merkki ja toisen
edessä >-merkki.
82d81
kertoo, että ensimmäisestä
tiedostosta on poistettu (delete) 82. rivi; kyseinen rivi
näkyy tulosteessa.
119a119,120
kertoo, että ensimmäisen
tiedoston 119. rivin perään on lisätty (add) kaksi riviä; rivit
näkyvät tulosteessa.
-c
, joka aiheuttaa sen,
että tulostus on pitempi siten, että eroavuuksien yhteyteen tulostuu
tiedostojen sisältöä laajemmin; silloin ehkä näkee helpommin
muutettujen tekstien asiayhteyden (context).
tr-ohjelma tekee tietoaineistolle yksinkertaisen merkkimuunnoksen eli -translaation, jossa annetut merkit korvataan toisilla. Esimerkiksi komento
tr '{|}[\]' 'äöåÄÖÅ'kopioi standardisyöttövirran standarditulostusvirtaan siten, että merkki
{
korvautuu merkillä ä
,
merkki |
merkillä ö
jne.
Jos tällainen muunnos halutaan tehdä tiedostolle, toimitaan
seuraavaan tapaan (koska tr
on filtteri):
tr '{|}[\]' 'äöåÄÖÅ' <muistio.asc >muistio.iso
Kun halutaan tehdä asioita, joita ei voi suoraan hoitaa yhdellä komennolla tai valmiilla ohjelmalla, voidaan valita jokin seuraavista toimintatavoista:
Putkitusta käsiteltiin juuri edellä ja käännettäviä ohjelmia aiemmin. Komentotiedostoista tulee puhetta myöhemmin. Seuraavassa kuvaillaan hiukan tulkittavia kieliä.
Valinta edellä mainittujen vaihtoehtojen välillä riippuu monista asioista kuten tehtävän mutkikkuudesta ja siitä, mitä välineitä käyttäjä osaa käyttää. Usein tilanne on se, että tehtävä hoituisi nopeimmin jollain sellaisella välineellä, jota käyttäjä ei tunne. Tällöin on tietysti otettava huomioon, että uuden välineen opetteluun voi kulua paljonkin aikaa - ja toisaalta siitä voi olla paljon hyötyä myöhemmin.
Tehokkuusseikat kannattaa myös ottaa huomioon. Kertaluonteisiin hommiin sopii yleensä hyvin työkaluista tehty putki tai vaativammissa tapauksissa komentotiedosto tai pienen kielen käyttö. Jos taas on kehitettävä tapa tehdä homma, joka joudutaan tekemään usein ja joka kuluttaa paljon tietokoneen aikaa, voi ohjelmointikielen käyttö olla hyvin perusteltua. Tällöin on olennaista, että ohjelma käännetään konekoodiksi, jota tietokone suoraan toteuttaa; sen sijaan pieniä kieliä käytettäessä suoritus on tulkitsevaa, jolloin tietokoneajan kulutus voi olla monikymmenkertainen.
Pienet kielet muistuttavat monessa suhteessa varsinaisia ohjelmointikieliä - niissä on yleensä mm. ehto- ja toistorakenteita - mutta toteutus on tulkitseva. Pienellä kielellä kirjoitettu ohjelma voidaan joskus kirjoittaa suoraan komentoriville (ns. one-liner, yhden rivin mittainen ohjelma), mutta tavallisempaa on, että se kirjoitetaan tiedostoon. Seuraavassa kuvaillaan lyhyesti muutamia pieniä kieliä luonnehtien niiden yleisiä ominaisuuksia ja esittäen pikku esimerkkejä. Pienien kielien oppimisessa on oma vaivansa, joten kannattaa harkita, mihin niistä perehtyy, sen mukaan, miten ne tuntuvat sopivan omiin tarpeisiin.
Sed (stream editor) on Unixin alkuperäisestä
rivieditorista (Ed) kehitetty ohjelma, joka on monessa suhteessa
lähempänä työkaluohjelmaa kuin pientä kieltä - näiden käsitteiden
välillä ei ole tarkkaa rajaa.
Sedillä voidaan muokata tiedostoa esimerkiksi korvaamalla
merkkijonoja toisilla. Samanlaisia asioita voidaan tehdä editoreillakin,
mutta editorin käyttö on vuorovaikutteista, Sedin käyttö taas sellaista,
jossa käyttäjä vain kuvaa halutun muunnoksen ja käskee Sedin tehdä sen.
Tämän takia Sed sopii käytettäväksi työkaluajattelun mukaisessa
putkituksessa. Seuraavassa esimerkissä tehdään muunnos,
jossa tiedostossa olevat merkkijonot Atk
ja atk
korvataan merkkijonolla ATK
:
sed -e 's/[Aa]tk/ATK/g' <ohje >ohje.uusiTässä esimerkissä valitsin
-e
kertoo, että sen perässä,
suoraan komentorivillä, tulee Sedille annettava käsky; käskyt voitaisiin
kirjoittaa myös tiedostoon. Käsky on s
-käsky, joka
määrää suorittamaan korvauksen (substitution), ja sen lopussa
oleva g
määrää tekemään korvauksen kaikkialle,
yleisesti (generally). Rakenne [Aa]
on alkeellinen
esimerkki Sedin tunnistamista säännöllisistä lausekkeista
(regular expressions): se tarkoittaa kumpaa tahansa merkeistä
A
ja a
, joten rakenne
[Aa]tk
tarkoittaa siis
merkkijonoja Atk
ja atk
.
Awk on monipuolisempi väline, johon liittyy oma pieni kielensä, joka muistuttaa C-ohjelmointikieltä. Awk-kielellä käsitellään tietoaineistoa riveittäin ja määritellään malleja (patterns) ja niitä vastaavia toimenpiteitä. Tämä esitetään Awk-ohjelmassa seuraavassa muodossa:
malli { toimenpide }
Täten voidaan erilaisille, siis eri malleja vastaaville, riveille määritellä erilaisia muunnoksia tai muita toimenpiteitä. Oletustoimenpiteenä on (mallia vastaavan) rivin kopiointi standarditulostusvirtaan. Tämän ansiosta voidaan asiat usein esittää hyvinkin tiiviisti; esimerkiksi
awk 'length > 72' tiedtulostaa standarditulostusvirtaan ne ja vain ne
tied
-tiedoston rivit, joiden pituus on yli
72 merkkiä. Tässä length > 72
kuvaa mallin,
ja sitä vastaavat rivit tulostuvat, muut eivät; tätä mallia
vartenhan ei ole erikseen määritelty toimenpidettä, joten
Awk oletusarvoisesti suorittaa kopioinnin, ja ne rivit, joiden
pituus on enintään 72 merkkiä, eivät vastaa mitään mallia
(muita mallejahan ei tässä tapauksessa) ole, joten niiden
osalta Awk ei tee yhtään mitään.
Seuraavassa on hiukan pidempi esimerkki:
tiedostossa isot
on Awk-ohjelma
BEGIN { sum = 0; print "Isot tiedostot:" } $5 > 10000 { sum += $5; print } END { print "Isoissa tiedostoissa yhteensä " sum " tavua" }ja nyt käyttäjä voi katsoa, mitä isoja (tässä tapauksessa: yli 10000 tavua pitkiä) tiedostoja hänellä on työhakemistossaan, seuraavasti:
epsilon ~/tmp 51 % ls -l | awk -f isot Isot tiedostot: -rw-r--r-- 1 jkorpela staff 52876 Jan 24 14:45 btxdoc.dvi -rw-r--r-- 1 jkorpela staff 41569 Jan 24 14:44 btxdoc.tex -rw-r--r-- 1 jkorpela staff 10990 Jan 18 12:46 pctex Isoissa tiedostoissa yhteensä 105435 tavua epsilon ~/tmp 52 %Tässä esimerkissä siis Awk-ohjelma on erillisessä tiedostossa, jonka nimi ilmoitetaan
-f
-valitsimella.
Syötteenä Awk saa tässä komennon ls -l
tulostuksen,
ja se poimii siitä ne rivit, joissa 5. kenttä eli tiedoston koko
tavuina on yli 10000. Poiminta hoidetaan mallilla
$5 > 10000
. Awk-ohjelmassa on myös "mallit"
BEGIN
ja END
, joihin liitetyt toimenpiteet
Awk suorittaa vastaavasti tietoaineiston käsittelyn alussa ja lopussa.
Perl on erittäin ilmaisuvoimainen kieli, jonka monet piirteet on otettu C-ohjelmointikielestä. Sitä ei oikein voi lukea "pieniin kieliin", mutta sitä voi käyttää niiden tapaan, hyvinkin pienten työkalujen tekemiseen. Perlin "filosofia" on aika erikoinen ja vaatii totuttelua; yleensäkin kielten opettelussa alkuvaihe on hankala mutta sen jälkeen lisäoppiminen on aika helppoa. Perlistä on olemassa erillinen ohje Getting started with Perl ja myös suomenkielistä aineistoa. Seuraavassa on vain pieni esimerkki Perlin tarjoamista mahdollisuuksista:
#!/usr/bin/perl while(<*.for>) { $oldname = $_; s/\.for$/\.f/; rename $oldname, $_; }Tämä Perl-ohjelma muuttaa kaikkien työhakemistossa olevat
.for
-loppuiset
tiedostonnimet vastaaviksi
.f
-loppuisiksi.
Tällaista uudelleennimeämistä ei Unixissa voi tehdä millään
suoralla tavalla, esim. mv
-komennolla!
Ohjelman ensimmäinen rivi kertoo, että kyseessä on Perl-ohjelma.
Toinen rivi määrää ohjelman käymään läpi kaikki
.for
-loppuiset tiedostonnimet, ja tämän silmukan
sisällä sitten korvataan nimen loppuosa .for
merkkijonolla .f
ja suoritetaan uudelleennimeäminen
tiedosto kerrallaan Perl-käskyllä rename
, joka
olennaisesti vastaa Unixin mv
-komentoa.