Ubuntun käyttö > Ohjelmointi, palvelimet ja muu edistyneempi käyttö
Skriptiajuri
<< < (17/18) > >>
petteriIII:
Harrastuksena ja mieleisenä ajankuluna BASH alussa oli - mutta olin siinä luulossa että BASH on hidas ja kyvytön. Mutta enpä tiedä enää - alkaa tuntua siltä että kaiken saa toimmaan kymmeniä kertoja nopeammin kuin ennen  - ja paljon sellaista saa toimimaan jonka ei pitäisi toimia ollenkaan.

Sehän tuli kerrottua jo aikaisemminkin että yksi virtuoosi aikoinaan sanoi että jos todellista nopeutta etsii niin awk:it, sed:it ja semmoiset saa unohtaa. Silloin pidin sitä melkoisena liioitteluna mutta virtuoosi taisikin humalapäissään kertoa salaisuuksia ?

---

Miksi BASH:ista esittään kaikkialla niin vähän esimerkkejä? Ja miksi kaikki lähetyvät ongelmia samalta suunnalta vaikka BASH on niin laaja että kaikkeen on lukemattomia lähestymistapoja? Johtuuko se siitä ettei ohjeita tehdä vaan ne kopioidaan eikä niitä ohjeitakaan päivitetä koskaan - tosin joillain harvoilla verkkosivuilla mainitaan puolihuolimattomasti että 'tämmöistäkin uutta surkeutta löytyy'. BASH:ia tosiaan kehittää vain yksi ukko - ansiokkaasti kehittääkin ja varmaankin apujoukon kera - mutta mieleen tulee ettei hänkään halua sohia ampiaispesää tarpeettomasti.

Esimerkiksi 1+1 voidaan laskea ainakin kahdella yleisessä käytössä olevalla tavalla: ensinnäkin se normaali 'echo $((1+1))', se toinen tapa on : + 1 1  .
- mattematiikamoottori on kummassakin sama - tuo: $(($1+$2))
- kummessakin laskutavassa apuna täytyy olla funktio: siinä normaalissa on BASH:in sisäinen funktio nimeltään echo ja toisessa itsetehtävä funktio nimeltään +   .

Funktion nimeksi voidaankin määrätä melkein mitähyvänsä, esimerkiksi:

--- Koodia: ---function + () { echo $(($1+$2)) ;}

--- Koodi päättyy ---
- laskussa $(($1+$2)) + on matemaattinen merkki eikä funktiokutsu joten kyseessä ei ole rekursio (eli funktio ei kutsu itseään)
- kun annetaan käsky: + 1 2  niin se tulostaa 3 - mikäli löytyy tuo funktio nimeltään: +  .

Nimeltään tuo juohea laskutapa '+ 1 2' on käänteinen Puolalainen ja koska se vaatii BASH:issa käyttäjää tekemään itse tarvittavat funktiot niin käänteistä Puolalaista kannattaa käyttää vain kun matematiikkaa on tosipaljon. Ei tuota käänteistä Puolalaista tosin enää juuri koskaan käytetä.
- esimerkiksi laskuohjelman bc isä nimeltään dc oli käänteistä Puolalaista - se on koneelle helpompaa mutta ihmiselle niin vaikeaa että sille tehtiin kuori-ohjelma nimeltään bc joka muuttaa ihmisen ajatukset koneelle sopivammaksi käänteiseksi Puolalaiseksi. Onkohan käänteinen Puolalainen parempikin matikassa koska noin täytyi toimia?
petteriIII:

Yksi menetelmä etsiä tekstistä jotakin on muodostaa tuosta etsittävästä muotti ja ajaa teksti muotista läpi jolloin kaikki muotin mukainen tarttuu muottiin. Käytännössä toimitaan seuraavasti:

- etsittävän rakennetta kuvataan oliolla nimeltä regex: regex on muotti jonka määräysten mukaan etsittävän tyyppiset muuttujat on koottu - esimerkiksi päivämäärän muotti on: [0-9]+:[0-9]+:[0-9]+
- tässä paikassa merkki + merkitsee: yksi tai useampi samanlainen kuin on edessäkin.
- monille merkeille tulkki antaa asiayhteydestä riippuvan  erikoismerkityksen - useimmilla merkeillä on aina sama merkitys mutta esimerkiksi merkillä: * niitä on monia. Jos ei haluta tulkin antavan merkille erikoismerkitystä kirjoitetaan sen eteen kenoviiva - siis esimerkiksi: \*
- kun tekstin jokaista sanaa sovitellaan tuohon muottiin niin kun muotti sopii niin kyse on päivämäärästä.
- regex:ää on totuttu käyttämään jonkun apu-ohjelman avulla - esimerkiksi grep:in avulla ja kyllähän sillä omat etunsa onkin, mutta BASH kykenee useisiin regex:iin itsekin ja silloin kun BASH kykenee niin grep jää surkeasti kakkoseksi.
- myös tuo regex-maailma on valtavan suuri - katsopa verkkosivua: https://www.regexbuddy.com/. Ja samatyyppisiä sivuja on monia muitakin.

haku kokonaisuudessaan:

--- Koodia: ---teksti="höpinää 17:06:23 lisää höpinää"; regex="[0-9]+:[0-9]+:[0-9]+"
[[ "$teksti" =~ $regex ]] && echo "tekstissä on päiväys:$BASH_REMATCH"

--- Koodi päättyy ---
- tuosta regexään:n sopivasta muodostetaan aina BASH:issa muuttuja $BASH_REMATCH.

---

Mutta päiväys voidaan jakaa osiin: tuntiin, minuuttiin ja sekuntiin - ja mikäli regex on jakamista silmäälläpitäen muodostettu niin kukin näistä osista talletetaan omaan BASH_REMATCH[numero] nimiseen muuttujaan. BASH_REMATCH:ia selvitetään kaikkialla juurta jaksain - ja ehkäpä joku saa niistä selityksistä selvääkin. Mutta esimerkkejä ei esitetä; siis sellaista yksinkertaista skriptiä jossa tulostetaan BASH_REMATCH, BASH_REMATCH[1], BASH_REMATCH[2], BASH_REMATCH[3] ... jotta asiasta tulisi rautalankamalli jota ei voi käsittää väärin - tai oikeastaan että asian voisi nopeasti edes käsittää. Koetetaanpa nyt esitää asia ymmärrettävästi:

- BASH_REMATCH:ia käytettäessä regex:ät on jaettu suluilla osiin ja jokainen sulkupari muodostaa uuden muuttujan nimeltään:  BASH_REMATCH[1], BASH_REMATCH[2], BASH_REMATCH[3] ... ja niiden kokonaisuuden nimeltä: BASH_REMATCH. Esimerkiksi päivämäärää kuvaava regex jaetaan:

--- Koodia: ---([0-9]+):([0-9]+):([0-9]+)   

--- Koodi päättyy ---

Esimerkki:

--- Koodia: ---teksti="höpinää 17:06:23 lisää höpinää"; regex="([0-9]+):([0-9]+):([0-9]+)"   
[[ "$teksti" =~ $regex ]] && echo "tekstissä seuraava palanen täytti koko regex:än: $BASH_REMATCH - ja sen osat: tunti:${BASH_REMATCH[1]} minuutti:${BASH_REMATCH[2]} sekunti:${BASH_REMATCH[3]}"

--- Koodi päättyy ---
- mikäli koko regex ei täyty niin ei tulosteta mitään - elikä 2013:06 ei tulosta mitään.
- siis jokaisesta regex:än (....)-kentästä muodostuu uusi BASH_REMATCH[numero] - ja numerointi alkaa ykkösestä.
- muuten teksti voi olla matriisikin:
--- Koodia: ---${teksti[@]}   
--- Koodi päättyy ---
- silloin tosin saadaan vain ensimmäinen joka sopii.
- kieliasetukset vaativat skriptiin omat muutoksensa - näillä määrityksillä tämä toimii Suomessa.
 
toinen esimerkki:

--- Koodia: ---[[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]] && {
      echo "- kolmeriviä selitystä: REMATCH:it tehdäån kohdassa:[[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]]."
      echo "  Regex muodostuu useasta regex:stä. Tässä esimerkissä on kolme regex:ää ympäröity kaarisuluilla - huomio että"
      echo "  [0-9] on kaksikin kertaa BASH_REMATCH:in ulkopuolella vaikka kuuluvatkin regex:ään - siis valintajoukkoon ne kuuluvat mutta eivät BASH_REMATCH:iin."
      echo "BASH_REMATCH on koko regex alue= "$BASH_REMATCH
      echo "BASH_REMATCH[1] (elikä regex:än ensimmäisessä ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[1]}"
      echo "BASH_REMATCH[2] (elikä regex:än toisessa      ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[2]}"
      echo "BASH_REMATCH[3] (elikä regex:än kolmannessa   ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[3]}" ;}

--- Koodi päättyy ---


kolmas esimerkki, kenttien kääntäminen - vaikkapa Suomalaisen päiväyksen muuttaminen jonkun muun maan esitystapaan sopivaksi:

--- Koodia: ---[[ 1.2.2013 =~ ([0-9]+).([0-9]+).([0-9]+) ]] && echo ${BASH_REMATCH[3]}.${BASH_REMATCH[2]}.${BASH_REMATCH[1]}

--- Koodi päättyy ---
- kyse on nopeudesta. Työ olis helpompaa awk:lla tai semmoisilla mutta myös hitaanpaa. Ero on on millisekunneissa mitättömän pieni eikä muiden hitauteen edes vihjata missään - mutta itseasiassa nopeusero on yli kymmenkertainen. 

---

Esimerkiksi matriisien tarkistaminen regex:ällä senvaralta että siinä olisi kirjaimia:

--- Koodia: ---function tarkista_matriisi () { [[ $@ =~ [[:alpha:]] ]] || (( $BASH_REMATCH )) && echo matriisissa ensimmäinen vastaantuleva kirjain:$BASH_REMATCH || echo "matriisisssa ei ole kirjaimia";}; matriisi=({1..100000}); matriisi[5000]=7A7e; time tarkista_matriisi ${matriisi[@]}

--- Koodi päättyy ---

- tuo [[:alpha:]] on tässä se regex. Regex:t voivat olla tällaisia pieniäkin mutta esimerkiksi IP6-verkko-osoitteen tarkistamiseen sopiva regex on monen rivin hirviö - mutta silti erittäin nopea.
petteriIII:
Funktioiden kuvaukseen tulee usein uusia huomioita joten funktioiden ohjeistus taitaa olla viisainta kirjoittaa silloin-tällöin uusiksi:
- esimerkiksi nimiparametrit ovat toimneet jo vuosia - ja ne myös poistivat tarpeen parametrien palauttamiseen. Mutta uudet käskyt muuttivat nimiparamtrien käyttämisen aina samaksi - kun siinä oli aikaisemmin paljonkin taiteilua.

Rajoitetaan sana funktio käsittämään vain niitä koodinpätkiä joiden edessä on sana function:

--- Koodia: ---function funktion_nimi () { tässä on ihan normaali skripti ;}

--- Koodi päättyy ---
- tulkki ei vaadi sanaa function kirjoitettavaksi sillä tulkki käsittää ilmankin mistä on kyse mutta ihmisten takia on viisainta kirjoittaa sana: function.
- se normaali skripti voi olla kirjoitettu yhteen tai useampaan riviin.
- kun skriptin koodissa on missähyvänsä sana joka on sama kuin jonkun funktion nimi niin mikäli tulkki ei keksi sille sanalle mitään muuta tehtävää niin silloin se on funktiokutsu. Tulkki vaihtaa kutsun ja sen perässäolevien muuttujien paikalle funktion koodin ja toimittaa muuttujat tähän siirtämäänsä funktioon. Muuttamatta pilkkuakaan kutsun kummallakaan puolella. Muuttujia ei tarvitse olla yhtään tai niitä voi olla vaikka kuinkamonta. Muuttujan paikalla voi olla myös vakio.
- kun päätteessä määritellään funktio niin jää se päätteen muistiin niin pitkäksi aikaa kun päätettä ei sammuteta. Mutta sammuttamisen yhteydessä kaikki katoaa peruuttamattomasti.
- funktion tarkoituksena on käsitellä muuttujia jotka funktioon on siirretty - siirrettyjä muuttujia kutsutaan parametreiksi.
- muttujasta voidaan siirtää sen arvot tai nimi - puhutaan arvoparametreista ja nimiparametreista. Pahantahtoiset ihmiset ovat kehittäneet tarun ettei BASH nimiparametreja tunne mutta se on legendaa - tosin BASH:issa nimiparametrit täytyy käsitellä toisellatavalla kuin muissa kielissä.
- arvoparametreja käytettässä voidaan siirtää vain yksi matriisi ja sen täytyy olla viimeisenä. Nimipaametreja käytettäessä nimisssä saa olla vaikka kuinkamonta matriisia eikä nimien sijaintipikallakaan ole väliä.
- sen merkiksi että parametreja saattaa funktioon tulla kirjoitetaan funktiomäärittelyyn () - näiden sulkujen väliin ei koskaan kirjoiteta mitään. Nuo sulut kannattaa kirjoittaa vaikka funktioon ei siirrettäisi yhtään parametria.
- funktiossa siihen siirretyillä parametreilla ei ole nimeä vaan vain siirtonumero - siis $1, $2 ...$n.
- $0 on funktion nimi.
- parametrien numerointi alkaa aina ykkösestä ja kasvaa parametrien välillä aina yhdellä. Toisaalta vaikka oisi siirretty muuttujanimi ei siitäkään funktiossa puhuta nimenä vaan numerona - ihmiselle vähän sotkuinen homma mutta koneelle ilmiselvä.
- parametri voi olla myös laskukaava tai funktiokutsu (= jonkun funktion nimi - myös funktion oma nimi kelpaa) - harvoin jotain muuta vaikka mahdollista se on.
- jokainen funktio vo kutsua nollaa tai useampaa ali-funktiota. Matemaattisilla funktioilla on yleensä useampia muiden funktioiden kanssa yhteisiä ali-funktioita mutta tekstinkäsittely tehtävillä usein ei ole yhtään - tosiaan paremmin pärjää kun ei jaa toimintoa funktioihin.
- tulkille on ihan selvää mikä kaikki funktiokutsun perässäoleva tauhka kuuluu parametreihin mutta tottumattomalle ihmiselle se ei ole aina selvää.
- parametrien lukumärää ei kirjoiteta mihinkään sillä tulkki haluaa laskea parametrien lukumäärän itse. Parametreja ei tarvitse olla välttämättä yhtään ja toisaalta niitä voi olla miljoona.
- nimiparametreja käytettäessä matriisejakin voi olla parametrien seassa useampia ja ne voivat sijaita misähyvänsä parametrien luettelossa - arvoparametreillahan matriiseja voi sirtää vain yhden ja sen tulee sijaita viimeisenä prametriluettelosa.
- funktioiden kutsuessa toisia funktiota täytyy kutsuista ylläpitää kutsupinoa - jotta virheen sattuessa tiedettäisiin mitä reittiä virheeseen on päädytty ja mikäli virhettä ei ole tapahtunut niin tiedettäisiin mihin pitää mistäkin palata - pinot toimivat automaattisesti eikä niihin käyttäjän tarvitse puuttua.

Funktiota ktsuttaessa BASH ei siirrä toimintaa funktioon vaan hakee funktion sinne missä toimitaan - siksi parametrien palauttaminenkin on turhaa.

Funktioiden pitää olla kirjastossa eikä sotkemassa skriptiään - kirjastojen olemassaolo onkin yksi BASH-kielen peruspilareista. Mutta koska BASH:ista halutaaan eroon niin sen kirjasto-osoitin poistettiin mikä tekee kirjastojen käyttämiestä hankalaa. Samalla alettiin mustamaalata funktioiden käyttöä. Mutta ennen kirjasto-osoittimen poistamista kerkisi nettiinkin tulla muutamia kirjastoja - siellä ne nyt kituvat unohdettuina.

Matikka kärsii kirjastojen keinotekoisesta puutteesta paljon mutta tekstinkäsittely vain vähän - ja matematiikkahan haluttiin estää kokonaan sillä sehän on valmisohjelmien reviiriä.

Tekstinkäsittely kärsii sekin vähän. Ja tekstinkäsittelyä opetetaan suorittamaan väärillä komennoilla: se hidastaa toimintaa ja aiheuttaa runsaasti epämääräisyyksiä. Lisäksi sitä vähättellään mahdottomasti mutta esimerkiksi BASH:in hakufunktiot toimivat aivan toisessa ulottuvuudessa kuin google tai mikään muukaan. Samoin oman koneen hoitamisen mahdollisuus on ensiluokkainen. Verkkotoiminnotkin ovat hyvät. Ainoa ongelma on se että kaikki on tehtävä itse, valmiita ohjelma-kokonaisuuksia on runsaastikin mutta niitä vartioidaan mustasukkaisesti - vallitseva mielipide tuntuu olevan: koko Linux saa mieluummin mennä turmioon kuin toiset käyttää töitäni. Ja nyt kaikki on hapantunut pilalle eivätkä loistavien töiden tekijät enää kehtaa töitään esittääkään.
petteriIII:
BASh ei toimi niin että se palauttaisi funktioista jotakin. Tätä virtuoosit jaksavat jauhaa ikäänkuin se olisi jotenkin huono asia - mutta hölmöä puhetta se on sillä arvoparametreja ei edes voi palauttaa vaan se on yksinomaan nimiparametreille mahdollista. Ja virtuoosit eivät tunnusta BASH:in nimiparametreja tuntevankaan.

Mutta BASH:issa nimiparametritkaan eivät palauta mitään - valitettavasti niitä ei voi yksinkertaisin keinoin tulostaakaan vaan niillä voi ainoastaan muuttaa muuttujia (siis BASH:in funktiot ovat WriteOnlyMemory - muuttujien arvot voi tulostaa vasta pääohjelmassa). Sentakia BASH:issa tulostusta on tarkoitus tehdä yksinomaan pääohjelmassa ja muuttujien muutokset suoritetaan funktioissa.

Kerrottuna toisella tavalla: parametrien palauttaminen ei ole päämäärä. Päämäärä on se että samaa funktiota voitaisiin kutsua mistävaan, minkänimisillä_ja_tyyppisillä muuttujilla vaan ja funktio muuttaisi oikeat muuttujat. Mikäli muutos voidaan tehdä palauttamatta parametreja niin sehän on loistavaa. Mutta ei suinkaan ole sattumaa että BASH toimii näin - toiminto on aikaansaatu monien miestyövuosien tuloksena - ja sitten toiset heitivät toiminnon roskakoriin - hölmöyttään vai roistomaisuuttaan? Jokatapauksessa toiminta on seuraavanlaista:

Numeromuuttujan kanssa:

--- Koodia: ---function koe () { read<<<$(($1*99)) $1;}; muuttuja=2; koe muuttuja; echo $muuttuja

--- Koodi päättyy ---
- matikkamoottori siis hallitsee nimiparametrinkin tulostuksen:

--- Koodia: ---function koe () { echo $(($1+0)) ;}; muuttuja=2; koe muuttuja

--- Koodi päättyy ---

Tekstijono-muuttujan kanssa:

--- Koodia: ---function koe () { read<<<"kova koitos" $1 ;}; muuttuja=alkutilanne; koe muuttuja; echo $muuttuja

--- Koodi päättyy ---
Numeromuuttujista kootun matriisin kanssa:

--- Koodia: ---function koe () { read<<<$(($1[3]*2)) $1[2];}; matrix=(1 2 3 4 5 6 7 8 9); koe matrix; echo ${matrix[@]}

--- Koodi päättyy ---
Tekstijono-muuttujista kootun matriisin kanssa:

--- Koodia: ---function koe () { read<<<$3 $1[$2] ;}; matrix=(yksi kaksi kolme neljä); koe matrix 2 loisto-uutinen; echo ${matrix[@]}

--- Koodi päättyy ---

---

numero-muuttujan ja tekstijono-muuttujan raja täsäkin työssä yhtä epämääräinen kuin muutenkin - ja voidaan kirjoittaa:

--- Koodia: ---function kerro9 () {
luku1=$1
luku2=$2
int1=${luku1%%.*};
int2=${luku2%%.*};
apu=$(($int1*$int2))
kokonaisia=${#apu}
tulos=$((${luku1//./}*${luku2//./}))
echo ${tulos:0:$kokonaisia}.${tulos:$kokonaisia} ;}

function koe () { read<<<$(kerro9 $1 11.22) $1;}; muuttuja=2; time koe muuttuja; echo $muuttuja

--- Koodi päättyy ---
petteriIII:
Kuinka nimi-parametrin saa toimitettua funktioon ja palautettua funktiosta muutettuna:

- vaikka itseasiassa BASH ei palauta mitään: kaikki kielet pitävät kirjaa muuttujistaan. Muut kielet osaavat päivittää kirjanpitonsa vain pääohjelmassa joten niiden tarvitsee palauttaa parametrinsa funktiosta - mutta BASH osaa päivittää kirjanpitonsa jo funktiossa joten sen ei täydy parametreja palauttaa.
- aluksi esitetään vain lopputulos selittämättä vielä mitään.
- yksinkertaisuuden vuoksi parametreja on tässä vain yksi matriisi.

--- Koodia: ---function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; apu2[2]=""oh hoh""; unset $1; read<<<${apu2[@]} $1;}; matrix=(1 2 3 4 5 6 7 8 9); koe matrix; echo ${matrix[@]}

--- Koodi päättyy ---
- vastaava funktio on kaikissa kielissä pitkä hirviö - muut kielet vain saavat inhottavuudet kätkettyä kirjastoihin mutta BASH:ilta se on kielletty. Nopeuteen se ei kumminkaan vaikuta - vaikka eihän BASH ole muutenkaan kovin nopea. 
- tässä funktiossa on hyvin paljon vakio-osia: vain käsittely täytyy muuttaa sellaiseksi kuin käsiteltävä vaatii - tässä sillä paikalla on: apu2[2]="ohhoh". Kaikki muu kopioidaan. Siis esimerkiksi tavallisella muuttujalla funktio on ihan samanlainen lukuunottamatta muuttujan käsittelyä:

--- Koodia: ---
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; apu2="oikea arvo on: "$(($apu2*2)); unset $1; read<<<${apu2[@]} $1;}; muuttuja=57; koe muuttuja; echo $muuttuja

--- Koodi päättyy ---


Yksi asia on varma: jos virtuoosit sanovat joukolla jotakin mahdottomaksi ei yksikään järjissään oleva ihminen tee vakavia yrityksiä sen valheeksi osoittamiseksi. 

Tässä on kymmeniä todella edistyneitä yksityiskohtia ja yhdenkin välipalikan puuttuminen romuttaisi kokonaisuuden täysin, elikä kokonaisuus on varmasti tarkoin suunniteltu. Miksi ponnistelut on valheilla tuhottu? Kunnia menisi väärälle henkilölle?

- käskysarjat ovat todella pitkiä - mutta ne ovat aina samanlaisia joten ei niitä kirjoiteta vaan kopioidaan jostakin. Kirjastot on tehty jotta koodatessa kopiointi onnistuisi yhdellä sanalla eikä tarvitsisi leikata-liimata pitkää litaniaa - mutta kirjastojen 'käyttökielto' pakottaa tuohon leikkaamiseen-liimaamiseen ja monen sivun skripteihin?
Navigaatio
Viestien etusivu
Seuraava sivu
Edellinen sivu

Siirry pois tekstitilasta