4.7  Erilaisista komennoista

Termiä ``komento'' käytetään yleisesti kaikista sanoista, jotka shell ymmärtää rivin alussa. Näitä on kuitenkin useampaa tyyppiä, jotka käyttäytyvät hieman eri tavoin. Jotkin komennot myös muistuttavat operaattoreita, joten nämäkin esitellään tässä yhteydessä. (Myöskään ``operaattori'' ei ole mitenkään yksikäsitteinen sana yleisessä kielenkäytössä.)

4.7.1  Operaattorit

POSIX luokittelee operaattoreiksi (control operators) seuraavat:

& && ( ) ; ;; | || sekä rivinvaihto ja EOF.

Näitä ei tarvitse erottaa sanoista tyhjällä eikä välimerkeillä (toisistaan ja muista erikoismerkeistä joskus, jos muuten olisi sekaannuksen vaara).

4.7.2  Varatut sanat

Nämä ovat aina shellin sisäisesti tulkitsemia, niitä ei saa suojata ('if' ei toimi),ja ne toimivat vain tietyissä yhteyksissä. Niitä ei myöskään voi käyttää ulkoisen komennon nimenä (eivät löydy PATHista, joskin eksplisiittisen polun kanssa samannimisen komennon voisi suorittaa), ja mahdollisen uudelleensuuntaus voi toimia tavanomaisesta poikkeavalla tavalla.

POSIXin määrittelemät varatut sanat ovat:

! case do done elif else esac fi for if in then until while { }

Standardi mainitsee myös (ksh:n tuntemat) sanat

function select [[ ]]

sekä kaksoispisteellä alkavat sanat (``unspecified results'').

Varattujen sanojen käsittely eroaa operaattoreista mm. siinä, että ne pitää erottaa yhteydestä tyhjällä tai muulla erotinmerkillä, ja toisaalta varsinaisista komennoista syntaktisten rajoitteidensa takia (erilaisia eri sanoille, esim. in saa esiintyä vain for- ja case-lauseiden yhteydessä).

4.7.3  Erityiset sisäiset komennot

POSIX määrittelee seuraavat komennot erityisiksi (``special built-in commands''): ne ovat aina sisäisiä (shell suorittaa ne itse), mahdollinen syntaksivirhe saattaa aiheuttaa shellin suorituksen keskeytyksen, niillä voi olla erityisiä syntaktisia ominaisuuksia (voivat vaikuttaa muun komentorivin tulkintaan), ja niiden yhteydessä mahdollisesti tehtävä muuttujasijoitus jää voimaan komennon jälkeen:

break continue : . eval exec exit export readonly return set shift trap unset

Näitä ei ole olemassa ulkoisina eikä niitä voi suorittaa execillä (epäsuorastikaan, vrt. find).

4.7.4  Tavalliset sisäiset komennot

Tavalliset sisäiset komennot (``regular built-in utilities'') ovat komentoja, jotka joidenkin erikoispiirteiden takia lähes aina toteutetaan sisäisinä. Esimerkiksi shellin ympäristöön vaikuttavat komennot ovat tällaisia. Niistä pitää kuitenkin olla ulkoinenkin versio, ts. ne pitää pystyä suorittamaan execv():llä. POSIXin luettelee erikseen seuraavat tavalliset sisäiset komennot:

cd false kill true wait command getopts read umask

Näille on määritelty normaalista poikkeava suoritusjärjestys. Mahdolliset muut (toteutuskohtaiset) sisäiset komennot käyttäytyvät siinä suhteessa kuten ulkoiset.

4.7.5  Ulkoiset komennot

Ulkoiset komennot löytyvät erillisinä tiedostoina ja ne voi suorittaa execv():llä jne.

Kaikki edellä luettelemattomat POSIXin määrittelemät komennot ovat ulkoisia, tai niiden pitää toimia kuin ne olisivat ulkoisia ohjelmia vaikka ne olisikin toteutettu shellin sisällä (kuten esim. echo yleensä on, samoin test).

4.8  Komentorivin tulkinnasta

4.8.1  Komentojen erotinmerkit

Komentoja voi yhdistellä erottamalla ne jollakin seuraavista:

; Puolipiste: komennot suoritetaan peräkkäin, jälkimmäinen käynnistyy edellisen loputtua (vrt. newline).
& Komennot suoritetaan yhtaikaa, &:ta edeltävä jää taustaprosessiksi. Eksplisiittisen uudelleensuuntauksen puuttuessa stdin suunnataan /dev/nullista (interaktiivisessa shellissä voi tapahtua muutakin). Jälkimmäinen komento voi puuttua.
| Putki: komennot suoritetaan yhtaikaa, edeltävän komennon tulostus ohjautuu seuraavan syötteeksi.
&&: Ja: komennot suoritetaan peräkkäin mutta jälkimmäinen vain jos edellinen onnistuu (palauttaa statuksen 0).
|| Tai: komennot suoritetaan peräkkäin mutta jälkimmäinen vain jos edellinen epäonnistuu (palauttaa nollasta eroavan statuksen).

Näiden tulkintajärjestys on (1) |, (2) || ja &&, (3) ; ja &.
Huomaa erityisesti että || ja && ovat samanarvoisia:
prog1 || prog2 && prog3
on sama kuin
{ prog1 || prog2 ;} && prog3

Yhdistelmän palauttama status on viimeisen suoritetun komennon status, paitsi & palauttaa aina nollan.

Huom. |, || ja && vaativat kaksi komentoa, esiintyessään rivin lopussa ne jäävät odottamaan seuraavaa riviä ennen suoritusta.

Joukon komentoja voi suorittaa alishellissä kirjoittamalla ne sulkuihin:
(komento...). Sulut kannattaa selvyyden vuoksi erottaa yhteydestään välilyönnein ainakin jos niitä on useita peräkkäin (joissain tilanteissa kahdella peräkkäisellä sululla on eri merkitys). Tällaista alishelliä kohdellaan esim. I/O:n uudelleensuuntauksen kannalta kuten yhtä komentoa; esim.

(cat file1 file2; echo LOPPU) | grep L 

Komentoja voi yhdistellä myös aaltosuluilla {}. Tällöin ne suoritetaan samassa shellissä käynnistämättä uutta prosessia. Huom. aaltosulut eivät ole shellin mielestä erikoismerkkejä tässä yhteydessä vaan varattuja sanoja, niinpä ne täytyy erottaa yhteydestään kuten komennot. Esim.

{ cat file1 file2; echo LOPPU ;} | grep L

4.8.2  Komentorivin käsittelyjärjestys

Shellin pääsilmukka toimii pääpiirteissään näin: Shell

  1. lukee komentorivin (yleensä tiedostosta)
  2. jakaa komentorivin sanoihin ja operaattoreihin
  3. jäsentää rivin joukoksi komentoja ja niiden argumentteja
  4. tulkitsee tekstikorvauksia aiheuttavat erikoismerkit
  5. käsittelee syötön ja tulostuksen uudelleensuuntauksen
  6. etsii ja suorittaa komennon
  7. mahdollisesti odottaa komennon päättymistä

Tätä prosessia tehdään rekursiivisesti siltä osin kuin erikoismerkkien tulkinta sitä edellyttää. Mm. tekstikorvauksen tulos puretaan edelleen, ellei sitä ole suojattu. Esim. vertaa seuraavia:

x=echo
$x kala
"$x" kala
x='echo kala'
$x
"$x"
Tuplalainausmerkkien sisälläkin oleva komentokorvaus ($((...)) tai `...`) aiheuttaa kuitenkin sisällä olevan tekstin purkamisen (rekursiivisesti koko yo. prosessi läpi).

Kun komento on saatu eristetyksi, sitä etsitään seuraavalla tavalla:

Jos komennossa on kauttaviiva, se suoritetaan execve():llä tai vastaavalla tavalla. Jos tämä epäonnistuu, käynnistetään sh argumentteina ko. komentonimi argumentteineen (ts. skriptin pitäisi toimia ilman #!-alkuakin POSIX-yhteensopivissa järjestelmissä).

Jos siinä ei ole kauttaviivoja, yritetään järjestyksessä seuraavia:

  1. Erityiset sisäiset komennot
  2. Funktiot
  3. Kohdassa 4.7.4 luetellut tavalliset sisäiset komennot
  4. Etsitään PATHista. Jos se löytyy, mutta se on toteutettu funktiona tai sisäisenä komentona tämä suoritetaan, muussa tapauksessa se suoritetaan ulkoisena ohjelmana.

(Huom. mahdolliset varatut sanat on käsitelty jo aikaisemmin.)

Niinpä shell voi tietää, että /bin/ls on sen sisäinen komento, mutta sen pitäisi silti löytää /home/tt/ls ilman polkua jos /home/tt on PATHissa ennen kuin /bin. Sen sijaan omatekoinen set tai cd ei löydy vaikka sellainen kuinka olisi PATHissa; cd:n voi kyllä korvata funktiolla, setiä ei edes sillä.

Aliakset tulkitaan ksh:ssa (ja useimmissa muissakin aliaksia käyttävissä shelleissä) ennen mitään sisäisiäkään komentoja, mutta kuitenkin varattujen sanojen jälkeen (jälkimmäisessä on jo eroja, esim. bash sallii aliakset niillekin).

Huom. Lähes kaikki shellit sotkevat tämän tavalla tai toisella. Erityisen yleistä on, että kaikki sisäiset komennot suoritetaan funktioiden jälkeen ennen ulkoisia komentoja PATHista riippumatta. Niinpä minkään yleisen komennon nimen käyttäminen oman skriptin, funktion tai ulkoisen ohjelman nimenä ei ole turvallista ilman eksplisiittistä polkua. Mahdollisuudesta ohittaa erityiset sisäiset komennot funktioilla aiheutuu myös turvallisuusongelma; ksh:ssa se on ratkaistu tekemällä tärkeimmille komennoille automaattisesti aliakset.

Seuraava: 4.9-4.10 Ohjausrakenteet ja aritmetiikka
Edellinen: 4.6 Shellin jokerimerkit


File translated from TEX by TTH, version 1.98.
On 17 May 2001, 18:14.