Näytä kirjoitukset

Tässä osiossa voit tarkastella kaikkia tämän jäsenen viestejä. Huomaa, että näet viestit vain niiltä alueilta, joihin sinulla on pääsy.


Viestit - petteriIII

Sivuja: [1] 2 3 ... 34
1
Silloin kun BASH luotiin sen käskyt tulkattiin muistista ja ne olivat nopeita - ihan niinkuin muissakin kielissä.
Nykyiset käskyt luetaan levyltä ennen tulkkaamista - epäilen kylläkin että ne ovat jo käännettyä koodia eikä niissä enää mitään tulkkaamista ole - mutta levyn lukeminen niissä kestää.

Ei sitä yksiomaan tuomita voi sillä on sillä paljon hyviäkin seurauksia. Mutta esitetäänpä yks esimerkki huonoista puolista: erotetaan tekstijononsta joku määräätävä sana. Totunnaiseti aikaansaadaan jotakin seuraavankaltaista:
Koodia: [Valitse]
echo '9 87 654 3210' | awk '{ print $3 }'
# tai:
awk '{ print $3 }' <(echo '9 87 654 3210')
#tai:
echo '9 87 654 3210' | cut -d' ' -f3
# tai:
cut -d' ' -f3 <(echo '9 87 654 3210')
- awk on tosi nopea ja monipuolinen - ja tämä awk:ille epä-edullinen esimerkki. Muuten cut on nopeampi.

Nopeilla käskyillä se on esimerkiksi (tämmöisiäkin tapoja on ziljoonia täysin erilaisia ja kullakin on omat erikoisominaisuutensa - mutta kaikki ne ovat nopeita):
Koodia: [Valitse]
function tavu () { apu=$1; set ${@:2} ; eval echo \$$apu ;}
tavu 3 "9a 87b 654c 3210d"
Nopeusero on niin suuri ettei sitä saa kunnolla  mitattua millään konstilla. Yksi perusteltu arvio on: 50* nopeus.

---

Tein funktiosta version jonka toiminta-nopeus on sama mutta se toimii ikäänkuin parametri palautettaisiin eikä tulosta tarvitse pää-ohjelmassa lukea rakenteella $(kutsu). Skripti ja varsinkin sen kutsu ovat niin selväpiirteisiä että on käsittämätöntä ettei sellaisia käytännössä näy:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}
etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"
echo $sana # tämä echo on ihan vain varmistuksena että skripti on tehnyt sen mitä pitäisikin.
- heittomerkit voivat olla kovia, pehmeitä tai ne voivat puuttua kokonaan.
- etsintä  voi alkaa edestä tai takaa - suunta riippuu etsintä-numeron merkistä. Toiminnan nopeuteen se ei kuitenkaan vaikuta.
- etsittävässä saa olla suurinpiirtein mitätahansa merkkejä - paitsi samanlaisia heittomerkkejä kuin alussa ja lopussa. Nykymuotisessa BASH:issa ei saa.

- Puhutaan vain ettei BASH osaa palauttaa funktioista parametreja ihan niinkuin se merkitsisi BASH:issa jotakin huonoa - päinvastoin se tarkoittaa hyvää. Jos esimerkiksi skriptissä on rivi:
etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"
niin jos funktio:etsi on oikein tehty niin seuraavalla rivillä muuttujalla:sana on oikea arvo - eikä varmasti ole palautettu mitään - nyt voi vedota siihen ettei BASH osaa. Paikalle:sana voi kirjoittaa minkä tekstin hyvänsä ja seuraavalla rivillä se on saman-niminen muuttuja. Voi se alunperinkin olla muuttuja jonka arvo on aikaisemmin ollut mitävaan tai peräti ollut toisentyyppinen.

- suurta nopeutta ei voi mitata suoraan - eikä se oikein kannattaisikaan sillä skriptin suoritus-aika vaihtelee aina vähäsen - mutta yksi ratkaisu on muodostaa keskiarvoa sillä antaahan se ainakin jonkinlaisen ajan:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}
time { for n in {1..100000}; do etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"; done ;}; echo $sana
- tämmöisessä looppi-virityksessä pitäisi laskea loopin vaikutus pois - mutta sen arvoksi voi olettaa tässätapauksessa 10% - mitattu on.
- välimuisti sotkee oikeaa tulosta, mutta pakkohan keskiarvoa on muodostaa koska tuollaista nopeutta ei voi mitata mitenkään toisin - eikä se välimuisti nopeuta sen enempää kuin 50%. Eikä se edes vaikuta täysmääräisenä heti ensimmäisellä kerralla. Kokeile. Sen kokeileminen tosin edellyttää ziljoonaa skripiä ja viikkojen pähkälyjä.

- mikäli sanoja erottaa joku muu merkki kuin välilyönti voi sen määrätä - seuraavassa sana-välinä on \n  (elikä rivinsiirto). Mutta voi se tekstiäkin olla. Skripti ja sen kutsu ovat silloin:
Koodia: [Valitse]
function etsi () { apu=${@:4}; sana_matriisina=(${apu//$5/ }); read<<<${sana_matriisina[$2]} $1 ;}
etsi sana 3 lauseesta "9a\n87\n654c\n3(21)0d'ille!" \n ; echo $sana
- jos raakaa nopeutta haluaa niin tässä on 20 mikrosekuntia kestävä versio, mutta muita ominaisuuksia sillä ei montaakaan olet:
Koodia: [Valitse]

function hae_tavu () { sana_matriisina=(${@:2}); echo ${sana_matriisina[$1]} ;}
hae_tavu 3 "9a 87b 654c 3(21)0d'ille!"


2
kokeillaan toimivatko assosiatiiviset matriisit myös:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; echo ${apu2[@]} ;}; matriisi[555]=2; matriisi[17]=joo; matriisi[ei]=4; koe matriisi
   
tulostus: 4 joo 2
Joten toimivathan ne - ainakin jollain tavalla. Assosiatiivinen matriisi muuten aakkostaa itsekseen jotenkin.

---

Miksi parametrien palauttamisesta puhutaan jatkuvasti? - tai alkaahan puhe jo harveta kun BASH on tuomittu jo kauan sitten. Onko syy se että jo silloin aikoinaan alettiin suunitella BASH:in romutusta sillä se on virtuoosien kannalta nolo opetettava - jokainen lyö kirveensä kiveen liian usein. Joten ei tosiaan haluttu ihmisten käsittävän mihin kaikkeen BASH kykenee sillä kykenevästä BASH:ista ei päästäisi koskaan eroon. Sillä niinkuin äsken esitettiin niin BASH saa funkktio-parametrien palauttamisessa aikaan saman lopputuloksen - nopeammin ja sillätavalla millä asia pitäsikin hoitaa. Samoin ne hyvin toimivat desimaalilaskut? Ja monia muitakin mahdottomiksi väitettyjä toimintoja olen saanut toimimaan - mutta niiden merkitys on toistaiseksi ainakin minulle epäselvä sillä siitä on jo kauan kun teoriat kunnolla hallitsin.

---

Mikäli tarvitsee 'palauttaa' funktiosta vain toiminnan lopputulos kannattaa se tehdä niin että se lopputulos tulostetaan funktiossa ja pääohjelmassa se napataan johonkin muuttujaan käsky-rakenteella: muuttuja_nimi=$(funktiokutsu parametrit) - palautus tapahtuu siis näyttöpuskurin kautta - toisella tavalla kuin muissa kielissä mutta yhtä tehokkaasti. Myös matriisi voidaan palauttaa tällä tavalla:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2[2]=ohhoh; echo ${apu2[@]} ;}; matrix=(1 2 3 4 5 6 7 8 9); mutrix=($(koe matrix)); echo ${mutrix[@]}
- mutrix on pesunkestävä matriisi vaikka se tulostetaankin tässä yhteen riviin.
- käsky: mutrix=($(koe matrix)) on tarpeen vain jos matriisi nimeltään:mutrix halutaan aikaansaada. Mutta usein halutaan vain tuo tulostus ja sehän on funktiossa tehty jo. Enää ei ole tarvetta 'palauttaa' yhtään mitään - kutsua voi lyhyesti:  matrix=(1 2 3 4 5 6 7 8 9); koe matrix .
- näyttöpuskurin lukeminen rakenteella: muuttuja_nimi=$(funktiokutsu parametrit) tai lukemalla sen putkeen nollaa näyttöpuskurin eikä mitään kirjoiteta näytölle, muuten se kyllä näytölle aikanaan päätyy.

Aina ei tarvitse sitä näyttöpuskuria edes lukea vaan sitä voi käsitellä suoraankin, esimerkiksi:
function koe () { echo 123456789012345678901234567890; echo 1110000; echo 12345678901234567890 ;}; koe | wc -L
Tässä funktion kaikki tulosteet menevät näyttöpuskuri-matriisissa omalle rivilleen, näyttöpuskuri työnnetään putkeen joka johtaa käskyyn: wc -L joka tulostaa näyttöpuskurin pisimmän rivin pituuden joka on 30, toiset rivit ovat pituudeltaan 20 ja 9. Rakennetta: echo $(koe) ei siis tarvita.

- ja yksi keino parametrien palautuksessa jää jotenkin pimentoon: funktiossa voi myös kirjoittaa kovalevylle - ja myös kovalevyn nopea vastine RAM-levy kuuluu BASH:in perusvarustuksiin - ja funktiosta tultua kovalevy luetaan. Levyjen käsittelyyn on buffereita, tiedosto-kuvaajia, pelejä ja pensseleitä - ihan oma maailmansa - monimutkainen kylläkin sillä käskyt ovat kauheaa merkkisotkua - kirjastot on tarkoitettu muunmuassa muuntamaan käskyjä ja käskyryhmiä helposti muistettaviksi - mutta koska BASH:in ei haluta toimivan hyvin niin kirjastoille on määrätty käyttökielto - se onkin paras keino kielen tuhoamiseksi. Kovalevyllehän asiat yleensä muutenkin päätyvät - onko sillä niin väliä koska se tehdään?

---

- loppuyhteenveto kaikesta edelläesitetystä: kyllähän tämä vähän turhauttaa koskei asialle voi tehdä enää mitään. ChatGpt ehkä voisi tehdä jotain vieläkin joten ChatGpt:n tekemä koodi kiirehdittiin tekemään Linuxissa 'laittomaksi'. Mutta kukaan ei voi edes aavistaa mihin BASH pystyisi jos sen annettaisiin kehittyä - kolmenkymmenen vuoden hyljeksintää ei kyllä luultavasti koskaan saa kurottua kiinni. Tehdyt esteet ovat useimmiten vain tahallista väärin ymmärtämistä - sillä eihän se ole rikollista etteivät alan johtavat virtuoosit alansa kaikkia muttereita tunne? Varmasti aikoinaan tunsivat, mutta nykyään se on kylläkin vain opittu asenne. Asenne joka on minullekin opetettu - ja paljon kunnollistakin särkyy kun oppeja hylkää.

3
Muuttujien arvot on nyt siirretty funktioon - ja millä nimellä ne ovat funktioon tulleetkin niin funktion sisällä niillä on aina sama nimi joten niitä voidaan käsitellä tulonimestä riippumatta aina samoilla käskyillä - muutokset tulevat aina sille 'samalle nimelle'.

Ennenkuin skriptistä palataan nuo 'aina saman nimiset' muuttujat kopioidaan takaisin niihin parametreina tulleisiin muuttujanimiin - käskyllä:
Koodia: [Valitse]
unset $1; read<<<${apu2[@]} $1
- tässä on uutta vain tuo 'unset $1' - se tarkoitaa: nollaa $1 - koska $1:ssä on se vanha arvo ja jos sen lisäksi kirjoitettaisiin muuttunut arvo niin se alkuperäinen tulisi tulosteessa sen muutetun jälkeen.
- muuten: jokainen muuttuja on sama kuin saman-niminen matriisi jonka osoitteessa 0 on muuttujan arvo - siksi käsky:
Koodia: [Valitse]
read<<<${apu2[@]} $1 
toimii tavallisellakin muuttujalla.

4
Matriisit ovat BASH:issa vähän omituisia. Esimerkiksi matriisin: jokunimi=(1 2 3 4 5 6 7 8 9) kuvaus on: 
Koodia: [Valitse]
declare -a jokunimi=([0]="1" [1]="2" [2]="8" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9")
Siitä erotetaan tekstijono joka kuvaa muuttuja arvoja: ([0]="1" [1]="2" [2]="8" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9"). Tekstijono on nykykäsityksen mukaan kummallinen koska on totuttu siihen että matriisin jäsenistä ilmoitetaan vain niiden arvo - mutta tosiasiassa matriisin jokaisella jäsenellä on sekä arvo että osoite. Muitten kielien matriisit vaan ovat sellaisia että niiden osoitteet alkavat aina ykkösestä ja kasvavat yhdellä seuraavalla jäsenellä joten osoite on sama kun jäsenen järjestysluku. Mutta BASH:issa ei ole näin vaan matriisin jokaisella osoitteella voi olla ihan mikä positiivinen arvo tahansa joten sekä arvoa että osoitetta täytyy raahata perässään - kuvatulla tavalla muodostetuilla matriiseilla on erinomaisia lisä-ominaisuuksia mutta noiden lisä-ominaisuuksen soveltaminen käytäntöön on ihmisille niin vaikeaa käsittää että käytännössä niitä ei nykyään enää sovelleta - mutta jokatapauksessa tässä toiminta on otettu huomioon eikä rikottu BASH:in matriisien rakennetta.
- joten luetaan matriisin kuvaus muuttujaan apu1. 
- muuttujan apu2 muodostamiseksi annetaan käsky:
Koodia: [Valitse]

declare ${apu1:8:2} apu2=$tekstijono

- tämänjälkeen apu2 on klooni alkuperäisestä muuttujasta - muuten paitsi nimi on apu2 - siis sillä on sama tyyppi ja samat arvot olipa se numero- tai tekstimuuttuja, matriisi tai assosiatiivinen matriisi.


Jälleen selitystä:
käsky:declare täytyy kirjoittaa itse, se ei kopiosta toimi - muu osa kuvauksesta toimii kopiosta mutta muuttujanimi täytyy muuttaa. Koska tuon -a paikalla on muuttujan määreet niin sillä paikalla lukee milloin mitäkin, mutta koska se on aina samalla paikalla niin sillä paikalla oleva kopioidaan - se on tuo ${apu1:8:2}
- muuttujan määreitä ovat: onko kyseessä tavallinen muuttuja vaiko matriisi - tai assosiatiivinen matriisi - ja ovatko parametrien alkiot read-only, integer ...   
- jokaista skriptiin lähetettävää muuttujaa kohden täytyy muodostaa oma apu1 ja apu2, apu3 ja apu4, apu5 ja apu6 ... . Ensimmäisen muuttujan nimi on apu2. Seuraavista muuttujista tulisi apu4, apu6, apu8 .... Muuttujat: apu1, apu3, apu5 ... ovat vain väliaikaisia talletuspaikkoja.
- matriiseja saa nimiparametreissa olla kuinkamonta vaan ja ne voivat sijaita kutsussa missävaan.

5
Sitten niitä selityksiä. Ensin parametrien toimittaminen funktioon:

1. Alkeellisin tapa toimittaa parametrit funktioon on lähettää sinne funktioon parametrien arvot. Yksinomaan tätä tapaa käytetään nykyään. Pahin ongelma niiden kanssa on se että niitä pitää osata käyttää ja virtuoosit eivät osaa. Tai ainakin väittävät niin, sillä BASH:ista halutaan eroon ja jos puolitotuuksilla eron saa aikaiseksi niin siitävaan.
- esimerkiksi puhutaan parametrien palautuksesta ja sanotaan ettei BASH osaa parametreja palauttaa. Välittämättä siitä että ei tässä siitä ole kyse.
- mutta on arvoparametreilla todellisiakin puutteita: esimerkiksi kun niitä käytetään niin parametreissa saa olla vain yksi matriisi ja sen täytyy sijaita viimeisenä.
 
2. Kehittyneempi tapa on lähettää parametreista funktioon ainoastaan nimet. Kun BASH:issa funktioon tulee nimi ei sillä ole saman-nimisen muuttujan kanssa mitään tekemistä vaan se on ainoastaan tekstijono - tästä eteenpäin ei ole päästy ja niinpä on luultu että nimiparametrit eivät BASH:issa toimi - tai taas kertaalleen: BASH:ista halutaan päästä eroon ja .....

Muissa kielissä tulkissa/kääntäjässä on automatiikka nimen yhdistämiseksi saman-nimisen muuttujan arvoihin - BASH:in tulkista tuo automatiikka puuttuu ja se täytyy itse lisätä, esimerkiksi näin:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; echo ${apu2[@]} ;}; matrix=(1 2 3 4 5 6 7 8 9);  mitrix=(7.7 joo); koe matrix; koe mitrix; koe matrix

Taas selitystä:
- ensin teoria: BASH:issa on jokaisella prosessilla vain yksi kirjanpito muuttujille - ja se päivitetään aina välittömästi kun jonkun muuttujan arvo muuttuu. Kirjanpidossa ei voi olla kahta saman-nimistä muuttujaa sillä jos muuttuja määritellään uudestaan niin uusi arvo ja sen typpimääritykset korvavat välittömästi kirjanpidossa sen aikaisemman kuvauksen. Kirjanpito on sama pääohjelmassa, funktioissa, funktion-funktioissa, funktion-funktion-funktioissa .... joten jos jossakin muutetaan jotakin muuttujaa muuttuu se kaikkialla - sillä ei ole väliä jos muutos tehdään sijais-nimen välityksellä. Vasta kun siirrytään uuteen prosessiin ovat sen muuttujat vain sen omia.
- sitten kuinka käytännössä toimitaan: BASH antaa skriptaajan lukea kirjanpitonsa. Funktioon siirryttyä luetaan apu1:een kirjanpidosta sen muuttujan kuvaus jonka nimi on sama kuin parametrinä tullut muuttujanimi.
- senjälkeen muuttujaan nimeltään apu2 tehdään parametrista täydellinen kopio yhdistämällä kirjanpidosta nimeen apu2 parametrinä tulleen muuttujan kuvaus.
- ei se kopio tosin vielä ole ihan täydellinen sillä kuvauksestahan siirtyvät myös lainausmerkit arvoja ympäröimään ja ne täytyy poistaa käskyillä:
Koodia: [Valitse]
apu2=${apu2%\"}; apu2=${apu2#\"}
- mutta poistaminen tapahtuu vain mikäli niiden pakoilla tosian on lainausmerkki - aina ei ole, muut merkit saavat jäädä - lainausmerkien edessä täytyy olla kenot jotta tulkki käsittelisi lainausmerkkejä tavallisina aakkosina.
- asiasta kirjoitetaan kolme riviä tulostusta jotta olisi pakko uskoa että tämä tekee sen mitä halutaan - selitykset voi aina käsittää väärin jos kovasti yrittää mutta tulosteet ovat yksiselitteisiä ja ne on pakko ymmärtää oikein.

6
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: [Valitse]
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[@]}
- 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: [Valitse]

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


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?

7
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: [Valitse]
function koe () { read<<<$(($1*99)) $1;}; muuttuja=2; koe muuttuja; echo $muuttuja
- matikkamoottori siis hallitsee nimiparametrinkin tulostuksen:
Koodia: [Valitse]
function koe () { echo $(($1+0)) ;}; muuttuja=2; koe muuttuja

Tekstijono-muuttujan kanssa:
Koodia: [Valitse]
function koe () { read<<<"kova koitos" $1 ;}; muuttuja=alkutilanne; koe muuttuja; echo $muuttuja
Numeromuuttujista kootun matriisin kanssa:
Koodia: [Valitse]
function koe () { read<<<$(($1[3]*2)) $1[2];}; matrix=(1 2 3 4 5 6 7 8 9); koe matrix; echo ${matrix[@]}
Tekstijono-muuttujista kootun matriisin kanssa:
Koodia: [Valitse]
function koe () { read<<<$3 $1[$2] ;}; matrix=(yksi kaksi kolme neljä); koe matrix 2 loisto-uutinen; echo ${matrix[@]}

---

numero-muuttujan ja tekstijono-muuttujan raja täsäkin työssä yhtä epämääräinen kuin muutenkin - ja voidaan kirjoittaa:
Koodia: [Valitse]
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

8
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: [Valitse]
function funktion_nimi () { tässä on ihan normaali skripti ;}
- 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.

9

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: [Valitse]
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"
- 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: [Valitse]
([0-9]+):([0-9]+):([0-9]+)   

Esimerkki:
Koodia: [Valitse]
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]}"
- 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: [Valitse]
${teksti[@]}    - silloin tosin saadaan vain ensimmäinen joka sopii.
- kieliasetukset vaativat skriptiin omat muutoksensa - näillä määrityksillä tämä toimii Suomessa.
 
toinen esimerkki:
Koodia: [Valitse]
[[ "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]}" ;}


kolmas esimerkki, kenttien kääntäminen - vaikkapa Suomalaisen päiväyksen muuttaminen jonkun muun maan esitystapaan sopivaksi:
Koodia: [Valitse]
[[ 1.2.2013 =~ ([0-9]+).([0-9]+).([0-9]+) ]] && echo ${BASH_REMATCH[3]}.${BASH_REMATCH[2]}.${BASH_REMATCH[1]}
- 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: [Valitse]
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[@]}

- 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.

10
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: [Valitse]
function + () { echo $(($1+$2)) ;}
- 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?

11
Esimerkiksi kun tarvitsee tarkistaa onko matriisi pelkkiä numeroita niin hyvienkin skriptien toimintanopeuksissa on kymmenkertaisia eroja - silti jokainen sopii omiin tarkoituksiinsa. Esimerkiksi seuraava - hidas on mutta toisaalta erittäin helposti räätälöitävissä:
Koodia: [Valitse]
function tarkista_matriisi () {
for f in $@ ; do
    case $f in
        *[[:alpha:]]*) echo $f ;;
    esac
done ;}; unset matriisi;matriisi=({1..100000}); matriisi[5000]=4999e; time tarkista_matriisi ${matriisi[@]}
vertaminen voidaan suorittaa myös käyttämällä diff-käskyä - onhan se paljon hitaampi ja sen tuloste on omituinen mutta toiaalta kytkimistä löytyy vaikka mitä:
Koodia: [Valitse]
function tarkista_matriisi () { diff -q <(echo ${matriisi[@]%%[[:alpha:]]*}) <(echo ${matriisi[@]}) ;}; matriisi=({1..100000}); matriisi[5000]=4999s; time tarkista_matriisi
Arvoparametreilla vertailu:
Koodia: [Valitse]
function tarkista_matriisi () { [[ ${@%%[[:alpha:]]*} = $@ ]] || echo matriisin jossakin jäsenessä on kirjain ;}; unset matriisi;matriisi=({1..100000}); matriisi[5000]=4999; time tarkista_matriisi ${matriisi[@]} # Ei kannata laittaa lainausmerkkejä

function tarkista_matriisi () { echo -e  matriisissa on seuraavat kirjaimet:; echo $@ | tr -dc [[:alpha:]] ;}; unset matriisi;matriisi=({1..100000}); matriisi[5000]=4999e; matriisi[5001]=499a9d;time tarkista_matriisi ${matriisi[@]}

function tarkista_matriisi () { apu=$(declare -p $1); [[ ${apu##*[[:alpha:]] =$apu ]] || echo matriisin jossakin jäsenessä on kirjain ;}; unset matriisi;matriisi=({1..100000}); matriisi[5000]=4999; time tarkista_matriisi ${matriisi[@]} # Ei kannata laittaa lainausmerkkejä
Arvoparametrilla nopein funktio vertaamiseen on:
Koodia: [Valitse]
function tarkista_matriisi () { echo -n matriisin kirjaimet:; echo $@ | tr -dc a-zA-ZåöÅÖ;}; unset matriisi;matriisi=({1..100000}); matriisi[50000]=4999e; matriisi[10]=ö; time tarkista_matriisi ${matriisi[@]}
Nimiparametrilla sama on noin neljäkertaa nopeampi:
Koodia: [Valitse]
function tarkista_matriisi () { apu=$(declare -p $1 | tr -dc a-zA-ZåöÅÖ); echo matriisin kirjaimet:${apu:16} ;}; unset matriisi; matriisi=({1..100000}); matriisi[100000]=e4Ö9p99; time tarkista_matriisi matriisi

12
Kymmenen vuotta sitten yksi kaveri täällä foorumilla sanoi: miksi tehdä helpolla kun voi tehdä vaikeastikin? Luulin sitä silloin vitsiksi mutta siinä taisi olla vähän totuuttakin takana, sillä se selittäisi miksi vanhat käskyt hylättiin kolmekymmentä vuotta sitten: skriptit olivat liian helppoja niillä tehdä ja senaikaisen nopeuskäsityksen mukaan myös liian nopeita - ei taviksien käsiin sellaisia saa antaa.

Esimerkiksi yritetäänpä haea mw-luku tekstistä: {"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}
- tämän ongelman esitti tällä foorumilla Duracel

Asiaan on ratkaisu grep:in ja kehittyneempien regex:ien kautta:
Koodia: [Valitse]
apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}
echo \"$(echo $apu | tr -d \")\" | grep -Po '(?<=mw:).*(?=,t)'
Edelliseen on sijoitettu lukemattomia pätevien virtuoosien miestyövuosia ja se onkin vaikuttava tekniikan riemuvoitto? Tässävaiheessa on vaikeaa ottaa kantaa siihen että skripti kestää 5ms ja skriptin kasaaminenkin on vähän räätälöimistä ja taiteilua. Entäpä sama vanhoilla käskyillä:   
Koodia: [Valitse]
apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}   
apu=${apu#*mw:*}; echo ${apu%%,*}
Nykykäsityksenkin mukaan nopeaa ja yksinkertaista - kestää .1 ms - ja skriptin tekemisessä ei ole ongelmia. Ja toimi jo kolmekymmentä vuotta siten. Olisi todella kiinnostavaa tietää onko joku tehnytkin?

13
Vanha BASH oli tehty ratkaisemaan tämmöisiä tehtäviä salaman-nopeasti - esimerkiksi erottamaan haluttu osa tekstijonosta. Mutta melkolailla BASH:in synnyttyä alettiin ajatella että on kestämätöntä että taviksetkin saavat nopeasti ratkaistua tällaisia tehtäviä - eihän valmis-ohjelmilla olisi silloin mitään tehtävää. Joten BASH:in vanhat käskyt täytyi lähettää unholaan ja tehdä uusi tämmöiseen toimintaan sopimaton käskykanta - mutta onneksi niitä vanhoja käskyjä ei hävitetty.

Ja toistaiseksi kaikissa Ubuntu koneissa pääte-ohjelma osaa BASH:ia halusi tai ei - eikä edes lausetta: !#/bin/bash tarvita. Tässä on käytetty kahta BASH:in tuhansista vanhoista käskyistä. Tämän tehtävän ratkaisu:
Koodia: [Valitse]
apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}   
apu=${apu#*mw:*}; echo ${apu%%,*} 
- lainausmerkki pitää jättää valitsevista käskyistä pois. Siis 32 kirjainta koko käsky - helppo soveltaa mihin vain ja lisäksi nopeampi kuin Python - tai useimmat muutkaan. Ja mitä haluaakin niin muutokset skriptiin ovat yksinkertaisia ja toiminta yhtä varmaa kuin parhaimmallakin valmisohjelmalla. Esimerkiksi:
Koodia: [Valitse]
apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}   
apu=${apu#*mv:*}; echo ${apu%%,*} # antaa jännitteen kum muuttaa kirjaimen: w kirjaimeksi: v

apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}   
apu=${apu#*ma:*}; echo ${apu%%,*} # antaa virran kum muuttaa kirjaimen: w kirjaimeksi: a

apu={"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}   
apu=${apu#*wh:*}; echo ${apu%%,*} # antaa tehon kum muuttaa kirjaimet: mw kirjaimiksi: wh

14
BASH:issa on vanhoja käskyjä enemmän kuin uusia - ja ne ovat aivan erilaisia kuin uudet käskyt: ne eivät ole omissa tiedostoissaan eivätkä ne käsittele tiedostoja vaan matriiseja ja muuttujia - joten ne toimivat nopeasti. Niistä ei virallisesti puhuta missään: tosin saatetaan niiden olemassaolo mainita ylimalkaisesti. Mutta netti ei koskaan unohda ja sinne kerkisi kauan sitten tulla niistä enemmänkin asiaa - tosin missään ei mainittu kuinka nopeita ne ovat varsinkin ryhmänä. Eikä missään ei ole minkäänlaista yhteenvetoa niistä - ja kattavan yhteenvedon tekemisessä olisikin kauhea työ.

Vanhat käskyt ovat tekstinkäsittelyä eivätkä matematiikkaa vaikka saahan niillä matematiikankin toimimaan. Mutta tekstinkäsittelyä ne nopeuttavat paljon enemmän - en yritäkään ymmärtää sitä umpihullua tilannetta jossa nyt olemme:

Aikaisemmin olin varma että find-käsky on erinomainen. Entäs printf, grep, sed ... ja monet muut. Kuitenkin esitin aikoinaan epäilyksen siitä että uudet käskyt on tehty jotta nuo muut käskyt voisivat loistaa - maailmalla yksi pätevä virtuoosikin oli esittänyt jotain samankaltaista. Silloin en täysin uskonut siihen itsekään, mutta nyt alan epäillä että osuin oikeaan. Mahdottomasti töitä kyllä on tekemättä - niin paljon ettei ole selvää olisiko niiden tekemisessä enää mitään mieltä - sillä BASH on päästetty niin surkeaan tilaan etteivät isotkaan parannukset ehkä riitä.

Tosin vanhoista käskyistä ei ainakaan toistaiseksi ole vakavasti otettavaa vastusta noille erinomaisille ohjelmille vaikka satunnaisesti vanhat käskyt vievätkin voiton - toistaiseksi on täysin epäselvää kuinka satunnaisesti. Sillä kolmenkymmenen vuoden laiminlyönti tuntuu kyllä niinkuin myös tahallinen sabotointi. Virtuoosien syyttäminen kaikesta tapahtuneesa lienee hieman väärin sillä useat heistä ovat päinvastoin tehneet ihailtavaa työtä - mutta tuolla huipullahan syyllisten täytyy olla - joten voi vain sanoa että kuka homman on tehnytkin ei skriptaamisesta ole konnan-töissään välittänyt vähääkään.

Varsinkin matriisien toiminta on tekemällä tehty huonoksi - esimerkiksi BASH:issa kaikkilla viitataan matriisin jäseniin: ${matriisin_nimi[$jäsen_numero]} - noiden aaltosulkujen tarpeeseen annetiin aikoinaan sellainen seli-seli etteivät selittäjät itsekään siihen uskoneet. Mutta tavallaan ei voi sanoa että vanhoja käskyjä käsiteltäisiin jotenkin sorsivasti sillä myös uudet käskyt toimivat samallatavoin. Mutta kuinkas sattuikaan niin vanhoilla käskyillä asia kirpaisee paljon enemmän:

Matriisien - elikä tiedoston muistissa olevan kuvan - käsittely uusilla käskyillä on sellaista ettei kukaan viitsi matriisien kanssa toimia - oikeastaaan kyse on nopeudesta ja sitähän ei uusilla käskyillä saavuteta muutenkaan. Mutta vanhoilla käskyillä matriisien käsittely sujuu ihan kätevästi ja nopeasti. Esimerkiksi kun ison tiedoston joku sana pitää muuttaa kaikkialla toiseksi - uusilla käskyillä työ kestäisi iäisyyksiä ja niinpä turvaudutaan sed:iin. Mutta vanhoilla käskyillä luetaan tiedosto ensin matriisiin ja muutetaan sitten:
Koodia: [Valitse]
< tiedoston_nimi  readarray teksti; echo -e ${teksti[@]//mikä/miksi}
- tosin käsky on hieman hitaampi kuin sed mutta eipä paljoa. Ja sivuvaikutuksena on tiedostosta tehty matriisi.

Tai koetapa etsiä tiedostosta se mitä lukee esimerkiksi sanan: höikäsenpöläys jälkeen. Tavallisesti tähän on käytetty käskyä grep. Mutta sama etsintä toimii nopeasti myös vanhoilla käskyillä:
Koodia: [Valitse]
< tiedosto  readarray teksti; echo ${teksti[@]##*höikäsenpöläys }.
Kuinkahan BASH pärjäisi jos ulkoisiin käskyihin laitetut kehitys-ponnistukset olisi laitettu BASH:iin?

Vain mielikuvitus on rajana kaikessa nopeassa ja kummallisessa - aika kyllä vasta näyttää saako kokoon tarpeellista määrää luotettavasti toimivia ihmeellisyyksiä - sillä ei tulos enää taitaisi olla vaivan arvoista olipa tulos kuinka erinomaista hyvänsä. Katsopa esimerkisi seuraavaa käskyä:
Koodia: [Valitse]
a=(99a 88a 98a 89a 1234 zeta5 jaakkokulta 15 kiiski 1jones8 1joonas9 17 18 209); echo ${a[@]##*[0-8jk]}
- käsky suodattaa pois kaikki numeroihin 0-8 loppuvat matriisin jäsenet - samoin kuin poistaa jäsenistä alusta viimeiseen numeroon 0-8 tai kirjaimiin j tai k asti.
- onko tämän ryhmän neljällä mielettömällä käskyllä jotain käyttöäkin? Mutta ainakin käskyt kertovat yhden oleellisen eron vanhojen ja uusien käskyjen välillä: vanhat toimivat niinkuin jo etukäteen voi arvella ja aina samalla tavalla - mutta sensijaan kun uusilla käskyillä vaihtaa dataa niin tehdäänkin jotain muuta.
- tosin tämänkaltaisten skriptien tekemisessä olisi ihmiselle ihan liian suurin työ ja niiden tekemiseen olisikin tehtävä skripti - ja skriptaus nousisi ihan uudelle tasolle. Kauheaa - mitä noille hienoille ohjelmille tapahtuisi?

- eihän tästä mitään varmaa voi sanoa ennenkuin kymmenvuotisen tutkimisen jälkeen mutta näin vajain todistein: nykyisin käytettävät uudet käskyt ovat pahantahtoista murhaa. Pakko niitä on silti toistaiseksi käyttää sillä eihän niistä vanhoista käskyistä vielä kunnon käsitystä ole. Kenelläkään ei voi olla aavistustkaan mihin nämä päätyvät.

15
Aloittaessani BASH-skriptaamisen opin nopeasti ettei matematiikka toimi juuri ollenkaan - ja syy siihen selvisi hyvin hitaasti sillä syy on erittäin huolellisesti kätketty koska jäljet johtavat sylttytehtaalle: matematiikan ei haluttu toimivan. BASH:ille tilanne oli tosihuono mutta ulkoisille ohjelmille todella herkullinen - ei tarvinnut pelätä minkäänlaista kilpailua BASH:in taholta - nimittäin esimerkiksi vaatimattomista desimaalilaskuistakin BASH suoriutuu nytkin nopeammin kuin bc, awk tai joku muu. Mutta jo etukäteen on otettu huomioon se että joku kahjo sittenkin saa matematiikan toimimaan ja annettiin 'kirjastokielto' että tuloksienkin käyttäminen olisi vaikeaa. 'Kirjastokielto' vaikeuttaakin BASH:issa ihan kaikkea mitä itse teet - koodia tulee mielettömiä määriä ja funktio-kutsutkin ovat todella pitkiä.

- 'bc, awk tai joku muu' ovat hitaita koska ne ovat isoja ohjelmia loopeineen ja assosiatiivisine matriiseineen - ja ne täytyy ladata kovalevyltä ja tulkkaus-vaikeuksiakin taitaa tulla. Sensijaan BASH on jo koneessa eikä mitään tulkkaus-vaikeuksia varmasti tule. Mutta vain hyvin vaatimattomissa perus-laskuissa BASH on nopein.

KoskeiBASH:in mtematiikka juurikaan toiminut täytyi sille ensin kehittää rutiinit. Mutta kehittämis-yrityksistä oli tehty vaikeaa muodostamalla uusi käskykanta jossa oli 'hidastetyössy'. tekstinkäsittelykin jotuu töyssyyn kerran mutta tekstinkäsittelylle siitä on usein hyötyäkin - sensijaan matematiikka joutuu töyssyyn useasti muttei siitä koskaan ole mitään hyötyä. Joten työssy teki matematiikan kehittämisen yrityksistä niin hidasta että oli epätodennäköistä että joku huomaisi käytännossäkin kuinka matematiikan saa toimimaan. Siihen tarvittiinki omituinen yhteensattuma että huomasin käyttää vanhoja käskyjä sillä siinävaiheessa olin täysin tieämätön siitä että mahdollisuuksia kehittämiseen edes oli.

Hidastetöyssy toimi seuraavalla tavalla: uudet käskyt ovat kaikki omassa tiedostossaan joten käskyn suorittamiseksi on ladattava tiedosto ja kuluuhan siinä lukemisessa pieni hetki.

Mutta tiedoston lukemisessa tulee myös ajallista epämääräisyyttä: lukeminen toimii ainoastaan silloin kun prosessori sallii sillä tietokoneen käyttöjärjestelmä on yhtaikaa toimivien prosessien sekamelska ja prosessori määrää kuka milloinkin saa suoritusaikaa - kukaan ei koskaan joudu kohtuuttomiin odotusaikoihin mutta ilmanmuuta toinen on parempi kuin toinen ja kiilaa aina edelle - esimerkiksi kriittiset järjestelmä-prosessit. Kun käsky tulee tulkattavaksi niin ensiksi on luku levyltä ja siinähän sitten odotellaan milloin mitäkin ennenkuin käsky pääsee tulkattavaksi. Ja BASH totisesti odottaa sillä eihän se nokkimis-järjestyksessä korkealla ole. Noista epämääräisen pituisista odotuksista seuraa se että tekstinkäsittelyssäkin sama työ kestää joskus millisekunnin ja joskus kolme - matematiikkaan tulisi paljon pidemmät odotusajat mikäli käyttäisi uusia käskyjä. 

Matematiikan kehittäminen toimivaksi edellyttää ettei hitaita ja epämääräisen pitkiä toiminta-aikoja ole vaan että kaikki toimii välittömästi - ja vanhat käskyt toimivatkin välittömästi sillä niillä ei omia tiedostoja ole vaan kaikkien niiden tulkkkausohjeet ovat jo BASH-tulkissa eikä niiden tulkkaaminen edellytä hidasta tiedostolukua. Välitöntä toimintaa tarvitaan kokeilemisessa sillä kaikki mitä yrittääkin toimii jotenkin ja tarvitaan miljoona koetta ennenkuin on selvää yrittääkö oikealla tavalla vai täytyykäö alkaa uudestaan alusta - ja niitä tapojahan riittää. Tosin hidastelu ei täysin estä matematiikan kehittämistä - tekee vain toiminnan selvittämisestä mahdotoman hidasta. Selvitys kuinka BASH:issa matematiikka toimii:

Tulkin mielestä numeroista muodostuneella tekstijonolla ja matemaattisella arvolla ei ole mitään eroa - vaikka samassa lauseessa numeroista muodostunutta tekstijonoa voidaan käsitellä välillä tekstijonona ja välillä matemaattisena arvona - sillä numeroista muodostuneet tekstijonot kelpaavat matematiikkamoottorille ihan hyvin - se ei ole edes teoriassa virheellistä. Mutta jotta desimaalimatematiikan saisi toimimaan täytyy tekstijonojen käsittelyn olla todella nopeaa.

Suurten tiedostojen käsittelyssä BASH:in uudet käskyt loistavat. Ja kyllä ne toimivat tekstijonojenkin kanssa suurinpiirtein yhtähyvin kuin tiedostojenkin kanssa vieden aikaa millisekunnin-pari. Tuo millisekunti-pari on kuitenkin desimaalimatematiikan kehittämisen kannalta aivan liian pitkä aika - mutta vanhat käskyt kuluttavatkin mitättömän vähän aikaa tekstijonoja käsitellessään. Esimerkiksi käsky wc joka on tarkoitettu tiedostojen merkki-/sana-/lause-lukujen määrittämiseen - siitä pieni esimerkki:
Koodia: [Valitse]
# käsky wc tekstijonon pituuden määrittämisessä putkittamalla:
koe=1234567890; time echo $koe | wc -L     # kestoaika on 2ms
# käsky wc putkittamatta:
koe=1234567890; time wc -L < <(echo $koe)  # kestoaika on 2ms

# ja vastaava vanha käsky joka on tarkoitettu vain tekstijonon pituuden määrittämiseen eikä se tiedoston kanssa toimi:
koe=1234567890; time echo ${#koe}          # kestoaika on 0ms - tarkemmin noin 0.1 ms.
Mitenkä tämä siihen vaikuttaa että matematiikka tökkii uusien käskyjen kanssa? Syy on se että kaiken ennestään tuntemattoman kehittämisessä joutuu kokeilemaan miljoona kertaa mitenkä asia toimii - ja melkein mikähyvänsä toimii jotenkin ja kestää kauan ennenkuin varmistuu että yrittää väärällä tavalla - alusta taas uudestaan.

Ja uudet käskyt ovat niin epämääräisen hitaita että niillä desimaali-matemaatiikan kehittäminen olisi niin hidasta että on aivan varmaa ettei kukaan ala sitä uusilla käskyillä kehittämään. Ja onhan sen kehittäminen henkisestikin raskasta koska virtuoosit sanovat sillä ylimielisyydellä johon vain virtuoosit pystyvät ettei desimaalimatematiikka voi BASH:issa toimia ja hullu on se joka sitä edes yrittää - saa kokoajan epäillä onko itse enää järjissään.

Mutta kun oikea tapa on löytynyt niin senjälkeenhän on aivan selvää että niinhän desimaalimatematiikka toimiikin, mitä huomaamista siinä nyt on. 

16
Aikamonta muutosta 8.4.2024

Matemaattisten ongelmien alkusyy taitaa olla se että matemaatikot ja ei-matemaatikot ovat aina tapelleet keskenään veri roiskuen - ja BASH:issa ei-matemaatikot pääsivät niskanpäälle ja jotta matematiikasta ei koskaan enää olisi harmia niin he teloivat BASH:in matematiikkaan täysin sopimattomaksi - mutta ampuivat samalla myös omaan jalkaansa.

Nimittäin toiminnoissa on suuri ero: ei-matematiikka toimii tekstien kanssa ja tekstiä on usein paljon ja tiedostoilla on suuri merkitys. Matematiikka puolestaan toimii numeroiden kanssa muistissa eikä niitä käsiteltäviä numeroita kovin montaa ole joten ei tiedostoilla kovinkaan suurta merkitystä matematiikassa ole - joten siirryttäessä matematiikka-painotteisesta ei-matematiikkapainotteiseen skriptaukseen muunmuassa tehtiin uusi käskykanta.

Mutta käyttämättömiksi jääneet vanhat käskyt ovat niin nopeita että vielä kolmenkymmentä vuotta rappeuduttuaan ne pystyvät lähes nykyisten skriptikielten nopeuteen - mutta yksikin uusi käsky hidastaa toimintaa paljon. Ja lisäksi ne toimivat niinkuin ihmiset olettavatkin - merkintätavat vanhoissa käskyissä tosin ovat niin mielettömiä ettei vanhoilla käskyillä voi olla nimeäkään.

Vanhat käskyt ovat laajoja ohjelmia nekin. Esimerkiksi seuraavassa skriptissä on lause:
Koodia: [Valitse]
trap 'echo virherivi:${LINENO[@]};echo ${FUNCNAME[@]}; echo "   $BASH_COMMAND"' ERR
ja se toimii aivan niinkuin lause sanookin, mutta mikäli skriptiä suorittaessaan saisi virheviestin niin todennäköisesti virheviesti olisi seuraavankaltainen: virherivi: 356  . Eikä mitään funktio- tai käskyn nimeä sillä virhe on tapahtunut vanhassa käskyssä - ja on kieltämättä on paha puute ettei vanhoilla käskyillä ole kumpaakaan nimeä. Muuten rivinumeroita ja nimiä tuleee monta jos toimitaan funktiossa jota on kutsuttu funktiosta jota on kutsuttu funktiosta ...  BASH:issa on sellaiseenkin toimintaan tarpeelliset kutsupinot ja osoittimet. Pinot ovat osittain sitä varten että 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. Tuo 356 on siis virherivin numero jossakin vanhassa käskyssä kun käskyä on esimerkiksi käsketty tulostamaan paikasta jota ei ole - siis toiminnan aikainen looginen virhe sillä ei vanhoissa käskyissä fyysisiä virheitä ole.   

- tosin ei BASH skripteissä rivinumeroita ole - se mitä rivinumeroksi kutsutaan on editorin antama rivinumero joka ei suoranaisesti liity BASH:iin - numerointi alkaa aina yhdestä ja kasvaa yhdellä aina seraavilla riveillä. Kuitenkin rivinumero annetaan tuossa virheviestissä: se on juuri tuo rivinumero jonka editori antaisi. 

- osittain tästäsyystä BASH:issa ei ole goto käskyä: rivinumeroita ei ole eikä label:iakaan. Sensijaan BASH:issa on tavallistakin erinomaisempi gosub-käsky: tosin sanaa gosub ei siinä ole ja siirtyä voi vain funktioihin. Sillä jokainen muuttuja voi toimia funktiokutsuna - tulkki sen taas tekee: jos muuttujaa ei voi tulkita mitenkään muuten koetetaan onko se johonkin funktioon osoittava funktiokutsu elikä gosub-käsky - ja välittömästi ensimmäisen muuttujan perässä olevat muuttujat ovat sitten funktion parametreja. Erinomaisuus tulee siitä että myös matriisin jäsenet voivat toimia gosub-käskyinä. Ja assosiatiivisten matriisien jäsenet eritoten sillä nehän suorittavat samalla koodimuunnoksen. Kyllä oikein toteutettu gosub on ihan hyvä käsky. Sanoo esimerkiksi Linux Torvalds.
Koodia: [Valitse]
function summain18 () { # skripti laskee kahden desimaali- tai kokonais-luvun summan tai erotuksen mikäli tulokseen tulee vähemmän kuin 18 numeroa.
trap 'echo virherivi:${LINENO[@]};echo ${FUNCNAME[@]}; echo "   $BASH_COMMAND"' ERR
tulosta=echo # yhdessä paikassa päätetään tulostetaanko välituloksia. Vaihtoehdot:tulosta=echo ja tulosta=:
$tulosta -e '\n\nsummattavat luvut:  '$1' '$2 
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0" 
 
koko1=${luku1%%.*}
desi1=${luku1##*.} 
koko2=${luku2%%.*}
desi2=${luku2##*.}

koko1=${koko1:=0} ;koko2=${koko2:=0}  # kokonaisosat saavat arvon nolla jos niillä ei ole aikaisempaa arvoa
$tulosta koko1:$koko1'   koko2:'$koko2

# desimaaliosien tulee olla yhtäpitkät. Lyhyemmän perään kirjoitetaan nollia. Desimaalien määrä täytyy tallettaa.
(( ${#desi1} >= ${#desi2} )) && desipituus=${#desi1} || desipituus=${#desi2}
desi1=$desi1'000000000000000000'
desi2=$desi2'000000000000000000'
desi1=${desi1:0:$desipituus}
desi2=${desi2:0:$desipituus}; $tulosta desi1:$desi1' desi2:'$desi2

luku1=$koko1$desi1
luku2=$koko2$desi2; $tulosta luku1:$luku1'  luku2:'$luku2
luku3='0000000000000000000000000000'$(($luku1+$luku2)); [[ $luku3 =~ - ]] && { luku3=${luku3//-/} ; merkki=- ;} || merkki='' ; $tulosta 'luku3:' $merkki$luku3
desipituus=$(($desipituus-1))

echo -e "\n\nseuraavalla rivillä on varmasti oikea vertailutulos bc-matematiikkaohjelmasta ja seraavalla rivillä on se mitä tämä skripti antaaa:";bc<<<"$1+$2"
desipituus=$(($desipituus+1))

apu=${luku3:0: -$desipituus}.${luku3: -desipituus}; apu=${apu##+(0)}; [[ ${apu//[^.]/} ]] && apu=${apu%%+(0)}; apu=${apu%.}
(($koko1+$koko2+$desipituus)) && echo $merkki$apu || echo 0 ;}
 
time summain18 123456789013.02 -223456789012.012
time summain18 13 -12.00000007021
time summain18 123456789013.00000002 -123456789012.00000000000000002
time summain18 1 1

17
Matemaattista yhtäsuuruuden vertaamista ei kannata tehdä matemaattisesti sillä se rajoittaisi merkkiluvun 18:sta eivätkä  desimaalit toimisi ollenkaan - siksi on parempi suorittaa vertailu tekstijonoilla.

Tekstijono-vertailu on nopea mutta se toimii melkein aina virheellisesti desimaalien kanssa, sillä vertaaja ei tajua että desimaalit tulee verrata merkki kerrallaan alkaen desimaalipisteestä ja heti kun toinen on erilainen niin vertailu lopetetaan desimaalien osalta sillä voittaja on löytynyt - desimaalit voi kyllä verrata tehokkaasti ryhmänäkin mutta silloin lyhyemmän desimaaliryhmän perään tulee kirjoittaa niin monta nollaa että pituudet ovat samoja - tässä skriptissä käytetään tätä tehokkaampaa tapaa.

Samoin tekstijono-vertailu menee usein pieleen etumerkkien kanssa. Tässä asia hoidetaan erikseen.

Tekstijono-vertailu ei kavahda tekstiäkään vaan voit kirjoittaa: kumpionsuurempi 12km/t 13km/t

Aikaa vertaamiseen kuluu tällä skriptillä noin 1 ms riippumatta siitä millaisia lukuja verrataan.

Ja ennenkaikkea tälle skriptille olisi helppo opettaa uusia temppuja, esimerkiksi tieteellisesti esitettyjen lukujen vertailu, merkkimuutoksien tekeminen tai matematiikan suorittaminen ennen vertailuja ... Mutta tämänhetken toiminta:
 
1. Lukujen etumerkit talletetaan omiin muuttujiinsa.
2. Jos toinen tai kumpikin luvuista on pelkkä kokonaisluku niin sen desimaaleiksi kirjoitetaan .0   
3. desimaaliosista tehdäänyhtäpitkätkirjoittamalla lyhyemmän perään nollia.
4. jos toinen tai kumpikin luvuista on pelkkä desimaaliluku niin kokonaisosaksi kirjoitetaan .0   
5. kokonais- ja desimaaliosa kirjoitetaan peräkkäin jättäen desimaalipiste pois.
6. suuremmuuden testaus suoritetaan tekstijono-vertailuna. Tämä on melkein yhtänopea kuin erittäin rajoittunut matemaatinen vertailu BASH:in matematiikalla - ja paljon nopeampi kuin vertailu bc:llä.

- miten toiminta koodataankin niin käskyissä ei ehdottomasti saa olla yhtäkään uutta käskyä - ne on tehty tuhoamaan BASH:in nopeus - on syytä epäillä että tuhoaminen tehtiin tarkoituksella eikä tapahtunut  vahingossa.
- mikähän kaikki muukin olisi mahdollista kun BASH pystyy tällaiseen vielä kolmenkymmenen vuoden heitteillejätön jälkeen? Mutta jokatapauksessa kestää vielä kauan näiden skriptien vakautumiseen sillä oppirahat on aina maksettava.

Koodina tämä on tällainen:
 
Koodia: [Valitse]
function kumpionsuurempi () {
[[ $1 =~ .*\..* ]] && luku1=$1 || luku1=$1".0" # on tarpeen että luvussa on yksi desimaalipiste - joten piste lisätään luvun perään sellaista jos ei vielä ole.
[[ $2 =~ .*\..* ]] && luku2=$2 || luku2=$2".0"
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+ # lukujen merkit omiin muuttujiinsa - mutta ei niitä etumerkkejä poistaa tarvitse
 
koko1=${luku1%%.*};koko2=${luku2%%.*};koko1=${koko1:=0} ;koko2=${koko2:=0}     # käskyssä: koko2=${koko2:=0} -> 0 on oletusarvo jos mitään ei aikaisemmin ole

desi1=${luku1##*.}; desi2=${luku2##*.} 

# desimaali-osat yhtäpitkiksi:
(( ${#desi2} >= ${#desi1} )) &&
{ apu=$desi1"00000000000000000000000000"; desi1=${apu:0:${#desi2}} ;} || { apu=$desi2"00000000000000000000000000"; desi2=${apu:0:${#desi1}} ;}
luku1=$koko1$desi1; luku2=$koko2$desi2 # muutos

[[ $luku1 = $luku2 ]] && echo 'luvut ovat yhtäsuuria' || {
case $m1$m2 in
-+) echo 'toka on suurempi' ;;
+-) echo 'eka on suurempi' ;;
++) [[ $luku1>$luku2 ]] && echo 'eka on suurempi' || echo 'toka on suurempi' ;; # tai tulostus tosikäytössä: 'eka on suurempi' kirjoitetaan: 1 - ja 'toka on suurempi' kirjoitetaan: 0
--) [[ $luku1>$luku2 ]] && echo 'toka on suurempi' || echo 'eka on suurempi' ;;
esac ;};}

time kumpionsuurempi 7C12.5B 7C12.5C1234567890 # siis hexa-desimaali lukujen vertailu

# tai vertailtavissa voi olla niin paljon numroita etteivät ne mahdu yhdelle riville:
time kumpionsuurempi 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567891 

   



18
BASH:in pahin piirre on sen tajuton monipuolisuus ja se että salahautojen välissä on vain vähän tukevaa maata - lisäksi BASH-toteutuksia on melkein yhtämonta kuin erilaisia Linuksejakin - ja kaikki tekevät omia virityksiään - luulisinpa että joku tuhoaa Linuksiakin eikä vain BASH:ia. Nimittäin kielikin tuhotaan parhaiten sisältäpäin - IBM, Windows ja Mac:kin ovat osallistuneet karkeloihin - tahallaan vai tarkoituksella?

Mutta BASH:ista puhuakseni: kun lähestyin isojen lukujen kahdella jakamista toista reittiä kuin ennen niin havaitsin että esimerkiksi lasku: 12345678901234567890123456780876/2 lasketaaan BASH:illa näin:
Koodia: [Valitse]
echo $((12345678901234567/2))$((10#$((12345678901234567%2))890123456780876/2))   
ämä on jatkoa sille että matematiikkamoottorille voi lähettää millaista törkyä tahansa ja se yrittää selittää sen parhainpäin - tottamaar sen täytyy noudattaa tiettyjä määräyksiä. Mutta jos kuvatusta muodosta tekee skriptin:
Koodia: [Valitse]
echo $(($a/2))$((10#$(($a%2))$b/2))

elikä siinä alle 32 numeroinen luku on katkaistu suurinpiirtein keskelta ja a on se etuosa ja b on takaosa. Mutta lasku ei valitettavasti toimi oikein kaikilla a:n ja b:n arvoilla, useimmilla kylläkin - ja syystä ei vielä ole aavistustakkaan. Eikä ole minkäänlaista varmuutta että siitä koskaan saa tehtyä mitään todella käyttökelpoista - se on kyllä selvinnyt että itseasiassa se kelpaa alle 32 numeroisten kokonaislukujen jakamiseen kaikilla alle 16 numeroisilla - tulos on kylläkin kokonaisluku, ihan niinkuin ennenkin - jakojäännöksineen. Kai tätäkin toimintaa kannattaa tutkia vaikkei siitä enää muuta hyötyä ole kuin muinaisten virtuoosien kiemurtelu.

19
Edellä esitetty desimaalilukujen yhteenlasku olisi pitänyt kasata jo yli kolmekymmentä vuotta sitten kun sen osat toimivat jo - tämä on jonkinverran erilainen kuin aikaisemmin esitetyt versiot kiertäen pari salahautaa. Enkä usko sekuntiakaan etteivät virtuoosit tienneet näistä mutta sensijaan että olisivat julkaiseet tällaisten koodeja alkoivatkin väittää tämmöisiä mahdottomiksi. Joten täytyy nyt esittää tämä koodi kun asialla ei ole enää mitään virkaa - mutta voi sentään toivoa että jatkoa löytyy. Mutta mielenkiinnon vuoksi tutkin toimiiko tämä aina oikein.

Näiden perus-laskutoimitusten skriptien toiminnan tulee olla nopeutensa lisäksi toimia aina virheettömästi joten toiminta on syytä tarkistaa. Tarkituksesta tulee käsipelillä tosihidasta, mutta täysin automaattinen tarkistus loisi tässävaiheessa uskottavuus-ongelmia - joten aluksi tehokas puoli-automaattinen ratkaisu - jossa näkee jonkunverran siitä mitä tapahtuu.

Aluksi vain täytetään koko näyttö tuloksista. Tulostukset tulevat aina pareittain, ensin tulee arvo bc:stä ja se on ihan varmasti oikein - sen alapuolelle tulee tämän skriptin aikaansaannos ja sitten tyhjä rivi. Tarkoitus on siis varmistaa että lukuparissa kumpikin on sama.

Ajaakseesi skriptin kopioi koko koodikenttä kerralla ja liimaa se päätteeseen ja paina return jolloni tulee 20 tulosta - kun näyttö jähmettyy niin näkee heti onko jollain ryhmällä luvut erilaisia - ja jos on niin koodi on viallinen joten varmasti ei ole - mutta itse se on todettava. Kun painat nppulaa 'nuoli-ylös' palaa kutsu takaisin näytölle ja return saa aikaan 20 uutta laskua.

- kutsuttuasi kutsun takaisin voit editoida sitä ja poistaa kutsusta miinusmerkin joten vähennyslasku muuttuu yhteenlaskuksi.
elikä tämmöiseksi: for n in {1..20};do summain $RANDOM.$RANDOM $RANDOM.$SRANDOM; done .
Koodia: [Valitse]
function summain () {
echo; bc -l<<<"$1+$2" # jos kutsu on vähennyslasku niin laskuun tulee +- ja sekä BASH että bc tajuavat että se on - . Sensijaan merkintää: -- ne eivät ymmärrä.
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0" 
 
koko1=${luku1%%.*}
desi1=${luku1##*.}   
koko2=${luku2%%.*}
desi2=${luku2##*.}

# desimaaliosien tulee olla yhtäpitkät. Lyhyemmän perään kirjoitetaan nollia.
(( ${#desi1} >= ${#desi2} )) && desipituus=${#desi1} || desipituus=${#desi2}
desi1=$desi1'000000000000000000'
desi2=$desi2'000000000000000000'
desi1=${desi1:0:$desipituus} #; echo desi1:$desi1
desi2=${desi2:0:$desipituus} #; echo desi2:$desi2 ;$tulosta

luku1=$koko1$desi1
luku2=$koko2$desi2

tulos=$(($luku1+$luku2)); echo ${tulos:0: -desipituus}.${tulos: -desipituus} ;}

for n in {1..20};do summain $RANDOM.$RANDOM -$RANDOM.$SRANDOM; done

20
Supernopea ja moitteeton desimaali- tai kokonaislukujen yhteen/vähennyslaskin - sen lopputuloksessa voi kylläkin olla numeroita vain 18:
- mutta jo 36 numeroinenkin yhteenlasku vaatii paljon lisää
Koodia: [Valitse]

function summain () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0" 
 
koko1=${luku1%%.*};
desi1=${luku1##*.};   
koko2=${luku2%%.*};
desi2=${luku2##*.}

# desimaaliosien tulee olla yhtäpitkät. Lyhyemmän perään kirjoitetaan nollia.
(( ${#desi1} >= ${#desi2} )) && desipituus=${#desi1} || desipituus=${#desi2}
desi1=$desi1'000000000000000000'
desi2=$desi2'000000000000000000'
desi1=${desi1:0:$desipituus} #; echo desi1:$desi1
desi2=${desi2:0:$desipituus} #; echo desi2:$desi2 ;$tulosta

luku1=$koko1$desi1
luku2=$koko2$desi2

tulos=$(($luku1+$luku2)); echo ${tulos:0: -desipituus}.${tulos: -desipituus} ;}

summain 31.1  1.1111111  # tämä on yhteenlasku
summain 31.1 -1.1111111  # tämä on vähennyslasku
summain -31.1 -1.1111111 # voi kummatkin negatiivisiakin olla
summain 1 1              # kannattaa varmistaa että tämmöinenkin toimii 


Sivuja: [1] 2 3 ... 34