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 4 ... 35
21
 Sain lopultakin kasattua sellaisen desimaali-matriisin jäsenten yhteenlaskun joka ottaa huomioon myös lukujen konaisosan. Eikä tämä häviä muille kielille ihan mahdottomasti mutta ei tämmöisillä laskuilla merkitystä enää ole. Paitsi sen osoittamisessa että BASH tuhottiin jo 30 vuotta sitten - tietäen että tämmöiset toimivat jo siloin ja olivat ihan kelvollisia senajan vaatimusten mukaan - ja mitähän kaikkea vielä paljastuukaan.
- kopioi koko koodi-alue päätteeseen yhdellä kertaa ja paina enter
Koodia: [Valitse]

clear; koematriisi=($(for n in {1..170}; do apu1=$SRANDOM; apu2=$SRANDOM; echo $((1${apu1:9}${apu2:8}**4)).$((1${apu2:9}${apu1:8}**4)); done)); echo; echo yhteenlaskettavien joukko:; echo; echo ${koematriisi[@]} | xargs -n$(($(tput cols)/35)) | column -t; echo
# selitystä: | xargs -n$(($(tput cols)/35)) | column -t muodostavat BASH:in super-hyvän formatointi-ohjelman joka formatoi vasta tuloksen

function desisumma () { apu=($( eval echo \${$1[*]#*.})); lkm=${#apu[@]}; apu=$(printf "1%-16s+" ${apu[@]}); apu=$(echo ${apu// /0}); apu=${apu%+}; apu=$(($apu-$lkm*10000000000000000)); echo ;}

function kokosumma () { echo -n 'matriisin jäsenien kokonais-summa on: ';bpu=($( eval echo \${$1[*]%.*})); bpu=${bpu[@]}; echo $((${bpu// /+}+${apu:0: -16})).${apu: -16} ;}; time { desisumma koematriisi; kokosumma koematriisi ;}

time echo 'tarkistukseksi bc:n laskema summa   : '$( echo ${koematriisi[@]} | tr ' ' + | bc -l) 
tuloste on jotain tämän kaltaista (sillä jokaisella ajo-kerralla muodostetaan ensin uudet numerot):

yhteenlaskettavien joukko:

10427790765681.9314758936081  12280707219456.2504508814096   8195990282496.2488619831296   1249198336.1121513121          5861899530496.7871531640625
2520473760000.1421970391296   13448539200625.5185097880561   2025637716001.3743764484161   2457068790016.2229897104656    959512576.796594176
3787013300625.6439662447201   3491998578721.9660714532561    157351936.276922881           1642047467776.2449228130001    1536953616.1171350625
260144641.875213056           83521.187388721                705911761.126247696           395254161.322417936            157351936.1330863361
623201296.1275989841          9336104694016.11637711365281   252047376.875213056           1222830961.479785216           11840653050625.6044831973376
5580787292161.9926437890625   10000.38416                    3185853730816.7648371893761   12072122454016.4275978808336   163047361.1073283121
6327217036816.7146131900625   2356596484641.15122640998656   607573201.1026625681          2052941630481.9187452028561    2296318960321.1951955471376
221533456.221533456           2496554842401.8792909200656    2480703750625.5653761639696   1385858700625.5337948160000    130321.28561
2425818710016.11512204705296  7394054078401.13476652155136   373301041.418161601           1427186233201.2944999210000    1057187014416.3841600000000
112550881.303595776           15494111297536.4915625528641   1026625681.1445900625         1146228736.38416               13932481925376.2593262550321
14252505012001.3512479453921  11764241449216.2585098014976   2303789694976.2536514910736   6455847578896.4359848400625    1097199376.623201296
592240896.1171350625          14223186420496.2593262550321   2760652033441.15431729448976  362673936.1171350625           168896016.442050625
1861107122176.11738853292401  429981696.479785216            8550360810000.1396105301761   2266617569841.10312216477696   981506241.777796321
10106606869921.3700887165361  705911761.303595776            14641.28561                   835210000.104060401            1501725251601.8978179242321
9019744817521.2918114646001   7076456545921.1551160647936    260144641.875213056           2769228810000.1178883463696    547981281.244140625
759333136.688747536           3852587765601.1485512441856    14164684960000.1121144263281  655360000.108243216            11338204200625.5451216326656
815730721.181063936           3896774700625.6455847578896    130321.1026625681             104976.193877776               6504586067281.8854344140625
1387488001.373301041          50625.83521                    1365534810721.1749006250000   1121513121.373301041           1156418486161.8957450410000
6802837650625.5712654493456   2237205241441.2873716601616    1355457106081.14757890560000  1020150500625.6232012960000    937890625.1222830961
104976.136048896              11840653050625.6044831973376   577200625.562448656           916636176.454371856            13990263006736.3941340648961
136048896.1049760000          8118761230336.11117396444176   193877776.1196883216          236421376.429981696            855036081.174900625
1647857448721.3637263079921   3282314864656.6602890412881    181063936.741200625           11512204705296.2200843458576   174900625.151807041
136048896.1146228736          937890625.592240896            11239665258721.1618961043456  6652458343696.8273769005056    3797883801856.6602890412881
9314758936081.10427790765681  562448656.244140625            3341233033216.2094413889681   10243334671441.15904215784081  10000.65536
104060401.163047361           1032386052096.11968832160000   8312865305616.10875854196736  492884401.168896016            126247696.835210000
1121513121.1536953616         15936095936016.13004685652401  38416.10000                   7520406736896.6990080303376    50625.65536
2626114239841.3054399363856   1003875856.1222830961          3647809685776.2387176412401   5076013506001.1971847850625    1073283121.193877776
9208578670096.2418052990081   146410000.519885601            8752130560000.1448193221281   3637263079921.2025637716001    3963766428241.1712789917696
6184815851041.8650804500625   577200625.1445900625           1234134359056.4669488810000   688747536.1475789056           562448656.244140625
815730721.130321              1016096256256.3841600000000    959512576.466948881           130321.14641                   1165365589441.13032100000000
83521.981506241               14223186420496.2675975777281   981506241.1506138481          506250000.121550625            2394867671296.4205058789376
7611645084241.1912622616576   4275978808336.10828022035216   1171350625.592240896          28561.28561                    639128961.1536953616
3311675679601.13674678889041  104976.83521                   121550625.50625               577200625.533794816            1475789056.141158161
533794816.442050625           3214565712241.13961350047121   146410000.108243216           1003875856.1506138481          50625.130321


matriisin jäsenien kokonais-summa on: 527257947110132.9002487349303500

real   0m0,006s
user   0m0,004s
sys   0m0,002s
tarkistukseksi bc:n laskema summa   : 527257947110132.90024873493035

real   0m0,002s
user   0m0,001s
sys   0m0,003s


22
Katala juttu että BASH:ia väitetään kyvyttömäksi kuten seuraavasta pienestä näytteestä ilmenee - keskeneräistä tämä tosin vielä on muttei tässä viipyä ehdi:

Virheen rivinumeron tulostaminen on ehdottomasti tärkein asia virhe-rutiinin tehtävistä ja BASH-skripteissä sitä ei tulosteta. Perus-asennuksessa koneeseen kuitenkin tulee funktio nimeltään: command_not_found_handle - ja se toiminta on seuraava: kun ohjelmassa viitataan kirjoitus -tai muistivirheen takia sellaiseen jota ei ole niin BASH siirtää silloin toiminnan command_not_found_handle:lle joka tulostaa: command not found. Voit tulostaa tuon funktion käskyllä:
Koodia: [Valitse]
declare -f | grep -A 14 command_not_found_handle
Sieltä ilmenee että toiminta siirtyy koodiin tiedostossa: /usr/lib/command-not-found silloin kun se on olemassa - ja Ubuntussa on. Tiedoston koodi on Python3-koodia eikä se kuitenkaan osaa tulostaa virheen rivinumeroa. Mutta BASH osaa tulostaa millä rivillä virhe on tapahtunut - ja mikäli tuon command_not_found_handle tiedoston sisältö korvattaisiin BASH koodilla olisi kaikki toiminut aina - mutta kovalevyn systeemi-tiedostoihin kajomatta virherivin tulostamisen voi tällä-hetkellä aikaansaada vain lisäämällä BASH-skriptin alkuun kaksi yksirivistä funktiota:
Koodia: [Valitse]
function jup () { jxx=${BASH_LINENO[@]} ;}; jup
function command_not_found_handle () { apu="${BASH_LINENO[@]}"; echo 'virheen rivi on: '$((${apu##* }-$jxx+1)); [[ ${FUNCNAME[@]:1} ]] && echo funktio-pino:${FUNCNAME[@]} ;}
Koodia: [Valitse]

- mitään muuta ei tarvitse tehdä sillä tämänjälkeen kone hoitaa itse kaiken.
- tarvitaan kaksi funktiota sillä ${BASH_LINENO[@]} on suoritettujen rivien määrä päätteen avaamisesta lähtien ja ${BASH_LINENO[@]}:lla on arvo vain funktiossa oltaessa.
- tämä ilmoittaa rivinumeron silloin kun koodi viittaa johonkin jota ei ole - muu toiminta jää entiselleen. Ja mikäli virhe tapahtuu funktiossa niin tulostetaan funktio-pino.
- mutta funktio voidaan määritellä uudestaan vaikka jokaisessa skriptissä.
- sanaa function ei tarvitse funktio-määrittelyssä olla sillä tulkki ymmärtää muutenkin että kyse on funktiosta.
- BASH:illa ei ole rivinumeroita. Kyseessä on se rivinumero jonka editori pyydettäessä antaa - siis rivin järjestysnumero.
- siihenaikaan samaan muuttujaan ahdetiin mahdollisimman monta tietoa. Niinpä virheen tapahtussa funktiossa ensiksi tulee funktio-taso "${BASH_LINENO[@]":ssa mikäli virhe on tapahtunut funktiossa.
- funktio-tasolla on joku tajuttoman suuri maksimi joten funktio-pino voi olla pitkäkin.
- jos funktiossa tehdään virhe tulostuu kutsurivin rivinumero. Tämä johtuu siitä että kun skriptissä kutsutaan funktiota ei hypätä mihinkään vaan kopioidaan kutsutun funktion koodi kutsun paikalle. Tulkki muuten käsittää jokaisen skriptin yksirivisenä ja niinpä koko funktio on yhdellä rivillä - näet tämän hyvin kun skriptin suorituksen jälkeen painat nappulaa 'nuoli ylöspäin' - sieltä tulee koko skripti yksirivisenä. Muuten monilla on tapana lähettää toisille yksinomaan näitä yksirivisiä - niissä isokin skripti mahtuu yhdelle sivulle
.
Kokeile:
Koodia: [Valitse]
function jup () { jxx=${BASH_LINENO[@]} ;}; jup   
function command_not_found_handle () { apu="${BASH_LINENO[@]}"; echo 'virheen rivi on: '$((${apu##* }-$jxx+1)); [[ ${FUNCNAME[@]:1} ]] && echo funktio-pino:${FUNCNAME[@]} ;}
function koe () { matmax 1 2 ;}
#
c=0
koe    # ja koeta mitä tekee jos tässä ei kutsukaan funktiota vaan kirjoittaa suoraan: matmax 1 2
b=2
#

---

Mutta virhe-käyttäytyminen on sellainen miinakenttä että command_not_found_handle:a täytyy täydentää ikuisesti, esimerkiksi:
Koodia: [Valitse]
function command_not_found_handle () {
echo 'virheen rivi on: '$((${BASH_LINENO[@]}-$jxx+1))
# sitten koetetaan jos sana on jonkun tunnetun käskyn nimi jota ei vain koneessa ole ja jos se löytyy niin ladataan vastaava paketti paketti-järjestelmästä:
[[ $( apt-cache show "$komennon_nimi" | grep "Package:" ) ]] && { echo 'Koneessasi ei ole toimintoon tarvittavaa pakettia nimeltään:'$komennon_nimi'. Sen hakemiseksi tarvitsen salasanasi. Toiminnon jälkeen aja skripti uudelleen'; sudo apt-get install "$komennon_nimi" || echo tuntematon komento ;}
# mikäli olet tehnyt itsellesi kirjaston niin seuraavaksi etsitään kirjastosta:
[[ -x ~/OMATSKRIPTIT ]] && {
apu=("$(ls --hide=ARKISTO ~/OMATSKRIPTIT/FUNKTIOKIRJASTO )")
for n in ${apu[@]}; do
apu=$( grep $komennon_nimi ~/OMATSKRIPTIT/FUNKTIOKIRJASTO/$n)
[[ "$apu" ]] && echo "kirjoita skriptisi alkuun yksi näistä käskyistä: . ~/OMATSKRIPTIT/FUNKTIOKIRJASTO/$n/$komennon_nimi"
done ;}
}

23
Sain koottua desimaaliluvuista muodostettujen 2-250 rivisten matriisien summaimen joka on melkein yhtänopea kuin BASH:in matematiikka-ohjelmat. Tottakai skriptillä on lukemattomia puutteita koska se on ensimmäinen laatuaan ja lisäksi ottaa ensi-askeliaan - mutta tuskin niitä puutteita tulee korjattua sillä eiväthän tällaiset skriptit ole enää merkityksellisiä - ja tämähän osoittaa jo nytkin että BASH on matikassa todella nopea - siis se osaa kutsua C:n avukseen niinkuin muutkin.

Skriptin ainoa tarkoitus on varmistaa uskomus siitä että BASH:in matikka on aikoinaan tuhottu tahallaan - ei ole mahdollista että kymmenet käskyt vain 'sattuvat' toimimaan aina oikein kun BASH:issa mikään muu ei toimi temppuilematta.

---

Skriptissä lasketaan summa desimaaliluvuista kootusta matriisista. Testattaess muodostetaan matriisi satunnaislukujen avulla joten se on jokaisella ajokerralla hieman erilainen joukko - jalopuksi sen jäsenien järjestyskin sekoitetaan (käskyllä shuf). Testattavassa matriisissa on tässä toteutuksessa jäseniä noin 260 mutta määrä vaihtelee jonkinverran kerrasta toiseen (jäsenien lukumäärä otetaan automaattisesti huomioon, ei sitä laskea tarvitse. Desimaalien yhteenlasku on paljon vaikeampaa kuin kokonaisosien yhteenlasku ja siksi kokonaisia ei edes ole koodia sotkemassa):

Funktioon:matsum toimitetaan matriisista vain nimi koska silloin parametrien palautuksesta ei tarvitse välittää. Ainoa kieli joka osaa toimia näin on BASH - eivätkä muut kielet pysty tunnustamaan olevansa sekundaa.
Koodia: [Valitse]

function matsum () { # ensin lasketaan matriisin jäsenluku ja senjälkeemn tehdään matriisin jäsenien desimaaliosista tekstijono muokaten sitä samalla:
apu2=$(declare -p $1); declare ${apu2:8:2} apu=${apu2#*=} # koska matriisista on tullut vain nimi täytyy se ensin yhdistää niihin arvoihin jotka saman-nimisellä matriisilla on.
lkm=${#apu[@]}; apu2=$(printf "%-16s+1" ${apu[@]#*.}); apu2=${apu2[@]// /0}; apu2=1${apu2%??}; echo $apu2
# ja sitten vain työnnetään muodostettu tekstijono matematiikkamoottoriin ja tulostetaan tulokset:
apu3=$(($apu2)); summa=${apu3:0: -16}.${apu3: -16} ; summa=$((${summa%%.*}-${#apu[@]})).${summa#*.}; echo; echo 'matriisin summa='$summa'  matriisissa on tälläkertaa jäseniä:'${#apu[@]}'  matriisin jäsenten keskiarvo on:'.$((10#${summa//./}/${#apu[@]})) ;}

# esimerkkimatriisin muodostaminen ja esimerkki-kutsu:
echo; echo; echo ensin muodostetaan matriisi kokeilua varten - sen jäsenten pituudet, lukumäärä ja järjestys vaihtelevat erikerroilla. Tämänkertaisen matriisin jäsenien desimaliosat esitettynä tekstijonona:
koematriisi=($({ seq .0111111  .01$RANDOM 1; seq .1211111 .005$SRANDOM 1 ; seq .111111111111111 .055555555555555 1 ;} | shuf | tr , . )) 
echo; time matsum koematriisi
echo ; echo tarkistukseksi bc:n laskema summa - sillä bc:n oikeellisuuteen voi luottaa aina:$( echo ${koematriisi[@]} | tr ' ' + | bc -l)

---

Kommentteja ei kuulu kirjoittaa skriptin koodissa vaan dokumenttina skriptin jälkeen. Jonkunlainen dokumentti skriptin varsinaisesta sielusta:
Koodia: [Valitse]
apu2=$(printf "%-16s+1" ${apu[@]#*.})
jakaa desimaalisen matriisin apu jäsenien desimaaliosat matriisiin apu2 lisäten jokaisen jäsenen perään niin monta välilyöntiä että jäsenestä tulee 16-merkkinen - ja se lisää myös jokaisen jäsenen eteen numeron 1 jotta myös luvut joiden alussa on nollia laskettaisiin oikein (desimaalien alussa voi olla nollia) - paitsi ensimmäisen jäsenen eteen se ei lisää numeroa 1 joten se on itse lisättävä myöhemmin. Nämä lisäykset on huomioitava loppu-tulostuksessa. Käsky myöskin lisää jokaisen luvun välilyöntien perään + merkin.
Koodia: [Valitse]
apu2=${apu2[@]// /0}
muuttaa sitten printf:n modostaman tekstijonon välilyönnit nolliksi joten jokaisesta jäsenestä tulee 16 numeroinen. Kumma juttu ettå se jättää lukujen välissä olevat välilyönnit rauhaan - eikun samalta näyttäviä välilyöntejähän on useampaa sorttia ja ehkä tässä on kyse siitä.

- koska apu2:n viimeisenä merkkinä ei saa olla + poistetaan se käskyllä:
Koodia: [Valitse]
apu2=1${apu2%??}
ja sama käsky lisää tekstijonon ensimmäiseksi numeroksi 1 .
 

24
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!"

---

Onhan niitä parempiakin funktioita kuin seuraava mutta kannattaa esittää sillä se kertoo että vanhat käskyt osaavat toimia regex:ien kanssa ihan suoraan: kyseessä on skriptauksen 'ikuisuus-ongelman ratkaiseminen' - erottaa jonkun laitteen tulosteesta haluttu lukema:
- tulosteen ei saa olettaa noudattavan minkäänlaista rakennetta - täytyy varautua siihen että siellä voi halutun tekstin lisäksi olla vaikka mitä:
Koodia: [Valitse]
function etsi_arvo () { apu=${1#*$2}; apu=${apu%%[!-+.0-9]*}; [[ $apu ]] || { apu=${1#*$2[[:punct:]]}; apu=${apu%%[!-+.0-9]*} ;}; echo  $apu ;}
etsi_arvo ":::jännite215.61&'virta:-7.25 teho6.25 taajuus:123456.78" virta
- siis funktiokutsun viimeiseksi sanaksi (tässä: jännite) kirjoitetaan se jota haetaan. 
Koodia: [Valitse]
- lmaisu:apu=${1#*$2[[:punct:]]} - ihmiselle on ihan selvää mitä siinä yritetään - mutta on todella ihmeellistä että tulkki osaa tulkata sen oikein.
- lmaisun:apu=${1#*$2[[:punct:]]} voisi kirjoittaa myös: apu=${1#*$2?} 
[/code]

- mutta mitään semmoista ei voi tehdä joka ei kompastuisi johonkin.

---

Vanhojen käskyjen nopeus on tosiaan BASH:iksi tajuton - millisekuntien sijaan aletaan puhua mikrosekunneista. Mutuna sanoisin että vanhojen käskyjen kyvyt ovat kymmenesosa uusien käskyjen kyvyistä mutta koska ne ovat satakertaa nopeampia niin lopputulokseen päästään kymmenenkertaa nopeammin - ja vanhoista käskyistä voi tehdä kokonaisuuksia joiden kykyjä ei rajoita mikään muu kuin se mitä ohjelmoija osaa - ja ryhmä hidastuu vain vähän kun käskyjä tulee ryhmään lisää. 

Ja vaikka vanhoilla käskyillä ei ole kytkimiä niin kyllä niitä mukaella voi -> esimerkiksi seuraava lause erottaa tekstijonosta neljännen tavun ( looppi hidastaa toimintaa suorituskertojen lisäksi noin 10% mutta noin nopeaan välimuistin vaikutus on olemattoman pieni - siis ajat muuttuvat lineaarisesti kun loopin n muuttuu). Kun 1 sekunti jaetaan 100000:lla saadaaan 10 mikrosekuntia:
Koodia: [Valitse]
apu="1 2 3 4 5"; time  for n in {1..100000}; do apu=${apu#* * * }; echo ${apu%% *}; done


- siis käskyssä: apu=${apu#* * * } on '* '-ryhmiä yhtä vähemmän kuin minkä palasen se erottaa
- sen 10% saa määriteltyä käskyllä: apu="1 2 3 4 5"; time  for n in {1..100000}; do : ; done
- tuo käsky : on assemblerissa NOP elikä älä tee mitään. :  kestää noin 3 mikrosekuntia ja echo noin 6 - joten jos echo:n sijaan tulos kirjoitetaan muuttujaan niin nopeus nousee kaksinkertaiseksi, nopeimmillaan 9:ksi mikrosekunniksi.

---
 
Tai toinen tapa jakaa:
Koodia: [Valitse]
apu=6melkein_yhdentekevää_mitä_tässä_on_kunhan_on_jotakin2022; apu=${apu//[!0-9]/' '}; echo ${apu##* }     #-> 2022
apu=6melkein_ yhdentekevää_mitä_tässä_on_kunhan_on_jotakin2022; apu=${apu//[!0-9]/' '}; echo ${apu%% *}  #-> 6
- ^ toimii melkein samoin kuin !
- toki voidaan kirjoittaa myös: [!-+.e0-9] - ei se paljoa hidasta - mutta mitä enemmän tekee lisäyksiä jotta skripti toimisi erikois-tapauksissa oikein niin sitä useammmin kuitenkin karahtaa kiville. Jokatapauksessa tuolla [!-+.e0-9]:llä luvut voivat olla negatiivisia, desimaalilukuja tai tieteellistä esitysmuotoa.
- kyllä tuolla: melkein_yhdentekevää_mitä_tässä_on_kunhan_on_jotakin on rajoituksensa: sen ensimmäinen tai viimeinen merkki ei saa olla numero eivätkä ihan kaikki merkit kelpaa muutenkaan - ja ainakin jotakin siinä täytyy olla, vaikka vain välilyönti. 
- myös edessä tai perässä voi ollamitätahansa tekstiä kunhan siinä ei ole numeroita. Muodostuvat etu- ja perä-välilyönnit vain poistetaan - esimerkiksi
Koodia: [Valitse]
apu=6melkein_yhdentekevää_mitä_tässä_on_kunhan_on_jotakin2022jotain_muuta_tarinaa; apu=${apu//[!0-9]/' '}; apu=${apu%%+( )}; echo ${apu##* }    #-> 2022

***

- kaikki seuraavista ajoista ovat kone kohtaisia - nämä ajat ovat surkeasta läppäristä. Skriptit sensijaan toimivat missä koneessa tahansa.

Time-käskyn 1ms erottelukyky on uusille käskyille ihan riittävä - no melkein ainakin. Mutta vanhat käskyt vaatisivat vähintään 0.1 ms erottelukykyä jotta vanhoista käskyistä muodostettujen funktioiden nopeudesta saisi mielikuvan - ja joskus jopa 0.01 erottelukyky olisi tarpeen. Time-käskyä erottelukykyisempää käskyä ei kuitenkan ole mutta sellaisen voi tehdä itse - mutta se on kone riippuvainen joten time-käskyä kannattaa silti käyttää mikäli se näyttää enemmän kuin 1ms.

Tarkemman erottelukyvyn saa käskyn hetki=$(date +%s%N) avulla - se alkoi laskentansa kauan sitten ja on laskenut alkuhetken jälkeiset kellojaksot joten teoriassa se tietää tapahtumahetken nanosekunnin tarkkuudella - tämä on teoriaa mutta kuitenkin lyhyehkön ajan kuluessa se ei montaa nanosekuntia erehdy. Käyttöjärjestelmä se on kun aiheuttaa BASH:iille niin suuria horjahteluja ettei BASH tiedä kovin pitkään edes oikeaa millisekuntia - mutta 'nanosekunti'-kello pyörii kuitenkin kokoajan ja se on tarkinta mitä PC:sta löytyy.
- siis käskyllä: hetki=$(date +%s%N) BASH kysyy käyttöjärjestelmältä: mikäs nanosekunti on meneillään? Vastauksessa on 20 numeroa mikä riittää kertomaan mikä nanosekunti on meneillään vuodesta 1987 lähtien. Ja paljonko aikaa kysymykseen meneekään niin kahden kysymyksen väli on tarkka 'nanosekunnilleen'.

Ajoitetaanpa tällä menetelmällä nopea skripti:
Koodia: [Valitse]
function kerro9 () {
[[ $1 =~ \. ]] && { apu=${1%%[1-9]*}; apu=${apu##*.}; exp1=${#apu}; luku1=${1//0.$apu/0.} ;} || { apu=${1##*[1-9]}; exp1=-${#apu}; [[ $apu ]] && luku1=${1:0: -$exp1} || luku1=$1 ;}
[[ $2 =~ \. ]] && { apu=${2%%[1-9]*}; apu=${apu##*.}; exp2=${#apu}; luku2=${2//0.$apu/0.} ;} || { apu=${2##*[1-9]}; exp2=-${#apu}; [[ $apu ]] && luku2=${2:0: -$exp2} || luku2=$2 ;}
int1=${luku1%%.*}
int2=${luku2%%.*}
apu2=$(($int1*$int2))
kokonaisia=${#apu2}
tulos=$((10#${luku1//./}*10#${luku2//./}))
tulo=000000000000000000$tulos'00000000000000000000'
tulo=${tulo:0:$((19-$exp1-$exp2))}.${tulo:$((19-$exp1-$exp2))}
apu=${tulo##+(0)}; [[ ${tulo//[^.]/} ]] && apu=${apu%%+(0)}; echo ${apu%.}  ;}

function ajoita () { alku=0; alkuhetki=$(date +%s%N); sleep .1 ; loppuhetki=$(date +%%sN); alkuhetki=$(date +%s%N); $@ ; loppuhetki=$(date +%s%N); aika=$(($loppuhetki-$alkuhetki));  echo "toiminta \"$@\" kesti tälläkerralla: "$aika" ns" ;}

ajoita kerro9 0.000000000000000001234 1.234567809123456
saat ajaksi jotain: 2307981 ns

anna nyt käsky: time kerro9 0.000000000000000001234 1.234567809123456
Saat ajoiksi esimerkiksi:
real   0m0,001s
user 0m0,001s
sys   0m0,000s

Miksi ajat erovat sillä 'nanosekunti' menetelmä on taatusti tarkka - mutta niin on varmasti myös time-käsky. Niiden täytyy siis mitata eri asioiden aika.

Tarkka ajoitus laskee aikaan myös sen ajan joka kestää että BASH alkaa yleensä toimimaan - siis jotta time-käskystä saisi saman ajan täytyisi noita time-käskyn kolmea arvon laskea yhteen jollain painotuksella. Nyt ei kannata luottaa siihen 0.5s arvoon mikä arvoksi on tähänasti arvioitu vaan määritellä se itse. Koska kyse on onnettoman lyhyestä ajasta täytynee se mitata loopissa - se kestää mutta onpahan sitten tarkempi - ja toivonmukaan luotettavampikin:
- ajoitetaan siis käsky : joka kutsuu kyllä tulkkia mutta tulkki palauttaa koodin NOP - älä tee mitään.
Koodia: [Valitse]
alku=0; for n in {1..1000}; do alkuhetki=$(date +%s%N); : ; loppuhetki=$(date +%s%N); alku=$(($alku+$((10#$loppuhetki-10#$alkuhetki)))); echo "keskiarvo $n mittauksesta: $(($alku/$n)) ns"; sleep 1; done
Tulokseksi tulee jotain seuraavan kaltaista: keskiarvo tuhannesta mittauksesta: 1953962 ns. Sriptien ajoituksessa täytyy tämä huomiida korjaksena - mutta korjaustermi ei ole tämä vaan tämän puolikas:
Kun vähennetään: 2307981 - 1953962/2 saadaan: 1331000 - siis noin 1.350ms. Tuntuu oikealta.

---

Viimeajat olen yrittänyt saada kasaan kuvausta nano-sekunti menetelmän eduista mutta se on niin liukas käsiteltävä että tässävaihessa täytyy tyytyä esittämään siitä yksi taulukko:

  käsky         tulos-nano    tulos-time-real
sleep 1              1.002081         1.003
sleep .1             0.101887          .103
sleep .01           0.011309          .012
sleep .001         0.002221          .004
sleep .0001       0.001349          .003
sleep .00001     0.001349          .003

- tottakai totuus ei ole ihan tälläinen vaikka numerot ovatkin ihan oikein. Lisäksi nano:a on helpompi parantaa ja koneiden nopeutuessa se paranee itsekseenkin.


25
Väitetään ettei BASH:issa voi palauttaa parametreja. Virtuoosit esittivät lisäksi että lopulta tämä johtuu siitä ettei $x=jotakin onnistu silloinkaan kun $x:ssä on nimi.

Totta molemmat väitteet - ei onnistukaan. Mutta se mitä loppuksi halutaan on pystyä tekemään funktio jota voi kutsua samassa skriptissä eri muuttujilla ja funktio muuttaa aina oikeaa muuttujaa.

Toiminta jota $x=jotakin yrittää voidaan kuitenkin saavuttaa monilla muillakin tavoilla - painotus kohdassa monilla. Esimerkiksi:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}
mutta se voidaan kirjoittaa myös:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); export $1=${sana_matriisina[$2]} ;}
tai myös:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); declare -gx $1=${sana_matriisina[$2]} ;}
ja kaikilla niillä on omat erikois-ominaisuutensa.

Tarkistaa nuo funktiot voi käskyllä - vaihtaen tuota ensimmäistä numeroa joka tässä on 1:
Koodia: [Valitse]
etsi sana 1 lauseesta "9a 87b 654c 3(21)0d'ille!"; echo $sana
- nuo valittavien ympärillä olevat "" heittomerkit tarvitaan siksi että sanassa 3(21)0d'ille! on merkkejä jotka eivät muuten kelpaisi: ( ja ) ja ' ja ! . Jos niitä ei ole voi heittomerkit jättää pois.
- ja toiminnaltaan funktiot ovat tosi nopeita.
- read<<< voidaan kirjoittaa myös let mutta silloin valittavat voivat olla vain hyväksyttäviä kokonaislukuja joten parasta unohtaa se.
- muuten kun huomattiin että tuon read<<< paikalla toimii myös käsky: eval niin kiirehdittiin vakuuttamaan että eval on BASH:issa pahis - sillä noita toisia ei tunnettu joten sehän oli ensimmäinen joka tavallaan palauttaa parametrin ja olisi liian noloa myöntää että toimiihan se parametrin palautus.

***

Vanhoilla menetelmillä skriptauksen tuottavuus on niin alhainen että kesti kauan huomata ettei BASH:illa ole mitään rajoitteita - tai siis tavallaan on mutta kaikille puutteille löytyy useita keinoja kiertää ne - siis kieltämättä BASH on hankala kieli koska joutuu pähkäilemään kuinka mikin pitää koodata sillä varsinaisesti kyse ei ole kiertämisestä vaan siitä ettei BASH:issa ole automatiikkaa juuri millekään vaan kaikki on tehtävä itse. Koodi on kylläkin kummallisen oloista sillä se on sellaista kuin BASH aikoinaan oli.

Seuraavilla ohjeilla skriptaamisen tuottavuus nousee moni-kymmenkertaiseksi ja sehän se on joka merkitsee:

Kaikki skriptit kirjoitetaan samaan teksti-tiedostoon laittaen joku selvä erote skriptien otsikko-koodi-ja-koodin-dokumentointi kokonaisuuksien väliin. Vaikka tekisi kokopäiväisesti skriptejä kymmenen vuotta niin yksi tiedosto riittää useimmille sittenkin. Lausetta: #!/bin/bash ei missään nimessä saa kirjoittaa koskaan eikä hakea suoritusoikeuksia.

Skriptin saat käyttöösi kun luet tuon ison tiedoston johonkin editoriin ja leikkaamalla tarkoittamasi skriptin koodin - ja toteutettua sen saat kun liimaat leikkaamasi koodin päätteeseen ja painat return. Tai surffatessasi netissä kun kohtaat jollain verkkosivulla mielenkiintoisen skriptin voit kokeilla toimiiko se leikkaamalla nettisivulta sen koodin, liimaamalla se päätteeseen ja painamalla return - se ei ole tekijä-oikeuden loukkaamista - skriptin siirtäminen sellaisenaan omaan tiedostoosi saattaa joskus ollakin varsinkin jos et kerro lähdettä. 

Mikäli et muista missä skriptisi siinä isossa tiedostossa on niin käytössäsi on linuxin erinomaiset hakutyökalut - googlea ei edes voi käyttää mutta jos voisi niin se ei toimisi oikein sillä se löytäisi vain uusia käskyjä ja vanhoja käskyjä se löytää vain vahingossa - ja vanhat käskyt soveltuvat tiedonkäsittelyyn kertaluokkia paremmin kuin uudet sillä uudet käsyt on tarkoitettu tiedostojen käsittelyyn - elikä vanhat käskyt toimivat RAM:min kanssaja uudetkäskyt toimivat kovalevyn kanssa. Tuo iso tiedosto luetaan johonkin editoriin ja jokainen editori tuntee käskyn ctrl-f ja jo se on paljon parempi kuin google sillä se löytää mitävaan mitä tiedostossa on (esimerkiksi ##*$ ) ja lisäksi nopeasti. Mutta parempiakin haku-työkaluja kuuluu jo BASH:in käskyihinkin ja ulkoisia löytyy enemmän kuin jaksat koota tai edes lukea niistä kaikista - ja ne etsivät kirjaimellisesti mistävaan mikä alueeksi määrätään - semmoinenkin hakumoottori löytyy joka sallii sen että etsittävässä on kirjoitus virheitä.

---
 
Päätteessä on yksi skripti jo avatessasi sen: se on pääte itse ja kun kirjoitat tekstiä päätteeseen ei tarvitse vakuutella että se on BASH-koodia ja tarkoitus olisi ajaa se kun return:ia painetaan - pääte nimittäin olettaa niin ellei erikseen toisin käsketä - se että pääte on samalla myös pääte-ohjelma ei kuulu tähän.

BASH:in muuttujien näkyvyys-säännöistä vähäsen: pääte on skriptien isä ja niistä skripteistä jotka teet itse tulee sen lapsia - ja tekemäsi funktiot ovat pääohjelmasi lapsia. Ja BASH:issa lapset perivät isältään kaiken - mutta isä ei peri lapsiltaan noinvaan mitään vaan lapsen täytyy myöntää isälleen muuttujakohtaisesti perimis-oikeus käskyllä: export muuttujan_nimi - tai: declare -gx muuttujan_nimi - tai: käskyn etumääreellä eval - tämänjälkeen muuttuja tunnetaan skriptissä kaikkialla eikä sen  palauttamisessa olisi mitään mieltä sillä muuttuja on muuttunut jo.
- siis funktiossa oltaessa perimisoikeus myönnetään isälle joka yksinkertaisessa tapauksessa on päähjelma joten muuttuja tunnetaan senjälkeen kaikkialla skriptissä, myös toisissa funktioissa.
- siis ohjelmallisesti ei voi määrätä:  $x=jotakin edes funktiossa oltaessa vaikka $x:ssä olisi nimi. Täytyy tosiaan kirjoittaa: muuttujan_nimi=jotakin. Tai määrätä isälle perimis-oikeus.
- tästäsyystä BASH-skriptissäei ole lopussa: END - se on isän hommaa ekä lasten

---

Päätteellä on ilmanmuuta suoritusoikeus ja shebang ( esimerkiksi #!/bin/bash ) joten niitä ei saa itse enää antaa - shebang täytyy antaa vain mikäli BASH:in sijasta ajetaan jotain muuta skriptikieltä. Mutta itseasiassa koko shebang on salahautojen viidakko.
- ei tarvitse välittää siitä että ubuntun BASH on kuulemma DASH.
- jos saat kasattua skriptin jolla on todellista käyttöäkin niin sitten siitä kannattaa tehdä siitä skripti sillä vanhalla ja vaivalloisella tavalla - sillä on sillä etujakin - jos on haittojakin. Minä olen onnistunut vain parikertaa tekemään sellaisen skriptin ja onhan näitä skriptejä jo tuhansia - tosi-isojakin. Ja niistäkin ainoa skripti jolla on päivittäin käyttöä on skriptiajuri, muut ovat näytös-luonteisia.

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

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

28
Sain BASH:in funktio-parametrien siirrosta tehtyä uuden version:
- näiden 'ei BASH osaa' menetelmien esittäminen on päättymätön suo sillä uusia huomioita tulee kokoajan joten dokumentin valmistuttua se on yleensä jo aikalailla vanhentunut.

1. Alkeellisin tapa toimittaa parametrit funktioon on lähettää sinne funktioon parametrien arvot. Yksinomaan tätä tapaa käytetään nykyään. Pahimmat ongelmat niiden kanssa ovat ne että niiden avulla voi funktioon siirtää vain yhden matriisin jonka tulee lisäksi sijaita parametrilistan lopussa, parametrinumeroiden päätteleminen on taidetta kun funktiosta siirrytään toiseen funktioon ja lopuksi se ettei arvo-parametreja voi 'palauttaa'.
 
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 skriptissä joka etsii matriisista sen suurinta tai pienintä arvoa tuo automatiikka on: apu=$(declare $1). Tarkistetaan samalla että arvo-parametreilla tulee sama tulos kuin nimi-parametreillakin (kaikki kolme lausetta voi kopioida-liimata yhdellä kerralla sinne päätteeseen).
- tämä yksikertainen keino on kyllä hieman likainen, sillä matriisiin täytyy liittää ylimääräinen jäsen sillä funktiossa käsky: apu=$(declare $1) jättää ensimmäisen jäsenen pois ja sen kykenisi korjaamaan vain Chet Ramey, BASH:in kehittäjä. 

Esimerkiksi matriisin tai tekstijonon maksimin tai minimin löytävä funktio:
Koodia: [Valitse]
apu=(¤ {1..9}) # tämä käsky muodostaa testattavan matriisin.
function haemaksimi () { echo -n "nimiparametrilla:  "; apu=$(declare $1) ; sort -g <(printf "%s\n" "${apu[@]}") | sed '/^$/d' | head -1 ;}; time haemaksimi apu
tarkistetaan että se silti laskee oikein:
Koodia: [Valitse]
apu=(¤ 5 4 3 2 1 2 3  5 6) # tämä käsky muodostaa testattavan matriisin.
function haemaksimi () { echo -n "nimiparametrilla:  "; apu=$(declare $1) ; sort -g <(printf "%s\n" "${apu[@]}") | sed '/^$/d' | head -1 ;}; time haemaksimi apu
isommilla matriiseilla skripteistä tulee esimerkiksi seuraavanlaisia:
Koodia: [Valitse]
apu=(¤ $(awk -v n=600000 -v seed="$RANDOM" 'BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.14f\n", rand()*10000000000) }')) # tämä käsky muodostaa testattavan matriisin.
function haemaksimi () { echo -n "arvoparametreilla: "; sort -g <( printf "%s\n" "$@") | sed '/^¤$/d' | head -1 ;}; time haemaksimi ${apu[@]}
function haemaksimi () { echo -n "nimiparametrilla:  "; apu=$(declare $1) ; sort -g <(printf "%s\n" "${apu[@]}") | sed '/^$/d' | head -1 ;}; time haemaksimi apu
Toinen tarkistus täysin erityyppisellä datalla - nyt kylläkin aakkostetaan (muuten vaikka dataa muodostetaan vähemmän kestää se silti paljon kaummin):
Koodia: [Valitse]
apu[0]=¤; for n in {1..1000}; do apu[$n]=$(cat /dev/urandom | base64 | head -c 100) ; done
function haemaksimi () { echo -n "arvoparametreilla: "; sort -g <( printf "%s\n" "$@") | sed '/^¤$/d' | tail -1 ;}; time haemaksimi ${apu[@]}
function haemaksimi () { echo -n "nimi-parametrilla: "; apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; sort -g <(printf "%s\n" "${apu[@]}") | tail -1 ;}; time haemaksimi apu

Pidempi ja kaksikertaa hitaampi mutta moitteettomampi menetelmä on seuraavanlainen:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; echo ${apu2[@]} ;}; matrix=(1 2 3 4 5 6 7 8 9);  mitrix=(7.7 joo); koe matrix; koe mitrix; koe matrix

Nimiparametrit vaativat lisää koodia jokaista siirrettyä parametria kohti - mutta nimi-parametrejä voi käyttää vain kun se on välttämätöntä ja muuten käytetään arvo-parametreja  (siis niitä voi käyttää sekaisin - eikä suoritus-ajoissakaan ole yleensä isoa eroa). 
- sama funktio löytää sekä maksimin että minimin. maksimi on: tail -1    ja minini on: head -1.
- ilmoitettu aika on funktiossa kulunut aika - se pitkä aika kuluu testi-matriisin muodostamiseen.
- kun funktio määritellään uudestaan vaikka samassa skriptissä korvaa uusi määrittely välittömästi sen vanhan. Se täytyy takistaa erikseen että uusikin funktio-määrittely onnistuu, muuten tulee hiljainen virhe - joka yleensä karjuu inhottavasti jotain ihan ihmeellistä.
- dokumentointi on tärkeämpää kuin itse koodi. Mutta näiden 'ei BASH osaa'-skripteillä ennenkuin dokumentoidaan mitään niin täytyy osoittaa että kyllä osaa ja vasta kauan senjälkeen tilanteen hieman rauhoituttua tulee dokumentoinin vuoro.
- kunnollisessa dokumentoinnissa on valtava työ - paljon-paljon suurempi kuin koodin tekemisessä. Ja miksi dokumentoida jotakin jota on vääristelty 30 vuotta ja joka sentakia kehittyy nyt niin nopeasti että koodi muuttuu päivittäin hieman erilaiseksi kuin ennen - joten kun saa dokumentoinnin kuntoon niin uusi koodi-versio valmistuu välittömästi jolloin vanha dokumentointi lentää roskikseen ja aletaan tekemään uutta dokumentointia. Joten vain harvoin pääsee koodaamaan kun melkein aina tekee dokumenttia - joka yleensä lentää roskikseen jo ennen kuin homma toimii ihan kunnolla.
- kyllä jonkinsortin dokumentti täytyy tehdä - mutta se on vain itselle omien ajatuksien selventämiseksi. Se jonkinlainen omaan käyttöön tarkoitettu dokumentti funktiosta: 
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; echo ${apu2[@]} ;}

- apu1=$(declare -p $1) laittaa apu1:n arvoksi esimerkiksi: declare -a matrix=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9")
 Siitä saa kunnollisen matriisin toteuttamalla sen. Toteuttaminen tapahtuu declare-käskyllä. Mutta declare täytyy kirjoittaa sillä apu1:ssä oleva sana: declare ei kelpaa. Tuo -a täytyy laittaa mukaan (ja sen paikalla voi lukea 'vaikka mitä' joten sen paikalla oleva täytyy noukkia tulosteesta). Lisäksi täytyy laittaa sanan matrix tilalle sana: apu2.

***


Kommentoinnin tärkeydestä: vain koodi on koneen kannalta merkityksellistä - esimerkiksi skriptin tulkkaaminen alkaa kommenttien strippaamisella sillä koneen kannalta ne ovat ainoastaan hidaste. Koodarillekin kommentoinnin tekeminen on hidastava lisätyö jonka takia myös yleiskuvan saaminen on hankalampaa.

Käskyt ovat nekin jonkun ihmisen tekemää koodia joita hän on dokumentoinut - eikä senkään koodin toiminta välttämättä ole ihan se mitä dokumentti väittää. Sitten joku tekee käskyistä skriptiä jonka hän dokumentoi. Siis se joka skriptin dokumentit lukee tulkkaa samaa asiaa kolmatta kertaa ja virheet tulkkaamisessa ovat ainakin kasaantuvia - mutta ehkåpä jopa kertautuvia tai peräti eksponentiaalisia. Siltä ei voi siis välttyä että seinkin menee vähän metsään.
Miksi siis tehdä jotakin joka on varmasti vähän väärin?

Kommentti kertoo sen mitä koetetaan saada kone tekemään elikä mitä luullaan käsketyn - mutta koneen toimintaan ne eivät vaikuta. Siis teoriassa ensin homma määritellään, sitten se koodataan, sitten kokeillaan saatiinko koodi toimimaan oikein ja tehdään kokeiden perusteella dokumentti - mutta epäilempä että yleensä kokeita ei juurikaan tehdä ja määrittelyt vaan nimetään dokumentiksi.

Väitetään koodin kommentointi on välttämätöntä myös opetuksen takia mutta pitääkö sekään paikkaansa? Sillä päähän kaadettu tieto häviää nopeasti eikä sitä yleensä osata soveltaakaan - mutta itse 'itkien' opeteltu pysyy mielessä ikuisesti eikä sen soveltamisessakaan ole vaikeuksia.

Samoin luullaan että koodia kannattaisi korjata ja päivittääkin joskus mutta halvempaa olisi tehdä uusi - tosin koska maailma toimii siten että kaikki hinnoitellaan yksinomaan sillä perusteella kuinka laadukas työ se on - joten koska päivitetty on vain pikkuisen parempi on se myös paljon halvempi.

Kunnollisen dokumentin tekemisessä on valtava työ - paljon-paljon suurempi kuin koodin tekemisessä. Ja BASH:in dokumentoiminen on vielä vaikeampaa koska sen parhaita ominaisuuksia on piiloteltu 30 vuotta. Kun tekee 'BASH ei osaa'-skriptin täytyy ennen dokumentointia osoittaa että kyllä BASH sittenkin osaa ja vasta senjälkeen dokumentointi on teoriassa mahdollista.

Dokumentoinnissa tulee loistavasti esiin se että paras on hyvän vihollinen - elikä jos selitetään ihan liian tarkkaan on se yhtä paha kuin jos asioita ei selitettäisi ollenkaan.

Jos ei esitetä esimerkkejä niin dokumentilta katoaa uskottavuus ja voisi yhtähyvin olla dokumentoimatta. Ja esimerkin yksi puoli on kokeilla ratkaisun toimivuus kaikissa vaikeissa tilanteissa - joskus riittää yksi koe mutta joskus tarvitaan lukemattomia kokeita.

Esimerkiksi oletkos lukenut päätteen ohjeen? Kaikkien käyttämiesi editorien ohjeet ? Ne ovatkin isoja raamattuja joten tosi-harva on viitsinyt ne lukea - eikä yksi lukukerta riitä alkuunkaan vaan vuosia niitä täytyy selailla.

Kun saa skriptin dokumentoinnin kuntoon niin uusi koodi-versio valmistuu melkein välittömästi jolloin vanha dokumentointi lentää roskikseen ja aletaan tekemään uutta dokumentointia. Joten vain harvoin pääsee koodaamaan ja kehittäminen hidastuu matelemiseksi kun melkein aina tekee dokumenttia - joka yleensä lentää roskikseen jo ennen kuin homma toimii ihan kunnolla.

Lisäksi ellei kukaan lue dokumenttia niin mitä mieltä sen tekemisessä on? Esimerkiksi oletkos lukenut päätteen ohjekirjan (~ dokumentoinnin)?  Käyttämiesi editorien ohjekirjat? Ketä nuo paljon koodaamista työläämmin aikaansaadut dokumentit ovat ketään hyödyttäneet? Kummasta pidät enemmän: sosiaalisesta elämästä vai koodaamisesta? Älä vaadi toiselta sitä mihin et itse kykene.

- kyllä jonkinsortin dokumentti täytyy silti olla - mutta se on vain itselle muistin-tueksi ja omien ajatuksien selventämiseksi. Esimerkiksi se jonkinlainen omaan käyttöön tarkoitettu dokumentti matriisin tulostavasta funktiosta (joka oikeastaan kuvaa nimi-parametrin käyttämistä):
Koodia: [Valitse]
function echomatrix () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; echo ${apu2[@]} ;} # tai monirivinen tulostus: printf "%s\n" ${apu2[@]}
Tämän lyhyt dokumentti on seuraavanlainen:
Käsky matrix=(1 2 3 4 5 6  8 9) aikaansaa BASH:in kirjanpitoon rivin: declare -a matrix=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9")
Tuon tulosteen saa luettua muuttujaan esimerkisi käskyllä: apu1=$(declare -p matrix)
Tämän saa palautettua matriisiksi toteuttamalla sen. Toteuttaminen tapahtuu declare-käskyllä. Mutta declare täytyy kirjoittaa sillä apu1:ssä oleva sana: declare ei kelpaa. Tuo -a täytyy laittaa mukaan ja koska sen paikalla voi lukea 'vaikka mitä' niin sen paikalla oleva täytyy noukkia tulosteesta. Lisäksi täytyy uudelleen-nimetä tullut muuttujanimi esimerkiksi apu2:ksi.
 
- dokumentointi on tärkeää siksikin että tehdessäsi dokumenttia selvität tapahtumia itsellesi ja se johtaa usein siihen että saat koodista nopeampaa ja saatat peräti löytää paremman menetelmänkin.

- kommenttien merkitys on siis epäselvä mutta esimerkkien arvo on kiistaton - siis esimerkkejä täytyy olla mutta ilman dokumentointiakin selviää - ainakin jotenkin. Ja jokainen skripti on esimerkki.

29
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?

30
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

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

***

Todella mielenkiintoinen tuo vanhojen komentojen joukko, mutta se että ne ovat kymmeniä kertoja uusia käskyjä nopeampia merkitsee vähän - se mikä todella merkitsee on että ne toimivat aina samalla tavalla - siis niillä skriptit onnistuvat kerralla ja uusilla käskyillä skriptejä joutuu korjailemaan 'kymmenen vuotta' - asian voisi esittää niinkin  että uusilla käskyillä on lukemattomia täysin erilaisia tapoja saada skripti toimimaan ja vasta monien korjausten jälkeen löytyy se moitteeton tapa - kun sensijaaan vanhoilla käskyilläjoko onnistuu tai ei - ei tarvitse olla löysässä hirressä.

Toinen tapa kertoa sama asia: vanhat käskyt ovat paljon alkeellisempia kuin uudet käskyt, joten tarvitaan suuri joukko vanhoja käskyjä tekemään sama kuin yksi uusi. Mutta vanhojen käskyjen nopeudesta seuraa se että tuo suuri vanhojen joukko toimi silti paljon nopeammin ja niitä voi lisätä joukkoon niin monta että loputa toiminta tyydyttää - nopeuden säilyessä.

Koneen hoitaminen on BASH:issa paljon monipuolisempaa ja helpompaa kuin muissa kielissä yhteensä - ja monia erilaisia käskyjähän siihen tarvitaan. Sitten vielä kehittyneet hakumentelmät, netin käyttö, kursorin käsittely, hiiren käsittely, sed, awk, bc ja kaikki kummalliset käskyrakenteet ... 

Koska BASH:issa on siis monia täysin erillisiä tajuttoman monipuolisia toimintoja oli liiallinen monipuolisuus ainakin jonkinlainen syy pyrkiä BASH:ista eroon. Eroa on kokoajan yritetty saada aikaan uskottelemalla ettei BASH osaa vaikka varmasti on tiedetty että kyllä osaa - silti mitään käsinkosketeltavaa ei ole tehty sillä koska jos itse ei tehdä mitään niin eihän siitä voi syyttää että toiset tulkitsevat sanasi väärin? Yksi keino oli lisäksi helppo vaikka silti törkeä: virtuoosit lakkasivat puhumasta noista loistavista vanhoista käskyistä - joten niitä käytetään päivä-päivältä harvemmin ja lopulta unohdetaan ne. Tosiaan ainoastaan vaietaan: noita vanhoja ole koskaan edes väheksytty - niistä vaan ei puhuta. Aivan varmasti vanhat käskyt pysyvät kuolleina, eivät nämä minun puheeni mitään auta.

Mutta koska tosiasia on että käskyjä on kamalan paljon niin vaihtoehtona voisi helpottaa niiden käyttöä muodostamalla toiminnoista taulukoita ja yhteenvetoja sekä tehdä runsaasti esimerkkejä ja keinoja tulosten oikeellisuuden tarkistamiseen. Ja siinä on niin paljon tehtävää että varmasti hyytyy ennenkuin loppuun pääsee joten kukaan ei ole uskaltanut yrittääkään.

Kokosin aikoinaan teoria-ohjeet kuinka sed, awk ja semmoiset toimivat mutta huomasin nopeasti etteivät teoriat pure minuunkaan vaan aina joutuu tavaamaaan teorioita kauan ennenkuin saa tehtyä edes jotakin - ja eiköhän näin ole kaikkien taviksien kanssa - ja varmaankin useimpien virtuoosienkin. Elikä ominaisuus-listoja ja niiden perusteella tehtyjä esimerkkejä ja niiden testausta tarvitaan - esimerkit ymmärtää paljon helpommin ja niiden väärin-ymmärtäminen on paljon vaikeampaa - vaikka toisaalta niitä tarvitaan paljon enemmän.

En käsitä miksi man-sivuilla ei ole esimerkkejä juuri ollenkaan eikä BASH-raamattu ole paljoakaan parempi - on nähty kamala vaiva ja sitten itse sabotoitu se - sillä toiset virtuoosit kyllä ymmärtävät mitä niissä sanotaan mutta taviksille asiat selkenevät tosihitaasti ja kaikki ei joskus selkene koskaan - ehkä virtuoosille on mahdotonta ymmärtää että tavikset BASH:ia kannattelevat. Toisaalta eipä sillä ole väliä sillä uudet käskyt ovat usein käyttökelvottomia muutenkin - mutta silti kymmenien mies-työvuosien vaiva on tehty. Virtuoosit tekivät ihan saman hölmöyden kuin minäkin alkuun tein - minä tunnustan mutta virtuoosit eivät.

Mutta prototyyppien tekemisessä BASH loistaa jokatapauksessa nyttenkin vaikkei mitään tekisikään ja jopa uudet käskytkin toimivat niissä jotenkin.

***

Funktioidn toiminnan tarkistaminen on melkoinen urakkaja kun se on tehty olisi syytä saada funktiot esimerkkiensä kanssa talteen 'ikuisiksi ajoiksi'. Esimerkiksi etsi-funtion tarkistaminen jossa jokainen alihuomiokin on hidas tarkistaa - eikä riitä että tarkistaa jokaisen alihuomion erikseen vaan myös sen että ne toimivat keskenään:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}; etsi sana 3 lauseesta "9a 87b 654c 3(21)0/d'ille!"; echo $sana  # siis numero 3; paikalle kirjoitetaan monesko sana ja lainausmerkkien väliin lause josta etsitään.
Huomioita:
- lainausmerkit 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.
- etsittävä voi olla muuttujassakin.
- huomioi muuten että BASH:issa matriisin ensimmäinen jäsen on jäsen-numeroltaan 0
- edellisen esimerkin sana voi olla tekstijono josta ei aikaisemmin koskaan ole puhuttu - tai vaikka toisentyyppinen muuttuja jollainen se aikaisemmim oli, esimerkiksi:
declare integer sana; etsi sana 2 lauseesta "9a 87b 1+2 3(21)0d'ille!"
tulostaa tekstijonon 1+2 eikä 3 miksi integer määrittely se tekisi.
- tällaisen nopean skriptin suurta nopeutta ei voi time-käskyllä 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: 
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}; time { for n in {1..100000}; do etsi sana 3 lauseesta "9a 87b 1+2 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ä.
- jos raakaa nopeutta haluaa niin tässä on 20 mikrosekuntia kestävä versio, mutta sillä ei ole kaikkia ominaisuuksia:
function hae_tavu () { sana_matriisina=(${@:2}); echo ${sana_matriisina[$1]} ;}; hae_tavu 3 "9a 87b 654c 3(21)0d'ille!"
- muuten funktion toimintaa tarkistettaessa täytyy funktio kääntää erikseen ja vasta myöhemmin sen ajo-ojelma tai vaarana on se ettei funktion uusi versio toimi ja mikäli samaan käskyyn laittaa ajo-ohjelman käytetäänkin jotain aikaisemmin määriteltyä funktio-versiota. Tai voihan sitä jokaisella kerralla avata uuden päätteen.

---

Vanhat käskyt osaavat toimia regex:ien kanssa ihan suoraan: kyseessä on skriptauksen 'ikuisuus-ongelman ratkaiseminen' - erottaa jonkun laitteen tulosteesta haluttu lukema.
- tulosteen ei saa olettaa noudattavan minkäänlaista rakennetta - täytyy varautua siihen että siellä voi halutun tekstin lisäksi olla vaikka mitä ja että tekstiä voi tulla kuinkapaljon tahansa. Toiminnan saa aikaiseksi esimerkiksi:
Koodia: [Valitse]
function etsi_arvo () { apu=${1#*$2}; apu=${apu%%[!-+.0-9]*}; [[ $apu ]] || { apu=${1#*$2[[:punct:]]}; apu=${apu%%[!-+.0-9]*} ;}; echo  $apu ;}
etsi_arvo ":::jännite215.61&'()/\nvirta:-7.25 teho6.25 taajuus:123456.78" virta
- siis funktiokutsun viimeiseksi sanaksi kirjoitetaan haettavan arvon nimi. 
- ilmaisu:apu=${1#*$2[[:punct:]]} - ihmiselle on selvää mitä siinä yritetään - mutta on todella ihmeellistä että tulkki osaa tulkata sen oikein.
- ilmaisun:apu=${1#*$2[[:punct:]]} voi usein kirjoittaa myös: apu=${1#*$2?} 
- tuo \n on rivinsiirto , siis tekstiävoi tulla monta riviäkin.
- mitään semmoista ei voi tehdä joka ei kompastuisi johonkin mutta jotkut kompuroivat kokoajan ja jotkut hyvin harvoin.

***

- arvo:n laatumääre tulostuu myös jos funktiota muuttaa mutta silloin laitten tulostuksessa täytyy laadun jälkeen olla välilyönti:
Koodia: [Valitse]
function etsi_arvo () { apu=${1#*$2}; apu=${apu%% *}; [[ $apu ]] || { apu=${1#*$2[ \:]}; apu=${apu%% *} ;}; echo  $apu ;}
etsi_arvo ":::jännite215.61Vac &'virta:-7.25kA teho6.25MW taajuus:123456.78Hz" virta
Koodia: [Valitse]
function etsi_arvo () { apu=${1#*$2}; apu=${apu%%[!-+.0-9]*}; [[ $apu ]] || { apu=${1#*$2[[:punct:]]}; apu=${apu%%[!-+.0-9]*} ;}; echo  $apu ;} 
 etsi_arvo "{"emeter":{"get_realtime":{"voltage_mv":232512,"current_ma":2607,"power_mw":496678,"total_wh":1395922,"err_code":0}}}" mw

32
Onneksi vanhoja käskyjä ei ole tuhottu - luulisin että haluja on ollut mutta kykyjä ei. Jokatapauksessa ne ovat seuraavankaltaisia tekstijonojen käsittelyssä ( mutta niitä on siis 'miljoonia' ja tässä vain pieni näyte):
Koodia: [Valitse]


merkkijono=123; echo "${merkkijono:mistä:montako merkkiä}         # määrät voi ilmoittaa numeroina tai muuttujina
merkkijono=123; echo "${merkkijono:: -montakolopustapoistetaan}"  # tulostaa: 12 (tai:echo "${merkkijono%?}"
merkkijono=123; echo "${merkkijono%3}"                            # ainoastaan perässä oleva 3 poistetaan mutta mikätahansa muu saa jäädä
merkkijono=12343; echo "${merkkijono%?43}"                        # tulostaa: 12 (tai:echo "${merkkijono%[0-9]43}")
merkkijono=123; echo "${merkkijono: -montakolopusta tulostetaan}"
merkkijono=123; echo "${merkkijono:0:montakoalusta tulostetaan}"  # eli: "${merkkijono:monennestako aloitetaan:kuinka monta}"
merkkijono=123; echo "${merkkijono:montakoalusta poistetaan}"
merkkijono=xabyabz; echo ${merkkijono/ab/acc}    # tulostaa: xaccyabz  -> korvaa vain kerran alusta - korvaava ja korvattava voivat olla eripitkiä
merkkijono=xabyabz; echo ${merkkijono//ab/acc}   # tulostaa: xaccyaccz -> korvaa kaikki
merkkijono='9 8 7 5 4'; echo ${merkkijono//8*5/} # tulostaa: 9 4       -> tässä * on jokerimerkki, eikä kertomerkki
merkkijono='9404'; echo ${merkkijono//$((8*5))/} # tulostaa: 94        -> nyt * on kertomerkki

merkkijono='9 8 7 5 4'; echo ${merkkijono%% *}   # tulostaa: 9       
merkkijono='9 8 7 5 4'; echo ${merkkijono% *}    # tulostaa: 9 8 7 5 ja echo ${merkkijono% * *}   tulostaa: 8 7 5
merkkijono='9 8 7 5 4'; echo ${merkkijono##* }   # tulostaa: 4
merkkijono='9 8 7 5 4'; echo ${merkkijono#* }    # tulostaa: 8 7 5 4

merkkijono='987km/t'; echo ${merkkijono#*[!0-9]} # tulostaa: 987 elikä ensimmäiseen ei-numeroon asti joten sama toimii millä kielellä vaan.
merkkijono=$( skripti joka tulostaa monta riviä); echo ${merkkijono##*[[:cntrl:]]} # tulostaa viimeisen rivin.
merkkijono='9 8 7 5 4'; echo ${merkkijono% * *} # tulostaa: 9 8 7 . Tämmöisiä koottuja voi muodostaa ohjelmallisestikin
merkkijono='9 8 7 5 4'; a=' '; echo ${merkkijono%%$a*} # tulostaa 9 - elikä jako voidaan tehdä myös muuttujan arvon avulla
 
- jakopisteen merkkien lista on esimerkiksi [- .:/] ja mikätahansa niistä kelpaa -> merkkijono='9:8:7:5:4'; echo ${merkkijono%%[- .:/]*}
- esimerkki jossa jakopisteenä on sana:
jakavasana=next; merkkijono='9next8next7next5next4'; [[ "$merkkijono" =~ "$jakavasana" ]] && echo ${merkkijono%%"$jakavasana"*} # tulostaa: 9
Ja vaikka merkintä on pitkä on se edelleen nopea.

- luulisinpa että se 'vallankumouksellisen hyvä' idea siitä että jokainen käsky käy läpi kymmen laajennosta ennen tulkkausta onkin vanhojen käskyjen perua. Tosin niille laajennus-sääntöjen täytynee olla aivan toisenlaisia - harmittaa kun tietää että niiden sääntöjen täytyy olla jossakin - mutta tuskin sellaisen asian sääntöjä löytää josta ei juuri koskaan puhuta. Mutta jonkun pöytälaatikossa ne on.

---

Joten tästä ryhmästä voidaan muodostaa seuraavat funktiot (siis määritellä uudet käskyt):
Koodia: [Valitse]
otaeka () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono%%$erottaja*} ;}
# kutsuesimerkki: otaeka '9 8 7 5 4' tai: otaeka '9 8 7 5 4' erottavasana
#
# siis mikähyvänsä muuttujan:erota merkeistä voi toimi erottajana.
# muuttuja:erottavasana sensijaan voi olla tosiaan sana. Sitä ei tarvise kutsussa olla mutta jos se on niin sitä käytetään muutta jos ei ole niin sitten käytetään merkkejä.
# rakenne:x if-then-else vanhentui - vastaava merkintä nykyään on tuo tässäkin oleva: [[ $2 ]] && 'joko hypi' || 'tai tärise'. Ja koko ehto on aina samalla rivillä.
## on kommentin lisä-selvennys. Joten ### on seli-seli-seli
## [[ $2 ]] on se if - elikä rakenne [ $2 ] vaheni myös. Se on ehto: onko-olemassa - mikähyvänsä teksti on jotakin joten myös merkki 0 on jotakin - ainoastaan '' on ei-mitään
## (( $2 )) olisi matemaattinen if: onko matemaatista arvoa vai onko se 0. Vain numeroita voi testata näin.
## && on then ja || on else

function otavika () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono##*$erottaja} ;}
function poistaeka () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono#*$erottaja} ;}
function poistavika () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono%$erottaja*} ;}
# kutsuesimerkki ja muut kommentit näissä kolmessa ovat samat kuin otaeka:ssa. Nämä yksittäiset käskyt ovat tosi-nopeita.

----

Ja näistä saa kasattua käskyn poimijoku:
Koodia: [Valitse]
otaeka () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono%%$erottaja*} ;}
poistaeka () { merkkijono=$1; [[ $2 ]] && erottaja=$2 || erottaja='[- .:/]'; echo ${merkkijono#*$erottaja} ;}
poimijoku () { merkkijono=$1; apu=$1; for (( n=1; n<=$(($2-1)); n++ )); do apu=$(poistaeka "$apu"); done; otaeka "$apu" ;}
Hidashan tämmöinen poimijoku on ja turhan monimutkainenkin mutta saihan sen tekemisestä oppiakin. Kymmenenkertaa nopeampi ja yksinkertaisempi on:
Koodia: [Valitse]
poimijoku () { apu=($1); unset IFS; echo ${apu[$2-1]}; unset IFS ;} # IFS kertoo mitkä merkit tulkitaan merkitsevän sananväliä - joka oletuksena on välilyönti.Se on aina joku yksi merkki annetusta listasta - siis tavallaan kokeillaan kunnes joku kelpaa. Esimerkkikutsu:
poimijoku () { IFS='-.:/¤ ' ; apu=($1); unset IFS; echo ${apu[$2-1]} ;}; time poimijoku '987654321-87654321 7654321.54321:4321/321&21¤1' 7 # tulostaa 1
mutta IFS on sikäli huono ettei se voi olla sana - ja toisekseenkin leikit IFS:n kanssa menevät joskus pahasti pieleen kun unohtaa lopusta: unset IFS:n. Seuraava hyväksyy erotusmerkiksi sanan - mutta se on hidas:
poimijoku () { apu=$(echo ${1//$2/' '}); apu=($apu); echo ${apu[$3-1]} ;}
# esimerkkikutsu:
poimijoku '987654321next87654321next7654321next54321next4321' poimijoku next 3
# apu=($apu) ei ole väkivaltaa sillä jokainen BASH:in tavallinen muuttuja on samalla myös matriisi joka on määritelty näin.
# kohdassa: echo ${apu[$3-1]} lasku: $3-1 on merkinnältään normaali eikä $(($3-1)) niinkuin BASH:issa muuten -> sulut tämän aiheuttavat.

---

Myös matriisien käsittelyyn on 'miljoona' vanhaa käskyä. Kaikki vanhat käskyt ovat rakenteeltaan merkki-sotkua ja kestää hieman ennenkuin oppii mikä sotku viittaa mihinkin. Käskyjä on niin paljon ettei niiden kaikkien täsmällistä muotoa kukaan voi muistaa vaan skriptatessa aikaa kuluu paljon taulukoiden selaamiseen editorissa - sillä merkki-sotkujen googlaaminen on vaikeaa mutta editorien ctr-f toimii loistavasti - ja BASH:in varsinaiset hakuun tarkoitetut työkalut ovat aivan omaa luokkaansa.

Esimerkiksi hyvin pieni otos matriisien käsittely-käskyistä:
Koodia: [Valitse]
a=(alpha beta gamma); echo ${#a[@]}         # tulostaa: numeron 3 (matriisin jäsenien lukumäärä. tyhjät rivit pois: echo -e ${mat[*]/%/\\n} | sed -e '/^$/d' | wc -l
a=(alpha beta gamma); echo ${#a[0]}         # tulostaa: numeron 5 (matriisin ensimmäisen jäsenen pituus)
a=(alpha beta gamma); echo ${#a[1]}         # tulostaa: numeron 4 (matriisin toisen jäsenen pituus)
a=(k kkk kk); echo -e ${a[@]/%/\\n} | wc -L # tulostaa: 4 -> matriisin pisimmän jäsenen pituus+1 -> 4 sillä matriisin jokaisen sanan edessä on IFS
a=(alpha beta gamma); echo ${a[1]:2}        # tulostaa: ta
a=(alpha beta gamma); echo "${a[@]#a}"      # tulostaa: lpha beta gamma     
a=(alpha beta gamma); echo "${a[@]%a}"      # tulostaa: alph bet gamm       
a=(alpha beta gamma); echo "${a[@]//a/f}"   # tulostaa: flphf betf gfmm     
a=(alpha beta gamma); echo "${a[@]/#a/f}"   # tulostaa: flpha beta gamma   
a=(alpha beta gamma); echo "${a[@]/%a/f}"   # tulostaa: alphf betf gammf     
a=(alpha beta gamma); echo "${a[@]/#/a}"    # tulostaa: aalpha abeta agamma
a=(alpha beta gamma); echo "${a[@]/%/a}"    # tulostaa: alphaa betaa gammaa
a=(alpha beta gamma); echo "${a[@]/#/oho}"  # tulostaa: ohoalpha ohobeta ohogamma
a=(alpha beta gamma); echo "${a[@]/%/oho}"  # tulostaa: alphaoho betaoho gammaoho
a=(alpha beta gamma); echo -e "${a[@]/#a/}" # tulostaa: lpha beta gamma
a=(aaa1 bbb2 ccc3); echo ${a[*]//[0-9]*/5}  # tulostaa:  aaa5 bbb5 ccc5
a=(1 2 3 4 5 6); echo ${a[$((${#a[@]}-1))]} # tuostaa: 6 joka on matriisin viimeinen rivi - siis tavallaan matriisin: tail -1

a=({1..99999}); time printf "%s\n" "${a[@]}"     # tulostaa matriisin kaikki-jäsenet kukin omalle rivilleen. printf:n formaatilla saat aikaan ihmeitä.
a=({1..99999}); echo -e ${a[*]/#/\\n}            # tulostaa matriisin jäsenet kukin omalle rivilleen - melkein sama kuin edellinen
a=(alpha beta gamma); echo ${a[*]}               # tulostaa alpha beta gamma  yhteen riviin
a=(alpha beta gamma); echo ${a[*]:1}             # tulostaa jäsenestä 1 alkaen: beta gamma
a=(alpha beta gamma); echo ${a[*]:1:1}           # tulostaa jäsenen 1: beta
a=(alpha beta gamma); echo ${a[1]}               # tulostaa beta
a=(alpha beta gamma [5]=delta); echo ${!a[*]}    # tulostaa 0 1 2 5; elikä mitkä indexit (osoitteet) ovat käytössä (jäsenet 3 ja 4 ovat määrittelemättä)
a=(alpha beta gamma [5]=delta); echo ${a[@]:2:3} # tulostaa: gamma delta (matriisin a jäsenestä 2 kolme jäsentä eteenpäin ja vaikka 3 ja 4 puuttuvat niin kyllä ne lasketaan) 
a=(alpha beta gamma);a+=("lisätty");echo ${a[@]} # tulostaa: alpha beta gamma lisätty    (elikä jäsenen lisääminen matriisiin)
a=(alpha beta gamma); echo "${a[@]:(-2)}"        # tulostaa: beta gamma   (elikä kaksi viimeistä jäsentä); siis: viimeinenjasen=${a[@]: -1}
IFS= a=(alpha "beta 2" gamma delta); printf "%s\n" ${a[@]:1}; unset IFS # tulostaa alekkain: beta 2  /  gamma  /  delta - siis 2 samalla rivillä kuin beta


***

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.

Tavanomaisempi funktio samaan tarkistukseen - ja se ilmottaa kaikki merkit eikä hermostu miinus-merkistä, plus-merkistä eikä desimaalipisteestä - mutta isommalla matriisilla aikaa alkaa kulua paljon:
Koodia: [Valitse]
function tarkista_matriisi () { apu=${@//[-+.,0-9]/}; [[ $apu > ' *' ]] && echo löytyi seuraavat merkit:$apu ;}; matriisi=({-500..+500}); matriisi[5000]=7A7e; matriisi[2]=c; time tarkista_matriisi ${matriisi[@]}

- koska tekstijono on saman-nimisen matriisin jäsen 0 niin voi näillä tarkistaa tekstijononkin.
- + on monisäikeinen juttu - yleensä sitä ei edes tarvita tuossa: [-+.,0-9]

33
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: +  .

seuraavanlainen on kätevä:
Koodia: [Valitse]
function + () { apu=$(echo ${@:1}); apu=${apu// /+}; echo $(($apu)) ;}; + 1 2 3 4 5 6 7 8 9

Tai:
Koodia: [Valitse]
function / () { echo -n $(($1/$2)).;apu=$(($1%$2))000000000000000000;apu=${apu:0:$1};echo $(($apu/$1)) ;}; / 223 17

- merkit: * @ # ? - $ ! 0  kelpaavat funktionimikisi mutta kutsussa niiden eteen täytyy laittaa keno:
Koodia: [Valitse]

function ! () { [[ $1 -eq 0 ]] && kertoma=1 || kertoma=$(seq -s* $1 | bc | tr -d '\\\n'); echo $kertoma ;}
\! 6  #-> tulostaa 720 niinkuin pitääkin. Lukualue on 'ääretön'.
Tai:
Koodia: [Valitse]

function * () {
luku1=${1//./,}; luku1=${luku1//E/e}; luku1=${luku1//+/}; luku1=${luku1//--/}
luku2=${2//./,}; luku2=${luku2//E/e}; luku2=${luku2//+/}; luku2=${luku2//--/}
luku1=$(printf "%.18e" $luku1)
luku2=$(printf "%.18e" $luku2) #; echo $luku1' '$luku2; read
exp1=${luku1##*e}; exp1=${exp1//+0/+}; exp1=${exp1//-0/-}
exp2=${luku2##*e}; exp2=${exp2//+0/+}; exp2=${exp2//-0/-}
luku1=${luku1%%e*}; luku1=${luku1%%+(0)}
luku2=${luku2%%e*}; luku2=${luku2%%+(0)}
[[ ${luku1:0:1} = '-' ]] && { merkki1=-1; luku1=${luku1:1} ;} || merkki1=1
[[ ${luku2:0:1} = '-' ]] && { merkki2=-1; luku2=${luku2:1} ;} || merkki2=1
int1=${luku1%%[,.]*}
int2=${luku2%%[,.]*}
kokonaistentulo=$(($int1*$int2))
kokonaisia=${#kokonaistentulo}
exp3=$(($kokonaisia+$exp1+$exp2)) #; echo $exp3; read
tulos=$((${luku1//,/}*${luku2//,/}))'00000000000000000000000000000000000000'; [[ ${kokonaistentulo:0:1} -eq 9 ]] && [[ ${tulos:0:1} -eq 1 ]] && exp3=$(($exp3+1))
[[ $(($merkki1*$merkki2)) -eq -1 ]] && echo -n '-'
[[ $exp3 -gt -1 ]] && apu2=${tulos:0:$exp3}.${tulos:$exp3} ||  apu2=.$(printf "%0"${exp3:1}"d")${tulos%%+(0)} 
[[ ${apu2//[^.]/} ]] && apu2=${apu2%%+(0)}; echo ${apu2%.} ;}
\* 223.12345678901234567  1.72356


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?

***

Tekstin lukemien nopeutuu kun sanojen väliin laitetaan välilyönti. Ihmisille sananväli onkin poikkeuksetta välilyönti ja jos määrättäisiin välilyönniksi merkkiksi: ¤ niin  kestäisi pitkään ennenkuin sen omaksuisi - mutta tietokone omaksuu sen välittömästi. BASH on määritelty siten että siinä sananväli on aina vain yksi merkki - jos esimerkiksi käsketään:
Koodia: [Valitse]
IFS=hjk a=(1hjk2); echo ${a[@]} tulostuukin: 1<välilyönti><välilyönti><välilyönti>2 -> siis IFS:n jokainen merkki joka löytyy sisäänmenneestä aiheuttaa ulostuloon merkin <välilyönti>.
IFS koskee vain lukemista ja kirjoittamisessa sanojen välissä on aina välilyönti. Huomioi ettei IFS:n asettamisen ja käskyn välissä ole kaksoispistettä.

BASH:in käskyt pitävät sananvälinä siis välilyöntiä jos ei toisin määrätä - tarkastiottaen se on alussakin kolmen merkin ryhmä: <välilyönti><tab><rivinvaihto> ja jokainen niistä on sananväli-merkki - mutta yleensähän tekstissä on aina vain yksi kerrallaan. Teoriassa IFS:ää voi muutella miten tahtoo mutta kun sen kerran määrää niin se on samassa päätteessä muutettuna voimassa kunnes toisin määrätään - siis viimeistään skriptin lopussa se pitäisi palauttaa alkuarvoihinsa mutta se unohdetaan usein - sen saa palautettu alkuarvoihinsa käskyllä: unset IFS . Mutta tämäntakia IFS on epäsuosittu.
- monilla uusilla käskyillä on kytkin jolla voi määrätä sen käskyn oman IFS:n ja se määrittely on voimassa vain käskyn suorituksen ajan.

Uudet käskyt eivät kykene jakamaan kirjain-ryhmän kohdalta kun sensijaan vanhat käskyt pystyvät jakamaan joko yhden tai useamman merkin kohdalta. Lisäksi vanhoilla käskyillä on lisäksi valtava nopeus-hyöty: kaikki vanhat käskyt on määritelty tulkissa joten ne ovat aina välittömästi käytettävissä kun taas uusista käskyistä useimmat on määritelty omissa tiedostoissaan joten käytettäessä ne täytyy ladata tiedostosta - eivät ne välimuistit aina niin kauheasti auta - keskimäärin luokkaa 50% muuten - joskus ne auttaa paljonkin mutta joskus tulee takkiin - mutta jokatapauksessa vanhat käskyt ovat kymmenenkertaa nopeampia.

Lisäksi vanhat käskyt osaavat paremmin hyödyntää yksinkertaisia regexiä - esimerkiksi [0-9] on yksinkertainen regex. Tai merkkiluokkia, esimerkiksi: [[:ctrl:]]

Vanhojen käskyjen olemassaolostakaan ei yleensä kerrota - tosin jopa BASH-raamatussa on joitain vanhoja käskyjä mainittuna sivumennen mutta sitä ei kerrota että kyseessä on iso nopeiden käskyjen ryhmä - mutta niiden nopeudesta ei puhuta eikä se kunnolla näykään ennenkuin niitä on skriptistä kaikki - yksikin uusi käsky voi hidastaa toiminnan taas mateluksi. Missään ei ole vihjettäkään siitä kuinka monta vanhaa käskyä on tai onko niiden joukko rajoittamattoman suuri. Minkäälaista ohjetta niiden muodostamiseen ei enää löydy - onko niiden muodostamisohjeiden häviäminen itsekseen edes mahdollista?

- voi ehkä luopua salaliitto-teorioista sensuhteen miksi uusiin käskyihin siirryttiin vaikka ne ovat normaalisti niin hitaita: isojen matriisien käsittelyssä ne ovat jopa hieman nopeampia kuin vanhat käskyt - joten ehkäpä uusin käskyihin siirryttiin osaltaan myös matriisi-huumassa.
- asiaan liittyy myös se että BASH:issa matriisin jäseneen viitataan: ${nimi[indeksi]}. Kun kauan sitten BASH:in suunnittelijoilta kysyttiin miksi nuo aalto-sulut tarvitaan niin vastaus ei ollut useimpien mielestä tyydyttävä. Mutta suurimmalta osin se ettei matriiseja käytetä johtuu tuosta aivot-nyrjäyttävästä viittaus tavasta.

***

Virtuoosit poistivat BASH:ista kirjasto-osoitteen koska BASH:ista haluttiin eron ja onistuihan aikomus - mutta toisaalta kirjastot kuuluvat BASH:in perusrakenteisiin joten niitä voi silti käyttää aivan huoletta kirjoittamalla kirjastoa haettaessa koko polku kirjasto-tiedostoon - ja nehän ovat merkittävin paikka tallettaa tekosensa - jos tekosiaan ei talleta kirjastoon vaipuvat ne itseltäkin nopeasti unholaan - tosin julkisia kirjastoja on vain kourallinen ja kuolin-kouristuksissa nekin  - mutta minulla on ollut oma hyvin-toimiva kirjasto jo kymmenen vuotta ja käytän sitä skriptatessani aina. Tosin se toimii enää vain tietovarastona sillä skripteillä ei ole mitään tekemistä tiedostojen ja niiden suoritus-oikeuksien kanssa - tiedostojen käyttämisestä ei ole mitään hyötyä ja niitä opetetaankin käyttämään vain koska se tekee skriptaamisesta niin vaivalloista että harvat viitsivät skriptata.

Uusia käskyjä käyttäen saa harvoin tehtyä vakaan skriptin - kyvyttömiä ja hitaitakin skripteistä tulee. Hieno paikka uskotella että BASH ei osaa, se on hidas ja höpertääkin raukka. Mutta jos alkaa käyttää vanhoja käskyjä niin saa tehtyä kyvykkäitä, vakaita ja nopeita perus-funktioita.

Kunnollisten perus-funktioiden tekeminen on silti vuosien urakka ja jos niiden annetaan tekemisensä jälkeen vaipua unholaan niin käy niinkuin BASH:ille on käynyt. Koska kirjastoja ei ole niin tekosien säiyttämiseksi jäljelle jää se että kirjoittaa huomioistaan nettiin. Niin monet tekevätkin mutta silti sieltä löytää perus-funktioita todella vaivalloisesti sillä juuri kukaan ei ole jaksanut pakertaa kovinkaan pitkälle joten todellisia kultajyviä on hyvin vähän ja niitä voi löytää vain sattumalta koska kaikki hautautuu nopeasti lukemattomien epäkelpojen ratkaisujen joukkoon.

Otetaanpa tekemisen vaikeudesta esimerkiksi se kuinka määritellään tekstinosan paikka tekstissä - millaista skriptiä jo yksi perus-funktio saattaa vaatia. Sillä aina tulos ei ole yksi numero vaan tulos voi olla:
- ratkaisua ei ole.
- usein se on kyllä yksi numero - ja siinä on vain kaksi käskyä ja se on nopea ja vakaakin:
Koodia: [Valitse]
apu="${teksti//$hakusana*/}"; echo ${#apu}. Mutta ratkaisuksi se on epä-kelpo
- joskus teksti löytyy useammasta paikasta ja jos tähän ei varauduta niin toiminta muuttuu joskus mählimiseksi. Löytyneiden joukkoa kuvaakin parhaiten matriisi:

- kun matriisin koko on nolla niin ratkaisua ei ole.
- kun matrisin koko on yksi niin se on se tapaus jota normaalisti halutaan. Toiminta on nopeaa - mutta tässä oletetaan että vain nämä kaksi ensimmäistä vahtoehtoa ovat mahdollisia mutta toisaalta näinolettaen saattaa saada tosipahaa sutta-ja-sekundaa.
- mikäli matriisin koko on kaksi tai useampi niin joudutaan valitsemaan mikä kelpaa ja se voi olla helppoa, vaikeaa tai mahdotonta. Jokatapauksessa silloin tarvitaan vielä lisää logiikkaa - joskus paljon enemmänja monimutkaisempaa  kuin varsinaisessa perus-funktiossa - joten kokonaisaika skriptin teossa on kammottava koska 'kirjastokiellon' takia pyörä pitää jokakerran keksiä uudestaan. Se ei ole vain vaikeaa vaan käytännössä mahdotonta - 'kirjastokielto' on todella tuhovoimainen. Käytännössä perus-funktiosta tulee tällainen:
Koodia: [Valitse]
teksti="kalliolle kukkulalle";hakusana=ll
apu=${teksti// /¤}; apu=($(echo ${apu//$hakusana/' '})); n=0; for ((i=0;i<${#apu[@]}-1;i++)); do echo $((${#apu[i]}+$n)); n=$(($n+${#apu[i]}+${#hakusana})); done

---

Vanhoilla käskyillä sama skripti on ihan toisenlainen, helpompi tehdä ja tosinopea - ongelmana oli vain se että käsky täytyi ensin kaivaa menneisyydestä:
Koodia: [Valitse]
teksti="kalliolle kukkulalle";hakusana=ll*
hakusana1=$hakusana; for ((i=0;i<99;i++)); do { apu=${teksti%$hakusana1}; echo ${#apu}; hakusana1=$hakusana1$hakusana; [[ "${teksti%$hakusana1}" == "$teksti" ]] && break ;}; done
- tosiaan niin nopeaa että parempaa keinoa on turha etsiä.
 
---

Mutta kummallakin keinolla muodostuu vasta matriisi ja sitä täytyy sitten käsitellä jotenkin mutta käsittely on tapauskohtaista - varmaan sillekin löytyy aikanaan yleinen ratkaisu.

***

BASH osasi jo alku-aikoina alkeis-matikkaa melkohyvin mutta sen ei koskaan sallittu esittää joutsen-lauluaan sillä matikka vaatii toimiakseen vanhoja käskyjä eikä BASH:ista päästäisi koskaan eroon mikäli sillä olisi niin erinomaisia käskyjä. Vaikka uusilla käskyillä on hyviä ominaisuuksia riittävästi niin niillä on myös paha sokea piste jossa vanhat käskyt ovat aivan ylivoimaisia. Kuvaillaanpa millainen sokea piste on:

Uudet käskyt ovat huomattavasti lähempänä ihmisen puhetta kuin vanhat käskyt. Kun tulkki tekee uudesta käskystä konekieltä tulee sitä ensinnäkin huomattavasti enemmän koodia ja tulkki myös joutuu huomattavasti useammin päättelemään mitä ihminen on tarkoittanut ja nämä päättelyt menevät joskus ihmisen kannalta ihan väärin. Sensijaan vanhoista käskyistä tulee paljon tiukempaa koodia eikä päättelyjä tarvitse tehdä ollenkaan joten koodista tulee paljon nopeampaa ja virheettömämpää. Sellainen vika vanhoilla käskyillä on että skriptien tekeminen on niitä käyttäen paljon työläämpää: skriptaajan täytyy muistaa on paljon enemmän ja kirjoitettavaakin on paljon enemmän.

Kun täytyy tehdä jotakin joka vaatii vanhoja käskyjen käytämistä niin ei niitä kannata uusien käskyjen sekaan sotkea vaan tehdä kirjastoon vanhoja käskyjä käyttäen toiminnasta malli nimeltää perus-funktio jolloin mitään vaikeutta ei ole - nimittäin kirjastot toimivat BASH:issa hyvin. Kirjaston kaikki funktiot ovat skriptissä käytettävissä kun skriptin alkuun laittaa rivin: source kirjstotiedoston_nimi_polkuineen

Perus-funktiot eivät ole mitenkään erikoisia mutta ennenkuin ne toimivat moitteettomasti kuluu aikaa suunnattomasti kokeiluihin ja testaamiseen - yhtä perus-funktiota tehdään yleensä kymmenkunta minuuttia mutta joskus harvoin monta kuukautta.

Mutta BASH:issa virtuoosit estivät kirjastojen käytön joten funktiot on jokakerta tehtävä uudestaan ja lisättävä skriptin koodiin. Eihän tuhat-rivisiä perus-funktioita kukaan muista ulkoa ja kun kirjastoa ei ole sitä ei voi kopioidakaan mistään. Ja todella pahan-näköistökin se on vaikka löytäisikin paikan josta sen voi kopioida.
- muuten tuota kirjastojen kutsua skriptin alussa voi nimittää myös kopioinniksi sillä se kopioi kirjaston kaikkien funktioiden koodin skriptin koodiin vaikka se ei missään näykään. Sentakia kirjastoja on monta.

Melkein poikkeuksetta perus-funktiot toteutetaan vanhoilla käskyillä - uusilla käskyillä niistä tulisi vajaa-kykyisiä, hitaasti toimivia ja ne toimisivat joskus näin ja sitte taas noin. Kyllä uusillakin käskyillä on omat hyvintoimivat käyttönsä mutta ihan toisissa asioissa.

BASH:illa on taito saada täysin hulluntuntuisia käskyjä toimimaan: kaikki toimii siten että kokonaisuus jaetaan palasiin ja palaset ratkaistaaan yksi kerrallaan ja vaihdetaan alkuperäisessä laskussa palanen sille laskettuun arvoon. Se toimii mikäli palaset ratkaistaan oikeassa järjestyksessä - ja BASH:in tulkilla tuntuu olevan melkoinen järjestyksen taju. Ja vanhoilla käskyillä tuo taju on normaalia parempi.

Esimerkiksi tällaisia perus-funktiot ovat:
Koodia: [Valitse]

function floor () {
[[ ${1//[^.]/} ]] && luku=$1'.0' || { echo $1; return ;}
int=${luku%%.*}
[[ ${1//[^-]/} ]] && echo $(( $int-1 )) || echo $int 
# mikäli desimaalipistettä ei ole niin palautetaan luku sellaisenaan ja lopetetaan heti
# mikäli desimaalipiste on niin palautetaan positiiviselle luvulle sen int ja jos se on negatiivinen niin palautetaan int -1
}

function ceil () {
[[ ${1//[^.]/} ]] && luku=$1'.0' || { echo $1; return ;}
int=${luku%%.*}
[[ ${1//[^-]/} ]] && echo $int || echo $(( $int +1 ))
# mikäli desimaalipistettä ei ole niin palautetaan luku ja lopetetaan heti
# mikäli desimaalipiste on niin palautetaan positiiviselle luvulle int+1 ja jos se on negatiivinen niin palautetaan int.
}

function abs () { echo ${1//-/} ;}  # echo ${1#[-+]} toimii myös ja joskushan + merkkikin on pahis.

function int () {  echo ${1%%.*} ;}

function fract () { [[ ${1:0:1} = - ]]  && merkki=-; echo $merkki.${1##*.} ;}

function getexp { echo ${1##*[eE]};}

function from_sci () { luku='00000000000000'${1%%e*}; luku=${luku//./}; apu=${1%%.*}; despoint=${#apu}; exp=${1##*e}; decimals=$(($despoint+$exp+14)); tulos=${luku:0:$decimals}.${luku:$decimals}; echo ${tulos/*(0)/} ;} # poistetaan turhat etunollat

function to_sci () { apu=$1;sign=${apu//[+.0-9]/}; apu=${apu//[-+]/}; mant=${apu%%.*}; mant=${mant//-/};exp=$((${#mant}-1)); mant=$apu'000000000'; echo $sign${mant:0:1}.${mant:2:7}e$exp ;} 

function siisti () { apu=${1##+(0)}; [[ ${1//[^.]/} ]] && apu=${apu%%+(0)}; echo ${apu%.};}  # etunollien ja takanollien poisto ja desimaalipisteenkin jos se jää viimeiseksi
# poista kokonaisosan etunollat, desimaaliosan takanollat ja desimaalipistekin jos se jää viimeiseksi

function len () { echo ${#1} ;}       # esimerkiksi: len aupappi tulostaa: 8 eli merkkien lukumäärän

function pos () { teksti=$1; apu=${teksti//$2*/}; echo "${#apu}" ;}; pos "kalliolle kukkulalle" ll # tulostetaan ensimmäisen hakusanan paikka tekstissä
function pos () { teksti=$1; hakusana=$2'*'; for ((i=0;i<9;i++)); do { apu=${teksti%$hakusana}; echo ${#apu}; hakusana=$hakusana$2'*'; [[ "${teksti%$hakusana}" == "$teksti" ]] && break ;}; done ;}; pos "kalliolle kukkulalle" ll # tulostaa: 17 6 2 eli tulostestetaan kaikki sijainti-vaihtoehdot

function count () { teksti=$1; hakusana=$2; apu=${teksti//$hakusana/¤}; apu=${apu//[!¤]/}; echo ${#apu} ;}; count "momoimo monestiko teksti: mo esiintyy tässä lauseessa" mo # tulostaa 5 eli count-funktio

function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}; etsi sana 3 lauseesta "9a 87b 654c 3(21)0/d'ille!";echo $word  # siis numero 3; paikalle kirjoitetaan monesko sana ja lainausmerkkien väliin lause josta etsitään. Mekeinpä kaikki merkit sallitaan.

- on näissä varmaan paljonkin korjattavaa sillä ne on tehty jo silloin kun aloittelin enkä aavistellut missä virheet saattavat muodostua - mutta periaate on oikea ja kaikki ne käyttävät vanhoja käskyjä.
- perus-funktioita on teoriassa rajattomasti mutta käytännössä yleensä vain tuhansia jaoteltuina eri tiedostoihin toiminta-tapansa mukaan.
- perus-funktioiden pituudet vaihtelevat muutamasta sanasta miljooniin eikä funktion nimestä voi päätellä sitä ollenkaan.
- perus-funktiot ovat teoriassa aina nopeita.

---

Uusilla käskyllä on kaksi muutakin paikkaa jossa ne mählivät:

Otettiin käyttöön kieltämättä vallankumouksellisen hyvä menetelmä jossa jokainen käsky käy läpi kymmenen eri laajennosta ennen tulkkausta. Kehittäjiä oli vaan ihan liian vähän että sen olisi saanut toimimaan hyvin.

Toinen paikka jossa mählittiin oli jakopisteen valinta: valittiin nopeampi menetelmä: jakopiste on yksi merkki. Nopeaahan se on mutta sen aiheuttamaa epävarmuutta ei saa koskaan poistettua - useimmiten tästä syystä BASH-illa on vaikeaa tehdä moitteetonta skriptiä.
- jakopiste on se piste jossa esimerkiksi cut-käsky jakaa lauseen silloin kun se jakaa jonkun merkin kohdalta. Lauseessa on usein sama merkki useassa paikassa. Ei se merkkiryhmäkään moitteton ole mutta siinä on kumminkin paljon enemmän yksiselitteisyyttä.

34
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

35
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?

36
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

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

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

39
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

40
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 

   



Sivuja: 1 [2] 3 4 ... 35