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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


44
- on mielenkiintoista seurata kuinka nämä viritykseni hiljokseen vuotavat kansainvälisille foorumeille. Ja kohta näitä aletaan syöttää tänne takaisin sikäläisinä huomiona.

Desimaalisen matriisin jäsenten summa kun mukana saattaa olla negatiivisia jäseniä:
Koodia: [Valitse]
# ensiksi määritellään esimerkki-matriisi joka arvoja lasketaan:
unset matriisi # Jos matriisille on joitain vanhoja määrittelyjä niin aluksi ne poistetaan.
matriisi=($(seq -s' ' 9.9 -1.1 1 | tr , . ))
matriisi2=($(seq -s' ' -1.1 -1.1 -10 | tr , . ))
matriisi+=(${matriisi2[@]}) # tässä liitetään matriisit siten että kokonaisuuden nimeksi jää nimi: matriisi.
# sekoitetaan hieman pakkaa elikä kokeillaan kajahtaako muutos lopputulokseen oikein kun lisätään yhteen matriisin jäsenistä .01 (oikea lopputulos on tällä matriisilla 0.01)
matriisi[5]=4.41
echo katsotaanpa millainen matriisi tälläkertaa aikaansaatiin:
printf "%s\n" "${matriisi[@]}"; echo # näin etukäteen muillatavoin laskettuna matriisin jäsenien summa on tälläkertaa siis: 0.01

# sitten lasketaan matriisin jäsenten summa - ajoittaen lasku:
time { # matriisi täytyy jakaa desimaali-matriisiisiin ja kokonais-matriisiin sekä määritellä desimaali-matriisin pisin jäsen. Lopuksi desimaali-matriisin jäsenien loppuun laitetaan niin monta nollaa että jokaisen pituus on yhtämonta merkkiä:
desmatriisi=($(echo ${matriisi[@]##*.})); desmatriisinpisin=$(($(echo -e ${desmatriisi[@]/%/\\n} | wc -L)-1)); echo 'desimaaleja eniten: '$desmatriisinpisin; desmatriisi=($(printf "%.17f " ${desmatriisi[@]} | tr ' ' '\n' | tr -d , | cut -c -$desmatriisinpisin)); echo desijono=${desmatriisi[@]}; kokomatriisi=($(echo ${matriisi[@]%%.*})); echo kokojono=${kokomatriisi[@]}
 
# täsävaiheessa täytyy ottaa huomioon se että mukana saattaa olla negatiivisia jäseniä. Asia oikeastaan on ihan yksinkertainen: kun negatiivinen luku jaetaan desimaalipisteen kohdalta niin merkki jää kokonaisosaan ja desimaaliosa jää aina positiiviseksi - mutta tämänkaltaisissa laskuissa kokonaisosan merkki kuuluu myös desimaaliosaan ja se täytyy siis oikaista:
for (( n=0; n<=${#matriisi[@]}; n++ )); do [[ ${matriisi[$n]:0:1} = \-  ]] && desmatriisi[$n]=-${desmatriisi[$n]}; done
echo 'Desimatriisista tuli kun merkit oikaistiin: '${desmatriisi[@]}
 
# sitten lasketaan desimaali- ja kokonaismatriisien summat. Onneksi BASH:in matematiikkamoottori ymmärtää että ilmaisu: +-  merkitsee samaa kuin: -
desisum=$(($(declare -p desmatriisi | tr '"' '\n' | grep -E '^[-0-9]' | tr '\n' + ; echo 10000000 ))); echo desisum=$desisum' josta kokonaisiin lisätään: '${desisum:1: -$desmatriisinpisin}' ja desimaaaleihin jää: '${desisum:$((${#desisum}-$desmatriisinpisin))} # 10000000 on vain varmistus siitä että myös0.01 toimii oikein - etuykkönen hylätään aina
kokosum=$(($(declare -p kokomatriisi | tr '"' '\n' | grep -E '^[-0-9]' | tr '\n' + ; echo 0 ))); echo kokosum=$kokosum

# kokonais-summaa laskettaessa siirretään desimaaliosan ylivuoto kokonaisosan summaan:
kokosumma=$(($kokosum+${desisum:1: -${desmatriisinpisin}})); echo 'matriisin jäsenien summa='$kokosumma.${desisum: -$desmatriisinpisin}
}

 
- tästä skriptistä selviää moni asia joista olisi oleellista apua useiden jo osittain ratkenneiden matemaattisten tehtävien oikeellistamisessa ja yksinkertaistamisessa, alkaen kuitenkin desimaalilukujen yhteenalaskusta.
- tätä matriisien summaamista voi muuttamatta mitään käyttää eksinkertaiseen yhteen/vähennyslaskuunn jossa jäseniä voi olla vain kaksi - tai ihan niin monta kuin haluaa - elikä kauppalasku ratkeaa samalla funktiolla kuin yksinkertainen ynnä-lasku tai todella iso matriisikin.
- ei tämä vielä moitteetonta ole vaan paljon tytyy vielä tehdä - mutta jos tämänkaltaisia virityksiä olisi alettu harrastaa jo silloin kolmekymmentä vuotta sitten  kun nämä periaatteessa toimivat jo niin tänäpäivänä meillä olisi esittää jotain tosi-pätevää, olisivat pythonitkin nesteessä.
- BASH on siitä mukava että jo etukäteen voi olla varma että mitä yrittääkin onnistuu varmasti - toisaalta on melkein yhtä varmaa että ennenkuin kaikki on oikein löydät lukemattomia sellaisia keinoja joissa on jotain moittimista. 
- BASH on tuhansia kertoja liian hidas jotta tästä olisi ohjelmallisesti mitään mieltä lähi-tulevaisuudessakaan - mutta pähkäily pitää pää-nuppia kunnossa ja antaahan se tyydytystä tehdä sellaista jota väitetään mahdottomaksi.
- kannattaa kokeilla matriisiksi: unset matriisi; for n in {1..12345}; do matriisi[$n]=$SRANDOM.$SRANDOM; done
- Mielestäni opetuksella on vain kaksi vaihtoehtoa - yritetään opettaa kunnolla tai ei opeteta ollenkaan. Mitä nuo 'advanced guidet' sönköttää?

45
Tehdäänpä esimerkki desimaalisten matriisien summaamisesta. Aluksi määritellään matriisi:
Koodia: [Valitse]
unset matriisi  # matriisit kannattaa ennen määrittelyä nollata
matriisi=($(seq -s' ' 9.9 -1.1 1 | tr , . ))
matriisi2=($(seq -s' ' 1.1 1.1 10 | tr , . ))
matriisi+=(${matriisi2[@]}) # tässä liitetään matriisit siten että kokonaisuuden nimeksi jää nimi: matriisi.
# sekoitetaan hieman pakkaa elikä kokeillaan kajahtaako muutos lopputulokseen oikein kun lisätään yhten matriisin jäsenistä .01 (oikea lopputulos on silloin 99.01)
matriisi[5]=4.41
echo katsotaanpa millainen matriisi aikaansaatiin:
printf "%s\n" "${matriisi[@]}"; echo

# sitten lasketaan matriisin jäsenten summa - ajoittaen lasku:
time { # matriisi täytyy jakaa desimaali-matriisiisiin ja kokonais-matriisiin sekä määritellä desimaali-matriisin pisin jäsen. Lopuksi desimaali-matriisin jäsenien loppuun laitetaan niin monta nollaa että jokaisen pituus on yhtämonta merkkiä:
desmatriisi=($(echo ${matriisi[@]##*.})); desmatriisinpisin=$(($(echo -e ${desmatriisi[@]/%/\\n} | wc -L)-1)); echo 'desimaaleja eniten: '$desmatriisinpisin; desmatriisi=($(printf "%.17f " ${desmatriisi[@]} | tr ' ' '\n' | tr -d , | cut -c -$desmatriisinpisin)); echo desijono=${desmatriisi[@]}; kokomatriisi=($(echo ${matriisi[@]%%.*})); echo kokojono=${kokomatriisi[@]}

# sitten lasketaan desimaali- ja kokonaismatriisien summat.
desisum=$(($(declare -p desmatriisi | tr '"' '\n' | grep -E '^[0-9]' | tr '\n' + ; echo 0 ))); echo desisum=$desisum' josta kokonaisiin lisätään: '${desisum:0:$((${#desisum}-$desmatriisinpisin))}' ja desimaaaleihin jää: '${desisum:$((${#desisum}-$desmatriisinpisin))}
kokosum=$(($(declare -p kokomatriisi | tr '"' '\n' | grep -E '^[0-9]' | tr '\n' + ; echo 0 ))); echo kokosum=$kokosum

# kokonais-summaa laskettaessa siirretään desimaaliosan ylivuoto kokonaisosan summaan:
kokosumma=$(($kokosum+${desisum:0:$((${#desisum}-$desmatriisinpisin))})); echo 'matriisin jäsenien summa='$kokosumma.${desisum:$((${#desisum}-$desmatriisinpisin))}
}

- skripti aikaansaa seuraavan tulosteen:
katsotaanpa millainen matriisi aikaansaatiin:
9.9
8.8
7.7
6.6
5.5
4.41
3.3
2.2
1.1
1.1
2.2
3.3
4.4
5.5
6.6
7.7
8.8
9.9

desimaaleja eniten: 2
desijono=90 80 70 60 50 41 30 20 10 10 20 30 40 50 60 70 80 90
kokojono=9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9
desisum=901 josta kokonaisiin lisätään: 9 ja desimaaaleihin jää: 01
kokosum=90
matriisin jäsenien summa=99.01

real   0m0,014s
user   0m0,015s
sys   0m0,010s

- tietysti ajat vaihtelevat aika paljonkin ajo-kerasta toiseen mutta suuntaus on niissä sama: koska user on yleensä tällä skriptillä paljonkin suurempi kuin real niin ilmeisesti kahdenkilon kalakukko on pistetty yhden kilon pussiin. Sitten on vielä sys-aika joka on merkittävän suuri sekin - tuntuu pussi ainavaan venyvän

46

Lasketaanpa siten keskiarvo. Keskiarvo on useimmiten desimaaliluku joten se vaatii laskuun omaa pitkähköä funktiotaan. Ajaakseesi laskun leikkaa liimaa koko koodikenttä päätteeseen ja paina return.
Koodia: [Valitse]

function jaa () { (( ! $# )) && echo funktion ajokäsky on muotoa: jaa 1 2 . Siitä pitää tulla: .5000000000000000000000000000 && sleep 2 && return
local apu apu1 apu2
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia. Vaihtoehdot:tulosta=echo ja tulosta=:
# kun jakaja kasvaa yli 18 numerosta alkaa tulo epätarkentua kymmenenteen osaansa jokaista ylimääräistä numeroa kohti - jolloin desimaalipisteen jälkeen tulee yksi nolla lisää. Siten vielä 35 numeroinen jakaja antaa jonkinlaisen tuloksen -> luku=${luku##+(0); lkm=${#luku}; [[ $lkm>18 ]] && { ekstranollia=printf "%${lkm-18))s" | tr " " 0; luku1=${luku1:0:17} ;}
 
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."; [[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."

desimaaliosa1=${luku1##*.}; desimaaliosa2=${luku2##*.}; int1=${luku1%%.*}; int2=${luku2%%.*}

# desimaali-osat yhtäpitkiksi:
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
$tulosta koko1:$int1$desimaaliosa1'  koko2:'$int2$desimaaliosa2

# kokonais-osat yhtäpitkiksi:
(( ${#int2} >= ${#int1} )) &&
{ apu="00000000000000000000"$int1; int1=${apu: -${#int2}} ;} || { apu="00000000000000000000"$int2; int2=${apu: -${#int1}} ;}

luku1=$int1$desimaaliosa1;luku1=$((10#$luku1)); luku2=$int2$desimaaliosa2;luku2=$((10#$luku2)); $tulosta jaettava:$luku1"   "jakaja:$luku2

(($luku1 >= $luku2)) && { apu=$(($luku1/$luku2)); kokonaisiatulosteessa=${#apu}; nolliatulosteessa='' ;} || { apu=$(($luku2/$luku1)); apu=${apu//[1-9]/0}; nolliatulosteessa=${apu:1}; kokonaisiatulosteessa=0;}; $tulosta nolliatulosteessa:$nolliatulosteessa"   "kokonaisiatulosteessa:$kokonaisiatulosteessa   

# tähänasti on selvitetty desimaalipisteen paikkaa. Nyt aletaan laskea mitä numeroita tulokseen tulee:
unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
luku1=$luku1'0000000000000000000'; luku1=${luku1:0:18} ;$tulosta " "$luku1 #"  "${#apu2}"  "$apu

for n in {1..2}; do # muodostetaan tulos-palasia 9 merkkiä kerrallaan
apu=$(($luku1/$luku2)); (( ${apu: -1} )) || apu=$apu'0';tulos[$n]=${apu};  # yksi keino palauttaa niitä kadonneita nollia;  (( $(($luku1%$luku2)) > $((10*$q )) )) && apu=$apu'0' on joskus tarpeen ja joskus liikaa
apu2=$(($luku1%$luku2)); luku1=$apu2'0000000000000000000'; luku1=${luku1:0:18} ; $tulosta " "$luku1"  "${#apu2}"  p"$apu
done

vanhatmerkit=1
for n in {1..2}; do # kootaan tulosta matriisin palasista
uudetmerkit=${#tulos[$n]}; [[ $uudetmerkit -lt $vanhatmerkit ]] && tulos[$n]=$nolliatulosteessa${tulos[$n]} ; vanhatmerkit=$uudetmerkit
tulos=$tulos${tulos[$n]}
done
 
echo -e '\n'; [[ $nolliatulosteessa ]] && echo -n .$nolliatulosteessa${tulos:0} || echo -n ${tulos:0:$kokonaisiatulosteessa}.${tulos:$kokonaisiatulosteessa}
#bc<<<"scale=$((${#tulos}+$((${#nolliatulosteessa})))); $1/$2" | tr -d '\\\n'; echo '  tämä rivi on oikea tulos bc:stä' ;
}

unset matrix; for n in {1..12345}; do matrix[$n]=$SRANDOM; done; echo ${matrix[@]}; echo
sum=$(($(declare -p matrix | tr '"' '\n' | grep -E '^[0-9]' | tr '\n' + ; echo 0 )))
echo 'matriisin jäsenten summa='$sum'  ja jäsenien lukumäärä='${#matrix[@]}
time { keskiarvo=$( jaa $sum ${#matrix[@]}); echo; echo keskiarvo=$keskiarvo ;} # muissa kielissä koko skripti olisi vain viittaus kirjastoon ja lyhyt versio tästä lauseesta.

- eihän tämä skripti ole tarkoitettu käytettäväksi sillä onhan tämä hirveän monirivinen - lisäksi tämä on hidas muihin kieliin verrattuna vaikka BASH-skriptiksi todella nopea. Mutta sikäli mielenkiintoinen skripti että minkä takia opetetaan kelvotonta skriptaamista?   

47
BASH:in alkeita opetetaan monessa paikassa moitteettomasti - ja ainahan se on niin että kaikessa opetuksessa täytyy tyytyä vajaavaisiin opetuksiin, joitain asioita ei kannata kertoakaan, toisia painotetaan jotenkin ja vähänväliä tullee eteen sellaista jota ennen on sanotttu mahdottomaksi - mutta sen ratkaisu vain vaatii ihan uutta ajatustapaa - joka täytyy ensin opetella. Mutta BASH:in asiat alkoivat jo alkuunsa vinoutua: annettiin alkeisopintojen jälkeen ymmärtää että tässä tämä oli kirjoittamalla 'advanced guideja' joissa nimestään huolimatta ei ole mitään edistyksellistä - ihan kunnollisia opastuksia alkeisiin ne kyllä ovat - mutta niiden opetuksien jälkeenhän ne edistyneemmät menetelmät vasta alkavat. Ihan toinen asia on että kannattaako BASH:ia opiskella jos sitä kuolevaksi kieleksi luulee. Ja opetuksessa piisaa puutteita muutenkin:
- ei esitetä opetetun heikkouksia joten ollaan tyytyväisiä epämääräisesti toimiviin skripteihin ja todetaan vain että BASH on niin kelvoton ettei parempaa saa aikaiseksi.
- ei vihjatakaan kuinka pitäisi toimia. Kieltämättä edistyneemmät käskyt ja käskyryhmät ovat ulkonäöltään niin kummallisia ettei niitä kukaan halua käyttää tai edes muistaa.
- aivan kaikessa täytyisi painottaa että ei tämä aina ihan näinkään ole - tämä on vasta johdatusta.
 
Esimerkiksi BASH:in matematiikkaa ei käytetä ollenkaan koska siitä on tehty tarkasti harkituin toimenpitein käyttökelvoton. Otetaanpa esimerkiksi matriisin jäsenten keskiarvon laskeminen - aletaan helposta ja lasketaan aluksi se keskiarvo positiivisista kokonaisluvuista. Laskussa kaksi osaa: lukujen summaaminen ja keskiarvon laskeminen jakamalla summa kokonaislukujen lukumäärällä.

Tehdään matriisi jossa on 10000 suurehkoista satunnaisluvuista tehtyä jäsentä ja esitetään kolme tapaa saada niistä summa (skriptiä ajaakseesi leikkaa koko koodikenttä kerralla ja liimaa se pääteeseen ja paina return):
Koodia: [Valitse]
unset matrix; for n in {1..10000}; do matrix[$n]=$SRANDOM; done; echo ${matrix[@]}; echo

time { sum=0; for n in {1..10000}; do sum=$(($sum+${matrix[$n]} )); done; echo $sum ;}                     # se tapa mitä normaalisti käytetään - normaalinopea
time { sum=$(($(echo ${matrix[@]} | tr ' ' + ))); echo $sum ;}                                             # supernopea
time { sum=$(($(declare -p matrix | tr '"' '\n' | grep -E '^[0-9]' | tr '\n' + ; echo 0 ))); echo $sum ;}  # hypernopea
- eihän varsinkaan tuota hyperiä kannata koodiinsa kirjoittaa vaan semmoiset kuuluvat kirjastoon. Virtuoosit toisaan tiesivät kuinka kieli nitistetään - ei tarvitse tehdä muuta kuin antaaa 'kirjastokielto' - eikä senjälkeen kukaan halua nähdä vaivaa löytääkseen mitään parempaa koska se kumminkin vaipuu unhoon kirjastojen puuttuessa.

48

Joo, Bash roikkuu vielä pitkään mukana Linux-jakeluissa. Applen macOS:ssä tosin vaihdettiin jo oletus-shelliksi Bashia edistyneempi ja monipuolisempi Zsh, joten se on käyttäjämäärältään ohittamassa Bashin. Ehkä osa Linux-jakeluista seuraa perässä tulevina vuosina. Zsh ei ole ihan nuori vaihtoehto sekään. Ensimmäinen versio on julkaistu vuonna 1990, eli vain vuosi Bashin ensimmäisen version jälkeen ja ennen Linuxin tuloa.


Kaikkea hyvää Zsh:lle sillä onhan BASH:issa paljon parannettavaa - mutta minä olen liian ryytynyt tapoihini siirtyäkseni Zsh:ta käyttämään.

Mutta hyppäsiköhän Apple sittenkin väärään junaan sillä kyse ei taida olla kielen kyvykkyydestä vaan sen käyttäjien asenteista - laulun sanoin: lökäpöksyni mun.

Enkä edes välitä siitä kuinka nopeasti skriptini toimivat huolimatta siitä että ajoitan ne aina - koska sehän taitaa kertoa mihin suuntaan kehitys on menossa - nopeampi ei oikeastaan ole parempi mutta hitaampi on varmasti huonompi. Mutta kyvykkyys sensijan kiinnostaa paljonkin. Ja päinvastoin kuin väitetään niin BASH:ilta tuntuu onnistuvan 'mikähyvänsä' - enkä tosiaan käsitä kuka sen kyvykkyyttä yrittää kätkeä. Tosin kaikki toimii hyvin hankalasti sillä eihän ongelmia kauan sitten ratkaistu ollenkaan samallatavoin kun nykyään - mutta paljolti samoja ne ongelmat olivat aikoinaan kuin nykyäänkin. Ja koodin pituushan ei kerro koodin nopeudesta paljoakaan vaan pidempi on useinkin nopeampi.

Mutta valitettavasti kaikki ponnistukseni menevät hukkaan sillä BASH:in kirjastojen toimimisen haittaaminen tuntuu olevan virtuooseille pyhä sota - ja kirjasto-kamaa kaikki edistyneempi on ja mikäli se ei kirjastoon päädy niin unohtuuhan se aikanaan.

***

Olen aina ihmetellyt niitä postauksia joita BASH skripteistä kirjoitetaan. Esimerkki on ulkomaisilta verkkosivuilta - sillä minä en maailmalle kirjoittele vaan poikkeuksetta ainoastaan luen. 

Käytännössä tapahtumat seuraavat toisiaan melkein aina seuraavalla tavalla: jollain ulkomaan verkkosivulla esitetään ongelma ja sille tulee esimerkiksi sata erilaista vastausta. Ensimmäiset vastaukset ovat tottakai vähän avuttomia mutta jo esimerkiksi kymmenes ratkaisee ongelman erittäin nopealla ja kaikissa tilanteissa toimivalla tavalla. Luulisi että vastukset loppuisivat siihen mutta niin ei koskaan käy vaan kelvottomien ratkaisujen tulva vain kiihtyy. Täytyy oppia etsimään se paras vastaus kaikista postauksista sillä se paras vastaus ei yleensä ole viimeisten joukossakaan. Olenkin alkanut epäillä että asioita sotketaan tahallisesti - tai sotkettiin silloin kauan sitten.

***

Seuraavista 'mahdottomista skripteistä' pikkuisen: nimi-parametrithan eivät kuulemma toimi BASH:issa? Tässä ne toimivat. Vertailun vuoksi esitän toiminnan käyttäen arvo-parametrejakin.

Tarkoituksena on tehdä skripti joka etsii maksimin/minimin silloinkin kun joukossa on desimaalilukuja. Aluksi täytyy kuitenkin kertoa kuinka desimaaalilukuja muodostetaan BASH:issa sillä kyllä BASH:in jotkut käskyt osaavat senkin ihan kikkailematta. Esimerkiksi anna käsky:
Koodia: [Valitse]
seq -s' ' 12600 -1.1234 1.1 | tr , . 
- seq noudattaa printf:n muoto-määräyksiä joten esimerkiksi seq laitaa tulostuksessaan Suomessa pilkun desimaalieroittimeksi. Jos se on alunperinkin piste niin tr on vain turha mutta virhe se ei silloinkaan ole.
- muuten seq toimii kaksimielisesti: se vaatii aina käskettäessä desimaalipistettä vaikka se antaisikin sen pilkkuna ulos.

Tämän avulla saa tehtyä sellaisen desimaaliluvuista muodostuneen matriisin jonka arvot ovat satunnaisessa järjestyksessä (ja jossa on jäseniä 11215 eli yhtämonta kuin edellisessä minimin määrittelyssä joka siis kesti 7 sekuntia):
Koodia: [Valitse]
sort -R <(seq -s' ' 12600 -1.1234 1.1 | tr ' ,' '\n.' )
seuraavaksi muodostetaan lauseen avulla matriisi ja etsitään siitä pienin arvo:
Koodia: [Valitse]

unset apu  # skriptin esimmäiseksi käskyksi täytyy kirjoittaa: unset apu. Sillä kun matriisi on määritelty jää se päätteen muistiin kunnes se tuhotaan käskyllä unset tai siirtytään toiseen päätteeseen - ja ellei unset:iä ole tulee sotkua kun määritelään apu uudelleen. Unset ei ole virhe vaikka matriisia ei vielä olisikaan.
apu=$(sort -R <(seq -s' ' 12600 -1.1234 1.1 | tr ' ,' '\n.' ))

# etsitään maksimi/minimi hakemalla matriisin arvot BASH:in kirjanpidosta nimi-parametrin avulla. Tämä on nopein menetelmä
function haeminimi () { sort -n <(declare -p $1) | tr -dc '\n'[.0-9] | head -2 | tail -1 ;}; time haeminimi apu     # selitys käskyille: | head -2 | tail -1 -> valitaan rivi 2
function haemaksimi () { sort -n <(declare -p $1) | tr -dc '\n'[.0-9] | tail -1 ;}; time haemaksimi apu

# tai etsitään maksimi/minimi ratkaisten matriisin arvot käyttäen käskyä: 'eval nimi-parametri' (mutta eval on jostain syystä virtuoosien kiroissa)
# apu on tässä esimerkissä nimi-parametri mutta siinähän voisi olla minkä matriisin nimi hyvänsä:
function haeminimi () { sort -n <(eval echo \${$1[@]} | tr ' ' '\n' ) | head -1 ;}; time haeminimi apu
function haemaksimi () { sort -n <(eval echo \${$1[@]} | tr ' ' '\n' ) | tail -1 ;}; time haemaksimi apu
 
# tai hetaan maksimi/minimi käyttäen arvo-parametreja:
function haeminimi () { sort -n <(echo "$*" | tr ' ' '\n') | head -1 ;}; time haeminimi "${apu[@]}"
function haemaksimi () { sort -n <(echo "$*" | tr ' ' '\n') | tail -1 ;}; time haemaksimi "${apu[@]}"

- näissa esimerkissä matriisin nimi on apu, mutta funktiot toimisivat samoin vaikka matriisin nimi olisi joku muukin
- ja nopeus on tosiaan kaiklla monisatakertainen siihen vertaavaan menetelmään verrattuna - ja toimitaan aina samalla tavalla riippumatta siitä millaista data on eikä milloin mitenkin niinkuin siinä hitammassa vertailussa joka tukehtuu jo desimaali-pisteeseenkin.
- muuten: BASH:issa ei kovin nopeasti pidä hyväksyä laskua oikeelliseksi vaikka se tuntuisikin toimivan. Samat testit tulee suorittaa eri päivinä kun kone on ollut välillä sammutettuna ja tehdä erilaisia testejä monta päivää - testejä samassa pääteistunnossa ja välillä vaihtaen pääteistuntoa, joskus tarkoin määritellyllä datalla ja joskus satunnaisella datalla, käännellen-väännellen ja välituloksia ottaen .... joten parin viikon kuluttua samasta aiheesta testin tekemisen ajatteleminenkin aiheuttaa paiseita ja on tehtävä välillä jotain muuta.

49
Mutta eipä BASH:ista ihan kohta taideta eroonkaan päästä päätellen siitä mitä vähäsen taas lueskelin - BASH:ista sinänsä vältetään puhumastakin mutta oletetaan sen kuitenkin toimivan sillä esimerkiksi tietokoneen ylläpito BASH:in avulla on helppoa kun voi ensin tutkia mikä tietokoneessa mahdollisesti on vialla ja perään asentaa tietokoneelle suojauksia - tosin vain harvat edelleen hoitavat konettaan itse. En tosiaan tiedä mutta aavistelen että pahikset kyllä hyödyntävät BASH:in osaamista mutta hyvikset eivät tajua että BASH on myös paras vastalääke.

Samoin pitää edelleenkin paikkansa että jos BASH:ia osaa on sillä nopea tehdä jollakin tavalla toimiva skripti kun haluaa selvittää kannattaako hommaan tehdä ohelma jollain nopealla kielellä.

Koko homma on tragikoominen, varsinkin jos alkaa levitä tieto siitä että kyllä BASH:kin osaa - ja jos muutamia temppuja on löytynyt jo niin pajonko niitä löytyy lisää? Esimerkiksi BASH:in käskyt osaavat paljon regex:istä - useat käskyt tottelevat niitä omalla tavallaan - olenkin viimepäivinä kokeillut niillä toimimista - en silti tiedä tuleeko niiden hyödyntämisestä mitään kunnolista. Kokeillut tosiaan sillä ei niistä ole hiiren hyppimääkään missään - lukuunottamatta vihjailuja sillä kaikki on tehty tarkkojen suunnitelmien mukaan mutta työt ovat jääneet dokumentoimatta - siis joku tietää.

BASH:ia alettiin tosiaan rapauttaa jo ennen kuin siihen oli saatu liitettyä kaikkia ehdottomasti tarpeellisia osia - tehtyjä ne jo tosin olivat mutta niiden liittäminen BASH:in koodiin olisi ollut melkoinen homma - ja ilmeisesti luultiin että eiköhän BASH kuole melko nopeasti ja työ olisi turhaa - ja tämä tuntui pahiten matematiikassa koska sille ei siihenaikaan BASH:issa juuri arvoa annettu - sillä desimaalien toimiminen on tosiaan välttämättömyys sille että matematiikasta voisi edes puhua. Eikä se oikein sytytä että aina täytyy käyttää ulkoisia matematiikkaohjelmia kun täytyy tehdä jotain matemaattista - vaikka ne ovatkin paljon parempia kuin BASH.

Esimerkiksi niin perustavanlaatuinen asia kuin maksimin/minimin määrääminen tekstijonosta tai matriisista: seuraava toteutus on se mitä tapaa opetetaan käyttämään - tosin tässä skripti on omituisen näköinen koska se on kirjoitettu funktiomuotoon:
Koodia: [Valitse]
function haemaksimi () { apu=$1; for (( x=1; x<=$#; x++ )); do  [[ $apu -gt $(eval echo \$$x) ]] && apu=$(eval echo \$$x); done; echo $apu ;}; unset apu; unset apu; for n in {1..9938}; do apu[$n]=$n; done; time haemaksimi "${apu[@]}"

- sen suoritus kestää noin 6 sekuntia - ja se toimii vain kokonaisluvuilla ja teksti sekoittaa sen kokonaan, myös desimaalipiste. Mutta maksimi/minimi löytyy melkein tuhatkertaa nopeammin sorttaamalla matriisi ensin nopealla C-kielisellä sorttaus-funktiolla joka toimii vaikka joukossa olisi desimaalilukuja tai tekstiäkin. Ja minimihän on senjälkeen matriisin ensimmäinen jäsen ja maksimi viimeinen - epämääräisyyksiä tulee paljon vähemmän kuin ennen ja niissäkin auttaa useimmiten kun säätelee sort-funktion kytkimiä - sillä jos sort käsketään sorttaamaan numeroarvon perusteella niin kyllä se sittenkin tekee parhaansa myös tekstin suhteen:

50
BASH:ista haluttiin päästä eroon jo ennenkuin Ubuntusta tuli ensimmäinen versio. Syy siihen oli sama kuin miksi huippu-kyvykäs assembler-kieli tapettiin aikoinaan: skriptit olivat sillä liian kalliita tehdä ja ylläpitää (se hinta maksettiin kylläkin yleensä vain koodaajan selkänahalla). Lisäksi koodarien uusi sukupolvi toi mukanaan uusia parempia kommervenkkejä hyläten kaiken vanhan. Muisa kielissä muutaman vuoden jälkeen alettiin hyväksyä jotakin siitä vanhastakin - mutta BASH:issa ei palattu osittainkaan vanhaan vaan jäätiin täysin siihen uuteen. Lisäksi silloin kauan sitten useimmat hoitivat tietokoneitaan itse ja ainoastaan siinä tehtävässä BASH on huippuhyvä. 

Mutta BASH:ia ei voitu tuhota kerta-runttuulla niinkuin assembleria vaan se täytyi kiduttaa hitaasti hengiltä - ja mikäs tähän on sopivampaa kuin edistää tapoja tehdä skriptestä hieman avuttomia sensijaan että opetettaisiin tapoja tehdä nopeita ja kyvykkäitä - ja nimenomaan jokavaiheessa täytyi tuoda esiin BASH:in surkeus. Sitä en osaa sanoa uskovatko nykyiset virtuoosit noihin väitteisiin mutta se on varmaa että aikoinaan -eivät- uskoneet vaan puhuivat palturia ihan tieten.

Kun aloin tutkia BASH:ia silloin kun ensimmäinen Ubuntu tuli - aluksi pintapuolisesti ja lopussa kokopäiväisesti kun alkoi olla varmaa että virtuoosit esittävät teoriat siten että ilman johdatusta skriptaajat varmasti valitsevat kahdella tavalla tulkittavasta teoriasta sen väärän vaihtoehdon. Esimerkiksi sanotaan: BASH:in matematiikkamoottori ei desimaaleja hanskaa eika BASH:issa ole desimaali lukutyyppiäkään - kumpikin väite onkin kiistaton totuus - mutta noista tosiasioista skriptaajat ilmanmuuta tekevät sen väärän johtopäätöksen ettei BASH desimaalilaskuja osaa laskea. Se kyllä kiinnostaa ovatko 'advanced guidet' uskoneet myös - minä en usko kaikkien oppineiden ammatti-ohjelmoijien syyllistyneen moiseen sillä esimerkiksi vuosia sitten peräti verkkosivulla https://www.anandtech.com/ asia selvitettiin maallikollekin ymmärrettävällä tavalla - nykyään silläkään verkkosivulla ei ole edes halua perusteita selvittää, ei niitä monikaan halua lukea eikä varsinkaan niiden lukemisesta maksaa. Sensijaan lierot maksaa siitä ettei asioista kerrottaisi - mainostuloiksi väittävät.

Yhteenvetoa BASH:in mustamaalaamisesta:

1. Sen aiheuttaminen ettei kukaan ole yrittänytkään suorittaa desimaalilaskuja. Nopeat desimaalilaskut ovat aivan välttämättömiä jotta kieli menestyisi.
2. 'Kirjastokielto'. Kirjastot ovat nekin aivan välttämättömiä - ei niitä tosin voitu estää käyttämästä sillä kirjasto-käsite kuuluu BASH:in perus-palikoihin - mutta mustamaalata niitä voitiin ja poistaa kirjasto-osoitin.
3. Väite että funktioiden nimiparametrit eivät toimi. Ilmanmuuta ne toimivat, mutta niiden saaminen käyttöön vaatii oikeita taikasanoja - ja ainoa mitä virtuoosit voivat puolustustukseen sanoa etteivät he tunteneet yhtäkään noista monista taikasanoista - mutta sitä ei kukaan usko sillä niitä löytyy myös kokeilemalla - eikä mahdollisia käskyjä ei ole kovin montaa.
4. Nopeiden käskyjen piilottaminen. Nopeat käskyt eivät ainoastaan ole 10-kertaa nopempia kuin uudet vaan yksinomaan nopeista käskyistä kootut funktiot tulkataan kerralla jolloin nopeus-hyöty on vielä suurempi. Kyllä uusissakin käskyissä paljon hyvää on mutta useimmien ne yrittävät ratkaista sellaisia ongelmia joita ei käytännössä kohtaa - ehkäpä ongelma onkin vain se ettei kehdata myöntää että tuli hölmöiltyä ja nyt täytyisi alkaa taas käyttämään niitä vanhoja - ei tosiaan ainoa kerta kun on hölmöilty tällä samalla tavalla.
5. eval-käskyn 'kieltäminen' - mutta BASH:in omassa ~/.bashrc-tiedostossa on tällähetkellä kolme eval-käskyä joten BASH:in kehittäjät eivät virtuoosien väitteitä usko.
6. Saatu kaikki uskomaan että BASH tarvitsee aina ensimmäiseksi rivikseen käskyn: #!/bin/bash tai että skriptistä täytyy tehdä tiedosto jolle ehdottomasti tåytyy antaa suoritus-oikeus - tai että skriptin käskyt kirjoitetaan aina kukin omalle rivilleen - tai ehtolauseet peräti useammalle. Varmaan niin on parasta tehdäkin kun aloittaa skriptaamisen mutta voisi sentään edes vihjata että ei niin kannata jatkossa toimia kuin hyvin harvoin. Paitsi ehkä wintoosassa ja semmoisissa.

- tottakai kaikkeen on myös ihan kunnollisia syitä - esimerkiksi tietokonehan on aina turvallisuusriski ja vanhentuvat ohjelmat eritoten. Asioita tosin painotetetaan ihan ihmeellisesti.
- tarkoitus saattoi siis olla hyvä ja ettei vaan tajuttu mitä tuhottiin. Mutta eipä ollut ainoa kerta kun huonompi voittaa parempansa. Tällähetkellä BASH on kylläkin sotkettu niin syvälle suohon  ettei se varmankaan voi nousta sieltä koskaan.

51
BASH:in muisti on globaali - elikä jokainen muuttuja jonka arvoa muutetaan jossakin muuttuu kaikkialla muuallakin: mitä pääohjelmassa tehdään vaikuttaa kaikissa funktioissakin - ja mitä funktioissa tehdään näkyy pääohjelmassa ja toisissa funktioissakin - on kyllä parikin harvoin käytettyä tapaa eristää funktio - eli tehdä hiekkalaatikko, se on jo BASH:in idea tai vieläkin vanhempi - mutta niistä myöhemmin. 

Siis jos viitataan eri paikoissa saman nimiseen muuttujaan niin viitataan varmasti samaan muuttujaan - tässä täytyy painottaa että sama tekstijono voi toisessa paikassa olla muuttuja ja toisessa paikassa vain tekstijono - siis kun määrätään: echo $tekstijono niin yhdessä paikassa tulos voi olla jotain_ muuta _kuin_0 ja toisessapaikassa 0. Jos siis halutaan tehdä jossain funktiossa temppuja saman nimiselle muuttujalle jokakerta niin parametreja ei silloin tarvita ollenkaan - jopa se () voidaan jättää funktiokutsusta pois - tosin ei siitä ole haittaa jos sen kirjoittaa:
Koodia: [Valitse]
function koe { echo $a ;}; unset a; a=543; koe $a #-> tulostuu 543
function koe { a=543 ;}; unset a; koe a; echo $a #-> tulostuu 543
- 'unset muuttuja' ainoastaan palauttaa muuttujan arvoksi tyhjän - siis antaa sille saman arvon joka sillä on avattaessa pääte, elikä: ''. Muuten sotkisi se että BASH ei nollaa muuttujiaan ohjelmansuorituksen lopuksi vaan ainoastaan senhetkisen pääteistunnon lopuksi - siis pääteistunto on isä ja kaikki muut sen mukuloita - päätteellä on muuten myös määre: #!/bin/bash ja suoritusoikeuskin eivätkä sen mukulat niitä enää yleensä tarvitse.
- voihan käskyn unset jättää poiskin ja avata jokaisen käskyn suorituksen alussa uusi pääte.
- muuttujalle annettu arvo siirtyy toiseen skriptiinkin ellei päätettä välillä sammuteta - jos ei tiedä että näin tapahtuu on skriptaaminen tuskallista koska skriptit näennäisesti tekevät mitä sattuu.
- funktiossa voidaan muuttuja määritellä paikalliseksi - siis vaikka jossain muualla olisi saman-niminen muuttuja ei funktion muuttujan muuttaminen vaikuta siihen. Pääohjelmassa ei voida määritellä paikallisia muuttujia.

Jos halutaan jossain funktiossa tehdä samat temput mille muuttujalle/muuttujille tahansa nimestä välittämättä niin siirretään muuttuja normaalisti arvoparametrina.
- vain nimiparametri on palautettavissa missähyvänsä kielessä - arvoparametrin palauttamisesta puhuminen on todella himmeää - ei yksikään virtuoosi saisi moiseen puheeseen syyllistyä.
- nimiparametreja ei aikaisemmi osattu käyttää sillä kun funktioon siirretään muutujan nimi niin välittömästi siirron jälkeen se on vain tekstijono joka ei viittaa mihinkään. Se täytyy erikseen käskeä viittaamaan johonkin arvoon tai arvojoukkoon. Kuinka se tehdään yritetään seuraavassa kertoa niin että jokainen saisi selityksestä selvää:
Koodia: [Valitse]
arvoparametri: function koe () { echo $1 ;}; unset a; a=543; koe $a  -> tulostuu: 543  (tämä on se toiminne jota normaalisti käytetään )
nimiparametri: function koe () { $1=543 ;}; unset a; koe a; echo $a -> tulostuukin: =543: command not found . Mutta kyllä funktio toimii oikeinkin pienin muutoksin:
1. numeromuuttujalla:
Koodia: [Valitse]
function koe () {  let $1=543  ;}; unset numero; koe numero; echo $numero
2. tekstijonomuuttujalla:
Koodia: [Valitse]
function koe () {  read<<<höh $1  ;}; unset tekstijono; koe tekstijono; echo $tekstijono
3. matriisilla:   
Koodia: [Valitse]
function koe () {  < <(echo -e 1\\n2\\n3) readarray $1  ;}; unset matriisi; koe matriisi; echo ${matriisi[*]}
   
- hommassa on monia salahautoja; esimerkiksi jos kirjoittaakin <(echo 1 2 3) niin ei muodostukaan matriisia vaan tekstijono - ja tuo matriisi-vai-tekstijono on vaikeasti havaittava tilanne joka saa skriptaajan repimään pelihousunsa - eikä varsinaisesti virhe vaan joskus harvoin jopa toivottua - mutta normaalikäytössä senjälkeen mikään ei tunnu onnistuvan - yksi keino tilanteen havaitsemiseksi on kirjoittaa:
Koodia: [Valitse]
echo ${matriisi[0]}
joka tulostaa vain jäsenen nolla (siis katsotaan muuttuuko tuloste tilanteesta:
Koodia: [Valitse]
echo ${matriisi[*]})
Tai voisihan matiisin tulostaa niinkin kuin matriisi oikeastaan pitäisi tulostaa, joko:
Koodia: [Valitse]
printf "%s\n" "${matriisin_nimi[*]}"    tai: echo -e ${matriisin_nimi[*]/#/\\n}
- mutta eihän tuommoisia rotlia viitsi aina kirjoittaa ja monirivinen tulostekin on epämiellyttävä.

- vaikka nimiparametrit voisikin palauttaa niin parametrien palauttaminen olisi BASH:issa silkkaa ajanhukkaa - ja siksi BASH ei parametrien palauttamista edes osaa. Tehty hommahan tämä  väärinkäsitys on joten virtuoosit ovat täysin varmasti sen aikoinaan tunteneet ja sitä käyttäneetkin.
 
- kun virtuoosit havaitsivat että:
Koodia: [Valitse]
eval \$$1
palauttaa myös viittaukset niin heille tuli kiire vakuuttaa että BASH:issa käsky eval on pahis eikä sitä saa käyttää. Eivät kai tienneet että onhan niitä muitakin samaan tehtävään kelpaavia käskyjä.
- esimerkiksi: BASH pitää kirjaa muuttujistaan ja antaa kirjanpitonsa käytettäväksi kaikkialla käskyllä: declare - se toimii myös funktioissa. Ja se on varmaankin jopa tehty siihen viittausten palauttamiseen - ja parametrien palauttamisen tarpeettomaksi tekemiseen.

52
Tämä postaus meni uusiksi:

Vasta tulkki tekee päätöksen siitä minkä roolin se muuttujalle milloinkin antaa: käsitetäänkö muuttuja niinkuin se olisi numero-arvo, tekstijono, jonkinsortin matriisi ... vai annetaanko sille merkitys käskynä, funktiokutsuna, matemaattisena kaavana, laskeeko se ja totettaako se totuusarvon, joukkotoiminteen, ascii-arvon, bitti-operaation, minkä hyvänsä. Myös matriisien jäsenet voivat toimia minähyvänsä näistä, myös assosiatiivisten matriisien jäsenet.

BASH on sekin käsky-pyramidi - jokainen käsky voidaan rakentaa hyväksikäyttämään kaikkea jo tehtyä - niin BASH:in omia käskyjä kuin tehtyjä funktioita - siinäpä työsarkaa tutkia kuinkahyvin sillä vaihtoehtoja on niin pajon ettei ole mahdollista kokeilla niistä pientä osaakaan. Lisäksi tässävaiheessa kannattaa tehdä mitä kummallisempia skriptejä joilla ei tällähetkellä tunnu olevan käyttöä - myöhemmin käyttöä todennäköisesti tulee. Käsitelläänpä joitain noista toiminnoista antamalla hommasta esimerkki:

1. Toimiminen käskynä, esimerkki1: todella merkittävästä ja isosta mutta väärin toimivasta skriptistä koetetaan usein etsiä vikapaikkaa vianetsintä-ohjemalla, debuggerilla. Kyseessä on siis homma jossa parempaa koetetaan korjata huonommalla - onhan debuggerien kanssa kiva leikkiä mutta eipä hommassa skriptaamisen kannalta paljon järkeä ole. Mutta on yksi helppo mutta silti tosihyvä debuggaus-menetelmä joka sopii niin pieniin kuin suuriinkin skripteihin: kirjoittaa skriptissä erittäin runsaasti välituloksia. Mutta et varmasti halua saada jokaisesta pikkuhommastakin paria sivua välitulosteita joten eikun skriptin valmistuttua poistamaan kaikki skriptin miljoona välitulostusta. Jonka jälkeen huomaat skriptiä vähänaikaa käytettyäsi että jokin on edelleen pielessä - joten eikun palauttamaan ne miljoona echoa muuttujineen.  Parin poisto-palautus kierroksen jälkeen oletkin kypsä. Tässä tulee apuun se että BASH:in muuttujat voivat toimia käskyinä: echon tilalle laitetaankin muuttuja jonka arvona on käsky echo tai käsky : joka ei tee mitään.
- siis: tulosta=echo  tai: tulosta=: . Koska : on sekin käsky tulkitaan perässätulevat tulostettaviksi tarkoitetut muuttujat parametreina ihan niinkuin echo:nkin kanssa.

Toimiminen käskynä, esimerkki2:
Koodia: [Valitse]
a='echo nyt kiristää pipoa'; $a   # tästä ei tule huomautusta niinkuin silloin tulisi jos a olisi tavallinen muuttuja - mutta ensiksi tässä tuleekin käsky.
a='eval set $b'; b='jostakin tekstistä tässäkin puhutaan'; $a; echo $3' '$4' '$1' '$2   
2. Toimiminen funktiokutsuna:
Koodia: [Valitse]
function koe () { echo täällä ollaan $1 ;}; a=koe; $a ' tälläkin kertaa' # siis tällaiseenkin käskyyn voi liittää parametrit ihan normaalisti.
3. Toimiminen matemaattisena kaavana:
Koodia: [Valitse]
function ratkaisija () { echo $1 | tr '[]' '()' | sed 's/x/'$2'/g' | bc -l ;};
a=x^2+x+1; ratkaisija $a 2.54   
- siis ratkaistaan yhtälö $a:n arvolla 2.54 - mutta kaavaksi voi kirjoittaa vaikka millaisen litanian sellaisia käskyjä jotka bc osaa ratkaista ja kirjoittaa arvoksi mitä lystää.
- kaavan suluiksi täytyy kirjoittaa hakasulut - esimerkiksi kun halut laskea jotain trigonometrista (luvut radiaaneissa): 
function ratkaisija () { echo $1 | tr '[]' '()' | sed 's/x/'$2'/g' | bc -l ;}; a=.2; ratkaisija s[$a]/c[$a]  # sin.2/cos.2  - taitaa olla tan.2 ?

- toisten käskyjen lomassa näitä käskyjä käytetään esimerkiksi näin: tarkistellaan matematiikkamoottoriin lähetettävää lausetta:
Koodia: [Valitse]
$(($apu$merkki(( ${desimaaliosa:$2:1} >= 5 )) ))
-> BASH kokoaa ensin sellaisen tekstijonon kuin käsky määrää. Lähetettäessä tekstijono matematiikka-moottoriin sen osista jokainen määritellään sentyppisesti kuin hyvä tulee: siis jokainen osa voi olla tarpeen mukaan joko: tekstijono, numero, operaattori, laskutoimitus, funktiokutsu tai looginen arvo 0 tai 1 -> kaikki mitä sinne matematikka-moottoriin lähetetäänkin tuntuu toimivan.

- mutta näitäkään toimintoja ei tiedetä hyödyntää sillä niistä ei opeteta missään. Epäoikeudenmukaisesti sanottuna: miksi opettaa kun on helpompaa sanoa ettei BASH osaa.

53
Neliöjuuri kannattaa BASH:issa laskea luvun tieteelllisestä esitys-muodosta. Tieteelliseen esitysmuotoon ei kannata siirtyä käskyllä printf vaan tehdä funktio joka tekee saman muunnoksen täsmälleen samallatavoin ja toimii erittäin nopeasti - sillä myös käsky printf on suunniteltu hukkaamaan aikaa - printf on kyllä erittäin monipuolinen käsky sillä sitä voi käyttää myös säilyketölkkien avaamiseen.   
 
Koodia: [Valitse]

function sqrt () { apu=${1##+(0)}; [[ $apu ]] || { echo 0; return ;} # lausetta tarvitsee vain kun lasketaan:  sqrt 0  . Tai: sqrt 00000....
function to_sci () { [[ $1 =~ e ]] && echo $1 || { apu=$1; [[ ${apu:0:1} = - ]]  && merkki=- || merkki=''; apu=${apu//[-+]/}; apu=${apu##+(0)}; kokonaisosa=${apu%%.*};[[ ${apu//[^.]/} ]] && desimaaliosa=${apu##*.} || desimaaliosa=0; [[ $kokonaisosa ]] && { expo=${#kokonaisosa}; echo $merkki${kokonaisosa:0:1}.${kokonaisosa:1}$desimaaliosa'e'$(($expo-1)) ;} || { apu=${desimaaliosa%%[1-9]*}; ekspo=$((${#apu}+1)); desimaaliosa=${desimaaliosa:$(($ekspo-1)):1}.${desimaaliosa:$(($ekspo))}; echo $merkki$desimaaliosa'0e'-$ekspo ;} ;} ;}

apu=$(to_sci $1); mant=${apu%%e*};apu=${apu##*e}; apu=${apu//+0/}; apu=${apu//-0/-}; (( $apu & 1 )) && kerroin=10 || kerroin=1; expo=$(($apu/2));   
in=${mant//./}"000000000000000000"; in=${in:0:17}
sqrt=2147483648  # 2^31 
delta=1073741824 # 2^30 # tämä tulee voida jakaa jatkuvasti kahdella siten että jako menee aina tasan 
for ((i=0;i<30;i++)); do
   x=$(($sqrt*$sqrt/$kerroin))
   (( $x>=$in )) && sqrt=$(($sqrt-$delta)) || sqrt=$(($sqrt+$delta))
   delta=$(($delta/2)) #; (( $delta )) || delta=1  # echo $((1/2)) lasketaan nollaksi - oikein pyöristettynä se on 1
done
#echo -n 'bc:stä ';bc -l <<< "sqrt($juurrettava)" # bc ei puutu laskuihin mitenkään vaan se laskee varmasti oikean tuloksen johon verrata. Mutta kun lauseen kommentoi niin silloin ei varmasti puutu.
apu=00000000000000000000000000000000000000000000000000$sqrt'000000000000000000000000000000000000000000000';apu=${apu:0:$((51+$expo))}.${apu:$((51+$expo))}
luku=${apu##+(0)}; [[ ${luku//[^.]/} ]] && luku=${luku%%+(0)}; echo ${luku%.}  ;}
   
 # esimerkkilaskuja: 
juurrettava=27.456789;time sqrt $juurrettava

juurrettava=2.7456789e-99;time sqrt $juurrettava
juurrettava=2.7456789e-98;time sqrt $juurrettava

juurrettava=2.7456789e+99;time sqrt $juurrettava
juurrettava=2.7456789e+98;time sqrt $juurrettava

juurrettava=0000000000000;time sqrt $juurrettava


***

Täytyy myöntää että nykytilanne turhauttaa, sillä kyse ei suinkaan ole siitä että nämä skriptini kelpaisivat käytettäväksi. Vaan kyse on siitä että ne kertovat että me uskomme kaiken mitä meille kerrotaan silloinkin kun se kerrottu on mahdotonta.

Esimerkiksi äskeinen sqrt samoinkuin aikaisemmin esitetty 36 numeroinen liukuvanpilkun kertolasku kerro18 toimivat BASH-skripteiksi salaman-nopeasti ja moitteettomasti jo kaksi vuotta sitten - ja mikäli ne olisi kasattu jo kolmekymmentä vuotta sitten kun BASH:ia alettiin romuttamaan olisivat ne toimineet jo silloin - sillä niissä ole mitään uutta - päinvastoin uudemmat käskyt on ilmeisesti tahallaan tehty sellaisiksi että nuo paremmat menetelmät alkavat nikotella jos mukaan otetaan jotain uudempaa. Mutta silti virallinen opetus on vieläkin etteivät tämmöiset BASH:issa edes toimi.

Nyt alkaa hiukan kajastaa syy miksi BASH:ista levitetään suoranaisia valheita: sen aikaansaa halu osoittaa että BASH on täysin kelvoton verrattuna muihin skriptikieliin niinkuin esimerkiksi: sed, awk, bc, Perl ja Python - ettei vaan pääsisi syntymään sellaista tilannetta että BASH kilpailisi niiden kanssa. Nimittäin silloin aikoinaan BASH ei tosiaankaan ollut kelvoton muihin verrattuna - nykyään BASH on kelvoton kolmenkymmenen vuoden heitteille jättämisen seurauksena - mutta onko se kelvottomuuskin paljolti näennäistä?

Myös BASH:in tarina on näemmä pelkkää historian kertausta - samoja tarinoita kerrotaan yhä uudelleen sillä uusia ei osata tehdä: tässäkin virtuoosit väittävät etteivät he ole tehneet mitään mutta kuitenkin pesevät kätensä kuin Pontius Pilatus.

54
Edellisessä tehtävässä printf:n tehtävänä on muuntaa normaalilla tavalla esitetty luku tieteelliseen esitys muotoon: x.xxxxx e yy. 'Nopeilla käskyillä' tehtynä muunnos tapahtuu noin kuusikertaa nopeammin. Ei se koodin laadusta johdu vaan ohjelmien koosta: printf on 50K järkäle ja to_sci on vajaa kilo:
[
Koodia: [Valitse]
function to_sci () { apu=$1; [[ ${apu:0:1} = - ]]  && merkki=- || merkki=''; apu=${apu//[-+]/}; apu=${apu##+(0)}; kokonaisosa=${apu%%.*};[[ ${apu//[^.]/} ]] && desimaaliosa=${apu##*.} || desimaaliosa=0; [[ $kokonaisosa ]] && { expo=${#kokonaisosa}; echo $merkki${kokonaisosa:0:1}.${kokonaisosa:1}$desimaaliosa'e'$(($expo-1)) ;} || { apu=${desimaaliosa%%[1-9]*}; ekspo=$((${#apu}+1)); desimaaliosa=${desimaaliosa:$(($ekspo-1)):1}.${desimaaliosa:$(($ekspo))}; echo $merkki$desimaaliosa'0e'-$ekspo ;} ;}
# mutta ei ääretöntä kovin nopeasti saa testattua:tämä vaatii uskomattoman monia tarkituksia - tämä on vain alkua:
to_sci 1
to_sci 10
to_sci 010
to_sci 1000000
to_sci +1000000
to_sci -1000000
to_sci .1
to_sci .01
to_sci 0.01
to_sci -.01
to_sci +.01
to_sci -0.01
to_sci -0.01
to_sci 123456789012345678901234567890123456789012345678901234567890   # nimenomaan eksponentin tulee olla oikein
to_sci .00000000000000000000000000000000000000000000000001                      # nimenomaan eksponentin tulee olla oikein
to_sci .00000000000000000000000000000000000000000000000001234567890
to_sci -123.0124
to_sci -123.0000000000123

- näillä itsetehdyillä ohjelmilla on yksi voittamaton piirre valmisohjelmiin verrattuna: jos itsetehty toimii väärin niin sitten se korjataan. Mutta valmisohjelmalle et voi mitään.
- näillä itsetehdyillä on rajoitetussa määrin jopa toivottavaakin että joskus toimii väärin: tarkoituksenahan on aivojumppa. Ei merkitse kovin paljoa sekään että onnistuuko korjaamaan toiminnan oikeelliseksi - sekin riittää että yrittää.
- on inhottavaa ja pelottavaa tehdä uudentyyppisiä kokeita - sillä aivan varmaa on että jotakin on jäänyt huomaamatta ja varmasti joskus kompastuu - ja etukäteen ei voi tietää saako vikaa koskaan korjattua. Mutta todella tuntee elävänsä.

55
 Olipa kerran suurvisiiri joka halusi kalifiksi kalifin paikalle - siis tässätapauksessa BASH:in paikalle. Mutta BASH oli aivan liian vahva jotta sitä olisi kannattanut haastaa. Siispä BASH:ia täytyi huonontaa paljon. Valhe on viisainta perustaa totuuteen joten kuka nämä tekikin puhui aina täysin totta mutta asetti sanansa niin taitavasti että käyttäjät älyttivät itsensä:
1. luulemaan etteivät esimerkiksi desimaalilaskut BASH:issa onnistu eivätkä siten edes yrittäneet tehdä niitä.
2. luulemaan että BASH kuuluu niihin mölleihin ohjelmiin joiden täytyy parametreja palauttaa - siis on ihan totta ettei BASH osaa parametreja palauttaa muttei palauttamisessa mieltä olekaaan.
3. hän myös häivytti desimaalilaskentaan pystyvät käskyt historian hämäriin uudella käskykannalla joka on desimaalilaskuissa tuhansia kertoja hitaampaa. Käytännössä tämä johtaisi siihen että vaatimatonkin desimaalilasku kestäisi uudella käskykannalla 10 minuuttia eikä kukaan viitsi niin kauaa yhtä pientä laskua tehdä.

Tässä on yksi desimaalilaskujen toteutus jonka sain nyt moitteettomaan kuntoon - sanotte varmaan ettei tässä mitään desimaaleja ole - johon vastaan että eihän niitä muulloinkaan ole, mielikuvahan se desimaali on (tai tieteelisemmin selitettynä: desimaali on asteikko jossa luku esitetään. Ja  asteikko määritellään numero-laskuista hieman erillään - tässä tuo printf on siinä merkityksellisin tekijä.) :
Koodia: [Valitse]
function sqrt () {
apu=$(LC_NUMERIC=C printf "%.17e\n" $1); mant=${apu%%e*};apu=${apu##*e}; apu=${apu//+0/}; apu=${apu//-0/-}; (( $apu & 1 )) && kerroin=10 || kerroin=1; expo=$(($apu/2));   
in=${mant//./}"000000000000000000"; in=${in:0:17}
sqrt=2147483648  # 2^31 
delta=1073741824 # 2^30 # tämä tulee voida jakaa jatkuvasti kahdella siten että jako menee aina tasan 
for ((i=0;i<32;i++)); do
   x=$(($sqrt*$sqrt/$kerroin))
   (( $x>=$in )) && sqrt=$(($sqrt-$delta)) || sqrt=$(($sqrt+$delta))
   delta=$(($delta/2)); (( $delta )) || delta=1  # echo $((1/2)) lasketaan nollaksi - oikein pyöristettynä se on 1
done
#echo -n 'bc:stä ';bc -l <<< "sqrt($juurrettava)" # bc ei puutu laskuihin mitenkään vaan se laskee varmasti oikean tuloksen johon verrata. Mutta kun lauseen kommentoi niin silloin ei varmasti puutu.
apu=00000000000000000000000000000000000000000000000000$sqrt'000000000000000000000000000000000000000000000';apu=${apu:0:$((51+$expo))}.${apu:$((51+$expo))}
luku=${apu##+(0)}; [[ ${luku//[^.]/} ]] && luku=${luku%%+(0)}; echo ${luku%.}  ;}


juurrettava=9;time sqrt $juurrettava
tai: juurrettava=73.456789e-3; time sqrt $juurrettava

- neliöjuuri luvuista: 4,9,16,25...1152921504606846976 esitetään aina kokonaislukuna sillä laskettaessa niiden desimaaleiksi tulee 0.
- eihän tämmöisellä skriptillä enää nykyisin tee mitään. Mutta tarkoitus onkin varmistautua taas vähän lisää siitä että puhuu asiaa väitteessä että on sössitty tahallisesti.

HUOMAUTUS: ei ääretöntä lukualuetta ole nähtävästi mahdollista hoitaa moitteettomasti: esimerkiksi lasku luvuilla joissa eksponentti oli -8 tai -9 päätyivät virheeseen - sitä pienemmät ja suuremmat luvut toimvat. Siitä nämä 'nopeat käskyt' ovat mukavia että niitä saa tulla lisää monta ennenkuin se näkyy missään joten uusittu koodi on yhtä nopea kuin vanhakin.

56
BASH oli kauan sitten kunkku - se jopa koettiin nopeaksi vaikka hidashan se on aina ollut eikä sen kykyjäkään ole koskaan paljoa hyödynnetty tietokoneen hoitamisen lisäksi - mutta eivät ne vaatimuksetkaan aikoinaan kovin kaksisia olleet. Mutta sitten virtuoosit siirsivät BASH:in satuun nimeltään: kuninkaalla ei ole vaatteita. Saman-nimisessä oikeassa sadussa tarvitaan pikkupoika sanomaan että alastihan kuningas on eikä komeasti vaatetettu niinkuin virtuoosit väittävät - ja senjälkeen kaikki muutkin uskalsivat myöntää etteivät he mitään vaatteita näe. Mutta BASH on päinvastainen tapaus: siinä virtuoosit väittävät ettei BASH:illa ole vaatteita. On melkein mahdotonta että tavis väittäisi vastaan ja sanoisi että BASH:in vaatteet ovat kyllä varsin resuiset mutta sittenkin vaatteita on - sillä häntä ei joko uskottaisi tai vaiettaisiin väitteet kuoliaaksi joten väittäjältä menisi maine - mutta minun maineeni meni jo kauan sitten joten voin väittää mitävaan.

Kyllä virtuoosit puheistani huolimatta ovat mielestäni hatun-noston arvoista ja pätevää porukkaa. Sitä en tosin käsitä miksi nimenomaan BASH on heidän hampaissaan.

On totuttu mollaamaan BASH:ia tyylliin: ei BASH osaa. Ei tietenkään osaa - mutta se on näennäistä sillä kirjastoissahan ne paremmat hommat luuraa ja virtuoosithan ovat asettaneet 'kirjastokiellon'. Mutta kyllä BASH sittenkin osaa - skriptin yhteydessä olevissa funktioissa. Onpa hankalaa mutta niin virtuoosit tahtovat.

Esimerkiksi ensimmäisen asteen funktiot - on omituista ettei BASH:in yhteydessä tästäkään edistyneemmästä ominaisuudesta kerrota mitään eikä niistä oikeastaan puhutakaan missään mitään - paitsi sana siellä ja toinen täällä sillä virtuoositkaan eivät malta olla ihan hiljaa edistyneemmästä BASH:ista. Tämäkin yhteenveto on koottu ziljoonilla verkkosivuilla olleista tiedoista.

'Ensimmäisen asteen funktiot' toimivat hyvin BASH:issakin. Mutta nimitys 'ensimmäisen asteen funktiot' on hämäävä ilmaisu sillä kyseessä on tulkin kyky tulkita muuttujalta näyttävä funktio-kutsuna mikäli siinä olevan tekstijonon ensimmäinen sana on sama kuin jonkin funktion nimi - eteenpäin viittaukset sallitaan - ja pitää muuttujassa mahdollisesti olevia loppuja arvoja tuon funktion parametreina.

Tuo parametri hommeli on muuten ovela juttu: luku on aina arvoparametri ja niin on tekstikin yleensä - mutta tekstijono tulkitaan nimiparametriksi mikäli saman-niminen muuttuja löytyy skriptistä jostain paikasta, vaikka myöhemminkin määriteltynä - suokaa anteeksi virtuoosit, eihän tämmöistä saisi kertoa - esimerkiksi tässätapauksessa muuttujasta jonka nimi on:numero puhutaan vasta funktiokutsun jälkeen joten tulkin täytyy sittenkin käydä skripti läpi ainakin kahdesti. Esimerkki:
Koodia: [Valitse]
function koe () { $1 numero; echo $numero ;}       # funktion nimeltä:koe parametri $1 on tekstijono:joo joka tulkitaan funktiokutsuksi ja muuttuja nimeltään numero sen nimi-parametriksi.
function joo () { readarray $1 < <(echo 12345) ;}  # tässä funktiossa $1 on nimiparametri nimeltään: numero - ja sille annetaan arvo 12345. Kun on palattu funktioon: koe ja jatketaan siinä
                                                   # olevan skriptin suoritusta välittömästi kutsu-paikan perästä ja tulostetaan muuttujan nimeltä:numero arvo käskyllä: echo $numero.
# annapa käsky:
koe joo 
# jolloin tulostuu: 12345
- siis vasta kääntäjä tekee päätöksen sitä minkä merkityksen se muuttujalle milloinkin antaa - tämä on siis aivan samanlainen tapaus kuin se jossa matematiikkamoottori tulkitsee sille annetun tekstin niinkuin hyväksi havaitsee.
- kun selittää kuinka joku menetelmä toimii kannattaa muuten selittää mahdollisimman paljon - silloin selittää myös itselleen joten tajuaa paremmin onko kaikki oikein. Ja vaikka selityksestä on useimmille lukijoilla pääosa itsestään selvää niin varmasti on joku sellainenkin lukija joka tarvitsee selitystä myös niissä itsestään selvissä kohdissa - ja sieltä pohjilta ne tulevat virtuoositkin kasvavat.

57
Funktion onkoekasuurempi lyhyempi versio osaa 'laskea' 32 numeron tarkkuudella 0.5 millisekunnissa. Silti lasku kuulunee skriptilajiin 'party trick' sillä eihän tämmöistä uskalla nykyään käyttää - mutta silloin kolmekymmentä vuotta sitten tämä olisi ollut kova juttu. Ja tiedähäntä mitä tästä saisi jalostettua - aikanaan se olisi kovastikin kannattanut mutta ei taida kannattaa enää tänäpäivänä. Tämä taas sarjassamme 'kummallisia sattumuksia'.
Koodia: [Valitse]
function onkoekasuurempi () { luku1=${1//[^-.0-9]/}; luku1=${luku1%.}; luku2=${2//[^-.0-9]/}; luku2=${luku2%.}; [[ ${luku1//[^-]/} ]] && m1=- || m1=''; [[ ${luku2//[^-]/} ]] && m2=- || m2=''; (( 10#0${luku1//.*/} == 10#0${luku2//.*/} )) && { desimaalit1=${luku1//*./}000000000000000000; desimaalit2=${luku2//*./}000000000000000000; (( $m1${desimaalit1:0:18} <= $m2${desimaalit2:0:18} )) && echo 0 || echo 1 ;} || { (( 10#0${luku1//.*/} <= 10#0${luku2//.*/} )) && echo 0 || echo 1 ;} ;} 
onkoekasuurempi 1234567890123456*1234567890123456 1524157875323881726870921383936 -> tulostaa 0 niinkuin pitäisikin
onkoekasuurempi 1234567890123456*1234567890123456 1524157875323881726870921383937 -> tulostaa 1 niinkuin pitäisikin

- 1234567890123456*1234567890123456 = 1524157875323881726870921383936
- ajatelkaa BASH:in monipuolisuutta - lukujen vertaamiseen tarkoitettu funktio on samalla myös osittain pätevä laskin - tekemättä kertakaikkiaan mitään.
- kyllä muutkin luvut toimivat samoin joten saisihan tästä tehtyä monenmoista.

58
Nyt alkoi selvitä miksi BASH on siinä tilassa kuin se on kun luin Ruotsin liikennekaoksesta, Norjan suojelupoliisin varoituksista jne jne. Asiat tiedostetaan ja konferensseja pidetään - mutta itse ei varmasti tehdä mitään eikä anneta toistenkaan auttaa vaan kielletään kaikkia tekemästä mitään sillä siitähän seuraisi että saattaisi joutua johtamaan koko hommaa joten viini, laulu ja naiset täytyisi unohtaa. Vastuuta pakoillaan vaikka ulkopuoliset selväsanaisesti sanovat että katastrofi on jo muodostunut.

Samasta syystä väitetään esimerkiksi ettei BASH tunne desimaalilukuja? Tämän aasin-sillan kautta pääsen selvittämään desimaalilukujen vertaamista taas kertaalleen mutta tälläkertaa  matemtiikkamoottorin avulla - se '$((1+2))' - sillä mikäli joskus tekisi BASH-skriptin matematiikkaa varten tulisi sen vertailu-funktion olla tosinopea - bc:t ja muut voit unohtaa sillä vertailun täytyy olla tehty jo ennenkuin ne edes aloittavat. Ja 'nopeilla käskyillä' tehdyt skriptit ovatkin tosinopeita vaikka niissä olisi monia tai vielä useampia käskyjä.

Lukujen vertaaminen kannattaa tehdä matematiikkamoottorilla sillä se on paljon nopeampaa kuin tekstijono-vertailu - niin nopeaa ettei sitä saa mitattuakaan - ja myös desimaaliluvut täytyy hyväksyä mutta koska matematiikkamoottori ei hyväksy desimaaleja täytyy lukujen kokonais- ja desimaaliosat verrataan erikseen. Silloin toiminta on seuraavanlaista:

1. Kaikki muu kuin numerot, miinus-merkki ja desimaalipiste poistetaan - tai mikäli desimaalipiste on viimeisenä niin sekin poistetaan. Kokonaislukujen miinus-etumerkki laitetaan muistiin mutta etumerkkiä ei poisteta. Desimaaliosien loppuun lisätään nollia niin monta että ne ovat 18 numeroisia sillä desimaaliosien vertaaminen ei mene oikein ellei molemmissa desimaaaliosissa ole numeroita yhtämonta.
2. Mikäli kokonaisluvut ovat yhtäsuuret tulee lopputulos yksinomaan desimaalien vertaamisesta. Desimaalien vertaamisessa täytyy huomioida kokonais-osien etumerkkit.
3. Mutta jos kokonaisluvut eivät olleetkaan yhtäsuuria niin vertailutulos tulee yksinomaan kokonaislukujen vertaamisesta. Etumerkit huomioidaan automaattisesti.

Tämmöisistä täytyy ehdottomasti olla funktio kirjastossa sillä hermothan tämmöisiä väsätessä menee, aikaa haaskaantuu ziljoonaan kokeeseen ja sittenkin sielua jää kaivertamaan tieto siitä että ihan varmasti jotakin on jäänyt huomioimatta tai että on luonut sellaisen skripti-hirviön ettei pysty enää hallitsemaan sitä ... :
Koodia: [Valitse]

function onkoekasuurempi () { luku1=${1//[^-.0-9]/}; luku1=${luku1%.}; luku2=${2//[^-.0-9]/}; luku2=${luku2%.}; [[ ${luku1//[^-]/} ]] && m1=- || m1=''; [[ ${luku2//[^-]/} ]] && m2=- || m2=''; (( 10#0${luku1//.*/} == 10#0${luku2//.*/} )) && { desimaalit1=${luku1//*./}000000000000000000; desimaalit2=${luku2//*./}000000000000000000; (( $m1${desimaalit1:0:18} <= $m2${desimaalit2:0:18} )) && echo 0 || echo 1 ;} || { (( 10#0${luku1//.*/} <= 10#0${luku2//.*/} )) && echo 0 || echo 1 ;} ;}

onkoekasuurempi 12345679012345678.12345679012345678 12345679012345678.12345679012345678
onkoekasuurempi .1 0.1
onkoekasuurempi 1 .1
onkoekasuurempi 1 1
onkoekasuurempi +1 1
onkoekasuurempi -1 -1
onkoekasuurempi -1 1
onkoekasuurempi 1 -1
onkoekasuurempi 1.9 1.89
onkoekasuurempi 1.000000000000000001 1
onkoekasuurempi 2. 1.   # tai: onkoekasuurempi 2$ 1$ tai: onkoekasuurempi 2km/t 1km/t ...
- sekä desimaaliosat että kokonaisosa saavat olla 0 - 18 numeroisia, desimaalipilkku saa olla tai sitten ei ja verrattavat luvut voivat olla pelkkkiä numeroita tai niissä saa olla mukana mitähyvänsä lukuja tarkemmin määrittelevää tekstiä.
- tuleeko mieleen presedenssi kun antaa käskyn: luku=${luku//[^-.0-9]/} -> ei toimi jos kirjoittaa: luku=${luku//[^.-0-9]/}. Että yksinkertainen kieli?
- muuten esimerkiksi: onkoekasuurempi 6-2 4 -> 0 . Kokonaislukujen miinuslasku siis toimii mutta taitaa olla viisainta jättää muu matikka tässä yhteydessä pois.

59
Kun pakolliset rinkulat on nyt tehty niin pistetäänpä vauhtia masiinaan (siis toiminta oli: etsitään tekstistä jokainen haettava ja tulostetaan yksinomaan niiden sanojen loppu jossa haettava on
- siis tulostetaan välilyöntiin tai rivinloppun asti):
Koodia: [Valitse]
function takaosa () { for word in ${@:1}; do [[ "$word" =~ $1 ]] && echo ${word#*$1}; done ;}
# kutsut ovat kirjoitusasultaan: sana:takaosa haettava_teksti teksti_josta_haetaan
# -   rivinsiirrot sallitaan. Silloin teksti täytyy laittaa heittomerkkien väliin:
time takaosa ma "ma1 kkk kkkma2 ma3 ma4jjjjj gggggma5
ma6 ma7 hhhhma9ppppp ma10 xyz ma11 ggggma12"
- tai mikäli kyse on tiedostoon kijoitetusta tekstistä:
Koodia: [Valitse]
time takaosa B $( cat /boot/grub/grub.cfg)
- 'nopeisiin käskyihin' perustuvat skriptit ovat pienissä etsintätehtävissä nopeampia kuin sed-awk-grep-regex toteutukset  mutta mutta vähänkin isommissa etsintätehtävissä ne ovat hitaampia ja isoilla tiedostoilla jopa yli kymmenenkertaa hitaampia - mutta 'nopeista käskyistä' tehty skripti tarjoaa sellaista mikä muille olisi mahdotonta tai ainakin hyvin vaikeaa - ja 'nopeista käskyistä' tehtyä skriptiä voi tästäkin räätälöidä edelleen mihin tarkoitukseen tahansa. 

- myös grep+regex on varsin pätevä tallaisiin toimiin - mutta muistihirviö-skriptaajienkin täytyy opiskella sitä tosikauan ennkuin alkaa sujumaan. Eikä noiden muidenkaan skriptikielten soveltaminen yksinkertaista ole.

60
Tällä kertaa tarkoituksena on muuttaa edellä esitetty rekursiivinen versio tavalliseksi - siis tavallaan tehdä uudestaan - ja usein kun skriptn tekee täysin uudelleen niin se samalla yleensä yksinkertaistuu ja nopeutuu - ellei mähli:
Koodia: [Valitse]
function takaosa () { apu="$(echo ${1//\\n/ })"; while [[ "$apu" =~ $2 ]]; do apu="${apu#*$2}"; echo ${apu%% *}; done ;}
takaosa "$( cat /boot/grub/grub.cfg)" B
tulostuu:
EGIN
e
EGIN
EGIN
EGIN
EGIN
EGIN
EGIN
EGIN
EGIN
EGIN
EGIN

- löytyneistä ensimmäinen on tekstin alustapäin ja viimeinen lopustapäin.
- EGINIT tulevat sanoista BEGIN. Yksinäinen e tulee siitä että tiedotossa yksi lause alkaa: Be
- haku voidaan kirjoittaa myös: 
Koodia: [Valitse]
takaosa "$(readarray -t a < /boot/grub/grub.cfg; echo "${a[@]}")" B   
- hakuaika putoaa kolmasosaan kun hakutermiksi kirjoitetaan pieni b - vaikka löytöjä tulee neljäkertaa enemmän - olen äimänkäkenä siitä mikä tuommoisen voisi aiheuttaa. A/a on vähän toistapäin.

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