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 ... 35
1
Laitealue / Vs: Power ja Economy -ytimien hyödyllisyys
« : 05.11.24 - klo:10.14 »
Rankka käyttäminenhän sen vasta varmasti kertoo, mutta teoria on tällainen:

Tulkkaava kieli tosiaan taitaa vain hidastua jos määrättäisiin mitä ydintä milloinkin käytetään. Mutta kääntävissä kielessä kuten C ainoastaan kääntäminen hidastuu mutta käyttö nopeutuu - ja tehon säästö on melkoinen.

2
Koetin siirtyä uusiin käskyihin tässä desimaali-matriisin-summaimessa niin paljon kuin mahdollista. Skriptistä tuli vaikea tehdä mutta sehän saattoi olla myös tottumattomuutta kun siirtyi toisiin menetelmiin. On mahdotonta osoittaa tarkempaa syytä - mutta sanoisiko näin: kun kokeili toimisiko joku käsky niin vanhoilla käskyillä joko toimi tai ei - mutta uusilla käskyillä tuli takaisin se BASH skriptien kirous: joskus toimii ja joskus taas ei. Toimintalogiikkakin muuttui kummalliseksi - ja taitaa olla niin että siirryttäessä Ubuntusta toisiin Linuxeihin lakkaa skriptin toiminta - nimenomaan lause:
Koodia: [Valitse]
desimaalimatriisi=$(printf "%-${maxdesimaalipituus}s\n" ${desimaalimatriisi[@]} | tr ' ' 0)
vaatii tulkilta ihan mahdottomia. Nopeus on kyllä hyvä:
Koodia: [Valitse]
unset data; for ((n=1;n<=200;n++)); do data[$n]=$SRANDOM$RANDOM.$RANDOM$SRANDOM; done; maxdesimaalipituus=$( printf "%s\n" "${data[@]}" | cut -d. -f2 | wc -L); desimaalimatriisi=$( printf "%s\n" "${data[@]}" | cut -d. -f2); desimaalimatriisi=$(printf "%-${maxdesimaalipituus}s\n" ${desimaalimatriisi[@]} | tr ' ' 0); kaikkiendesimaaliensumma=$(($(echo ${desimaalimatriisi[@]} | tr ' ' '+'))); kokonaismatrix=$( printf "%s\n" "${data[@]}" | cut -d. -f1); kokonaiset=$(($(echo ${kokonaismatrix[@]} | tr ' ' '+')+${kaikkiendesimaaliensumma:0: -$maxdesimaalipituus})); echo $kokonaiset'.'${kaikkiendesimaaliensumma: -$maxdesimaalipituus}; echo "bc:n antama varmasti oikea tulos johon voi verrata:";bc -l<<<"$(echo ${data[@]} | tr ' ' '+')"

3
Sitten desimaaliluvuista muodostetun matriisin jåsenien summaus tyyliin: 'ynnää kauppalaskun summat'- mutta sen voi koodata toimimaan ihan niinkuin haluaa. Tämäntyyppinen toiminta edellyttää parametrien siirtämistä nimiparametreina. BASH ei osaa palauttaa parametreja sillä se olisi turhaa - sillä ne palautuvat toiminnan aikana auomaattisesti mikäli tarpeen on.

- nuo lauseet funktion add_decimal_matrix alussa: apu=$(declare -p $1); declare ${apu:8:2} data=${apu#*=} kloonaavat funktioon tulleen $i nimisen muuttujan annetulle nimelle
- teoriassa kloonaamiseeen on käsky: declare -p nimi=$1 mutta se tuntuu olevan liian buginen tähän.
Koodia: [Valitse]
function add_decimal_matrix () { apu=$(declare -p $1); declare ${apu:8:2} data=${apu#*=}; savedata=(${data[@]}); apu=${#data[@]}; for (( n=0; n<=$apu; n++ )); do data[$n]=${data[$n]#*.}; data[$n]=${data[$n]}'0000000000000000000'; data[$n]=${data[$n]:0:15}; done; apu=$(($(echo ${data[@]} | tr ' ' '+'))); desimaalit=$(echo "${apu: -15}"); ylivuoto=${apu:0: -15}; kokonaiset=$(($(echo ${savedata[@]%.*} | tr ' ' '+')+$ylivuoto)); echo 'summa: '$kokonaiset'.'$desimaalit ;}

# esimerkki kutsu:
unset data; for ((n=1;n<=200;n++)); do data[$n]=$RANDOM$SRANDOM.$RANDOM$SRANDOM; done; clear; echo 'seuraava summattava matriisi vaihtuu jokaisella ajokerralla ja tällä kerralla se on:   '${data[@]}; echo; time add_decimal_matrix data; echo "bc:n antama varmasti oikea tulos johon voi verrata:"; echo -n '       ';bc -l<<<"$(echo ${data[@]} | tr ' ' '+')"

- tuo käsky: unset johtuu siitä että muuten määriteltäessä uudestaan edelliset määrittelyt häiriköisivät.
- matriisin koolla ei ole väliä muuten kuin että se hidastaa - ainakin 25000 onnistuu ja aikaa kuluu silloin sekunti.

4
BASH ei ole sellainen surkimus miksi se kuvataan - hidas se on kyllä mutta sen hitautta liioitellaan aina. Mutta toisaalta BASH:issa voi tehdä mitävaan - vaikka toisin väitetään - tosin useimmat edistynemmät toiminnot ovat vieläkin hitaampia ja kömpelömpiä mutta vastikkeeksi ne voi toteuttaa lukemattomilla täysin erilaisilla tavoilla joista jokaisella on omat ominaisuutensa - hyviä ja huonoja - joten ainakin oppii kritisoimaan omia tekemisiään.

En yritä edes ymmärtää miksi ja miten meille kaikille on pätevästi osoitettu että BASH:in matriisioperaatiot ovat kelvottomia sillä todellisuudessa ne ovat sentään käyttökelpoiset. Minäkin luulin aikoinaan BASH:in matriisioperaatioita käyttökelvottomiksi mutta tehdessäni funktioita matrisioperaatioita varten huomasin BASH:in monipuolisuuden matriiseissakin - vähättelijöistä ihmettelen kuinka kaikki voivat silmät sinisinä sanoa ettei BASH parempia menetelmiä edes tunne kun väittäjistä joku on itse ne toteuttanut?

Näin ensialkuun tein skriptin matriisin muodostamiseen : matriisin nimi on koematriisi, sen riviluku on 200 ja sarakeluku 26 - tulostuksesta sen rakenne selviää parhaiten:
Koodia: [Valitse]
function koematriisin_kasaus () { unset koematriisi; for ((n=1;n<=200;n++)); do koematriisi[$n]=$(echo {a..z} | sed "s/[a-z]/&$n/g" ); done; echo tämmöinen matriisista tuli:; echo; printf "%s\n" "${koematriisi[@]}" | column -t ;}; koematriisin_kasaus
- kun matriisi on muodostettu pysyy se määriteltynä niin kauan kuin päätettä ei sammuteta.

Sitten arvon hakeminen siitä - ja koska BASH on matriisioperaatioissa todella hidas niin vastikkeeksi laitoin tarkistuksen siitä ettei matriisin rajoja ylitetä (sentakia käytetään nimiparametria):
Koodia: [Valitse]
function arvo_matriisissa () { apu=$(declare -p $1); declare ${apu:8:2} apu2=${apu#*=} # apu2 saa funktioon nimenä tulleen muuttujan tyypin ja arvot
[[ $apu =~ \[$2\] ]] || { echo matriisissa ei ole sellaista riviä; return ;}
matriisin_rivi=($(echo ${apu2[$2]})); arvo_rivilla=${matriisin_rivi[$3-1]}
[[ $arvo_rivilla ]] && echo $arvo_rivilla|| { echo matriisissa ei ole sellaista saraketta; return ;} ;}
# esimerkkikutsu:
arvo_matriisissa koematriisi 200 26 # arvo joka on koematriisin rivillä 200 sarakkeessa 26

---

Kyllä tämä BASH on nolostuttava - koematriisin tekemiseen on aivan yksinkertainenkin tapa joka ei vaadi temppuilua - ja tehdään samalla iso koematriisi, se kylläkin kestää puolisen minuuttia:
Koodia: [Valitse]
unset koematriisi; for n in {1..20000};do koematriisi[$n]=$(echo {a..z}$n) ; done;printf "%s\n" "${koematriisi[@]}"

Samoin funktion arvo_matriisissa voi kirjoittaa yksinkertaisemminkin:
Koodia: [Valitse]
function arvo_matriisissa () { apu=$(declare -p $1); declare ${apu:8:2} apu2=${apu#*=}; printf "%s\n" "${apu2[@]}" | cut -d ' ' --fields=$3 | tr '\n' ' ' | cut -d ' ' --fields=$2 ;}
- ei siitä nopeus-hyötyä tosin ole -  se on vain sama asia kerrottuna toisella tavalla.

5
Kolmiulotteisen matriisiin tulostaminen: ensiksi muodostetaan ajatuksissa kaksiulotteinen matriisi - kaksiulottteisella puolella voi käyttää 'valenimiä' sillä eiväthän ne lopputuloksessa näy - mutta valinta yksinkertaistuu. Siis tehdään senmuotoinen kaksiulotteinen matriisi kun kuuluu - esimerkiksi:
a b c
d e f
g h i

# halutaan tulostaa tämän kolmiulotteisen mariisin jäsen 3 3 3 (=rivi 3,sarake 3, alkio 3). muodostetaan ensin valenimistä oikean muotoinen matriisi:
Koodia: [Valitse]
unset matriisi; readarray matriisi<<<'a b c
d e f
g h i';  echo tämmöinen kaksiulotteisesta matriisista tuli:; echo; printf "%s\n" "${matriisi[@]}" | column -t; echo

# Sitten muodostetaan kaksiulotteisen alkioita vastaavat lukujoukot. Lukujoukoissa on yleensä kaikissa sama määrä jäseniä mutta se ei ole pakko.
 
a="1 2 4"
b="2 4 6"
c="3 6 9"
d="4 8 12"
e="5 10 15"
f="6 12 18"
g="7 14 21"
h="8 16 24"
i="9 18 27"

# haetaan oikea alkio kaksiulotteisela puolelta sen alkio 'rivi sarake':
function hae_arvo_matriisista () { # kutsu on muotoa: matriisin_nimi rivinumero sarakenumero
 apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu=($(echo ${apu2[$(($2-1))]})); echo ${apu[@]:$(($3-1))} ;}

# mudostetaan tuossa 'oikeassa alkiossa ' olevista alkioista matriisi josta tulostetaan valittu jäsen
apu=($(hae_arvo_matriisista matriisi 3 3)); apu=($(eval echo \$"$apu")); echo ${apu[2]} # jäsen matriisi 3 3 3 = 27

---

Toiminta on samantapaista käytännön isoissa matriiseissa: esimerkiksi vuoden jokaisena päivänä mitataan lämpötilaa kerran minuutisssa ja siitä tehdään matriisi. Matriisin kaksiulotteinen puoli jakaa sen päiviksi ja niiden tunneiksi. Sitävarten muodostetaan kaksiulotteinen matriisi näin:
Koodia: [Valitse]
readarray matriisi<<<"$(for n in {1..365}; do for m in {1..24}; do echo -n $n't'$m' '; done; echo; done)";  echo tämmöinen kaksiulotteisesta matriisista tuli:; echo; printf "%s\n" "${matriisi[@]}" | column -t; echo
Sen alkiot täytetään kerran minuutissa tapahtuvilla mitauksilla - mittausarvojen väliin tulee välilyönti - ja joka tunti siirrytään täytämään sille tunnille osoitettua kaksiulotteisen matriisin jäsentä näillä lämpötilamittauksilla.

6
Matriisin määrittelyn voi tehdä toisinkin: voi kirjoittaa matriisin välittömästi skriptiin ihan semmoisena kuin se on -  kuten esimerkissä on tehty. Matriisi-määrittelyn voi silloin myös kopioida jostakin.
Matriisimäärittely näyttää vähän omituiselta mutta se on kopioinnin kannalta ihanteellinen:
Koodia: [Valitse]
unset matriisi; readarray matriisi<<<"! ' ¤ % & / ( )
@ £ $ ‚ { [ ] }
ä ö å Ä Ö Å ½ §
1 22 333 4444 55555 666666 7777777 -88.88e-88";  echo tämmöinen matriisista tuli:; echo; printf "%s\n" "${matriisi[@]}" | column -t; echo
- matriisi voi olla minkäkokoinen ja muotoinen tahansa ja useimmat merkit kelpaavat - mutta esimerkiksi: jos ulkona on ' niin sisälle kelpaa vain " ja jos ulkona on " niin sisälle kelpaa vain ' . Sama sääntö on ainakin sed:issä.
- toki matriisi-kuvauksen voisi kirjoittaa yhteen pötkyynkin mutta silloin kopioinnista tulisi vaikeaa ja työlästä kopioida se jostakin:
readarray matriisi<<<"1 2 3 4 5 6 7 8"$'\n'"a b c d e f g h"$'\n'"1 22 333 4444 55555 666666 7777777 88.88e-88"
- muuten tuo alussa oleva unset nollaa matriisin niinkuin sitä ei olisi ollutkaan ja aloitetaan puhtaalta pöydältä. Unset olisi hyvä lisätä aina mudostamisen alkuun sillä se ei vie aikaa ja esimerkiksi kun matriisit ovat todella suuria niin se on jopa tarpeen.
- kirjastokielto ja huonot ohjeet - man-sivut, BASH-raamattu ja virtuoosit - aiheuttavat sen että BASH:issa on pakkokin ottaa kopiointiin uusi asenne.

7
Aikoinaan ei annettu pienintäkään toivoa sille että kaksiulotteiset matriisit toimisivat joskus - että voisi tehdä funktion hakemaan vapaa-muotoisesta matriisista yksi jäsen kun nimetään matriisi ja sen rivi ja sarake. Sellaista kaivattiin jo vuosikymmeniä sitten ja monet yrittivät sellaista tehdäkin mutta asia ei edistynyt vähääkään. Kuitenkin BASH on aina osannut kaksiulotteisten matriisien käsittelemisen mutta skriptiä sitävarten ei vain ole tehty koska ei osattu valita oikeita käskyjä - löytyi ne kyllä äskettäin mutta koska kymmenen vuotta sitten BASH:iin tuli paremmat toiminnot niin tämä on niillä tehty sillä nopeutta tuli roimasti lisää.

En alkuunkaan usko, ettei kukaan ole aikoinaan tehnyt tätä toimintoa sillä se on niin yksinkertainen että eiköhän se ole kuulunut jo alkuperäisiin suunnitelmiin siitä miten matriisien tulee toimia BASH:issakin.

Mutta koska matriisin nimi passataan nimenä eikä arvoina on tässä paljon normaalista poikkeavaa. Käsiteltävät matriisit voivat olla minkä muotoisia tahansa ja muodostettu vaikka minkälaisista olioista: kokonaisluvuista, desimaalivuista, tekstistä ja vaikka nuolenpää-kirjoituksesta ...  Nopeuskin on BASH:iksi ihan siedettävä.

Nykyvaatimusten mukaan tämä on sittenkin kelvottoman hidas mutta tämä onkin vain osoitus että kyllä BASH:issa toimivat myös kaksi-ulotteiset matriisit - ja samalla periaatteella kolmi-ulotteisetkin.

On täysin mahdotonta saada minkäänlaista käsitystä BASH:in kyvyistä koska niin paljon mahdottomaksi väitettyä on osottautunut toimivaksi - ja kun aikaisemmin isot työt kestivät sekunteja niin nyt niistä monet kestävät millisekunteja. Vaikka pitää edelleen paikkansa että BASH on ohjelmointikieleksi rääpäle niin 'tunnelin päässä on valoa'.

---

esimerkki - ensin tehdään matriisi - voisi sen lukeakin jostain - ja sitten haetaan siitä arvo rivitä:2  sarakkeesta 8:
Koodia: [Valitse]
matriisi=([0]="1 2 3 4 5 6 7 8" [1]="a b c d e f g h" [2]="1 22 333 4444 55555 666666 7777777 8.8888e-88"); echo tämmöinen matriisista tuli:; echo; printf "%s\n" "${matriisi[@]}" | column -t; echo
function hae_arvo_matriisista () { # kutsu on muotoa: matriisin_nimi rivinumero sarakenumero
 apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu=($(echo ${apu2[$(($2-1))]})); echo ${apu[@]:$(($3-1))} ;}

time hae_arvo_matriisista matriisi 3 8
- ajan kulumisen kannalta on melkein yksilysti mitä numeroiden paikalla on: pitkää tai pätkää, numeroita, kirjaimia, mitähyvänsä merkkejä.
- samoin matriisin koko vaikuttaa suoritusaikaan vain vähäsen

8
Tekstijonon ja matriisin käsittelyyn tarvitaan omat funktiot. Kuitenkin tekstijono on matriisin jäsen nolla - selitys siihen on aivan yksinkertainen: kun matriisin indeksiä ei määrätä niin oletetaan että se on nolla. Ja vain koska tätä ei kerrota missään niin koko käsite on umpisolmussa. Sillä kaikkia muitakin matriisin jäseniä käsitellään toisin kuin koko matriisia - ei tekstijono ole minkäänlainen poikkeus. Esitetäänpä asia toisellatavalla:

BASH antaa skriptaajan tutkia kirjanpitoaan ja sieltä selviää muunmuassa kuinka minkin niminen muuttuja on määritelty. Anna kaksi käskyä ja tutkiskele tulostusta:
Koodia: [Valitse]
muuttuja="1 2 3"; declare -p muuttuja #-> tulostaa: declare -- muuttuja="1 2 3"
muuttuja=(1 2 3); declare -p muuttuja #-> tulostaa: declare -a muuttuja=([0]="1" [1]="2" [2]="3")


nyt tee kummajaiskäsky ihan vaan tutkimismielessä - tämänkaltaisissa tutkimuksissa kannattaa jokakerta avata uusi pääte sillä BASH:in roskankeruu on olematonta ja vanhat tulokset voivat muuten kummitella - mutta uudessa päätteessä voi olla varma siitä että kaikki roskat ovat hävinneet:
Koodia: [Valitse]
muuttuja=(1 2 3); muuttuja="1 2 3"; declare -p muuttuja -> tulostaa: declare -a muuttuja=([0]="1 2 3" [1]="2" [2]="3")
tulkki ei osaa suhtautua mielipuolisuuteen mutta tulostus kertoo sentään tekstijonon olevan matriisin jäsen nolla. Minkä muuten voit todeta toisinkin - avaa uusi pääte ja käske:
Koodia: [Valitse]
muuttuja="1 2 3"; echo ${muuttuja[0]} -> tulostuu: 1 2 3 #-> siis teit tekstijonon, tulostit matriisin ja silti onnistui ihan hyvin.

Kun mielestään tekee matriisin niin helposti siitä onkin tullut tekstijono eikä sitä välttämättä huomaa sillä virhe se ei ole vaan ainoastaan teko jota ei tarkoitettu - sillä matriisihan siitä tuli mutta kaikki arvot ovat jäsenessä nolla. Siis jos käsky: echo ${muuttuja[0]} tulostaa kaikki arvot niin kyseessä on tekstijono. Mutta tarkistamiseen kannattaa tehdä skripti sillä tietoja tarvitaan vähän lisääkin. Xref skriptin käytäminen on helppoa, kutsut vain: xref muuttujan_nimi. Xref ilmoittaa myös sotkuista tai jos muuttujan arvoa ei ole märätty - esimerkiksi käsky: muuttuja=(1 2 3); muuttuja="1 2 3"; xref muuttuja tulostaa tämmöistä:

muuttuja on matriisi.
arvot    : 1 2 3 2 3
osoitteet: 0 1 2
- ensimmäisen rivin kaksi viimeistä merkkiä ovat melkoisia haamuja; joillain tavoilla tulostettaessa ne näkyy ja toisilla ei.
- oikeastaan muuttuja-määrittelyn aluksi pitäisi antaa käsky: unset muutuja_nimi - silloin ei varmasti synny sotkuja - vaan enpä ole moiseen koskaan törmännyt vaikka ei käsky toimintaa hidastaisi sillä se on vain ohje kuinka pitää muutuja muodostaa.

Xref skriptin koodi:
Koodia: [Valitse]
function TulostaMuuttuja () {
[[ $(eval echo \$$1) ]] && echo -n $1' on numero- tai tekstimuuttuja arvoltaan: ' && eval echo \$$1 || echo $1" on määrittelemätön"
}

function TulostaMatriisi () {
echo $1' on matriisi.'
[[ $(eval echo \${$1[@]}) ]] && {
echo -n 'arvot    : '; eval echo \${$1[@]}               # arvojen väliin tulostuu välilyönti
echo -n 'osoitteet: '; eval echo \${!$1[@]}  | column -t # arvo ja sitä vastaava osoite kirjoitetaan aina alekkain riippumatta niiden pituuksista - joskus kylläkin vain teoriassa.
}; echo ;}

function xref () {
[[ $(eval echo \${!$1[*]} | cut -sd ' ' -f 2) ]] && TulostaMatriisi $1 && return # Matriisi ei voi olla määrittelemätön vaan silloin se on tavallinen muuttuja.
[[ $1 ]] && TulostaMuuttuja $1
echo # funktiossa täytyy aina tehdä jotakin - vaikka tulostaa tyhjää. Jos siinä on pelkkiä ehtoja eikä yksikään niistä toteudu niin muuten tulisi suoritusaikainen virhe
}

Käytännössä tämä vaikuttaa esimerkiksi näin kun muuttuja on joko testijono "1 2 3" tai matriisi (1 2 3):
Koodia: [Valitse]
echo ${muuttuja[@]} tulostaa sekä tekstijonon että matriisin:1 2 3
echo ${muuttuja[0]} tulostaa 1 jos kyseessä on matriisi mutta tekstijonon se tulostaa: 1 2 3
echo ${muuttuja[1]} tulostaa 2 jos kyseessä on matriisi mutta tekstijono ei tulosta mitään.
echo ${muuttuja[2]} tulostaa 3 jos kyseessä on matriisi mutta tekstijono ei tulosta mitään.

--------
 
Kokosin muutamia sellaisia käskyjä jotka toimivat matriiseilla joiden jäsenet voivat olla pitkiäkin tekstijonoja joissa voi olla välilyöntejä tai ei - ja käytännössähän useimmat matriisit ovat tällaisia.
Koodia: [Valitse]
echo ${muuttuja[1]:3:4} tulostaa matriisin toiselta riviltä alkaen merkistä 3 neljä merkkiä eteenpäin.
echo ${muuttuja[@]:3:4} tulostaa merkit 3-7 kaikilta matriisin riveiltä.
echo ${muuttuja[1]#* * * } puolestaan tulostaa toiselta riviltä kolmannen välilyönnin jälkeiset merkit rivin loppuun asti.
- välilyönnin paikalla voi olla myös sana, lueteltu merkkiryhmä (esimerkiksi[1 5] joka on 1 tai 5), regex (esimerkiksi [1-5] on kaikki numerot 1-5) ...
echo ${muuttuja[@]#* * * } puolestaan tulostaa saman viipaleen kaikilta matriisin riveiltä.
- ja jos haluaa tulostaa vain ensimmäisen sanan siitä erotetusta vaatii se oman käskynsä: echo ${muuttuja[@]%% *} - ja sekin toimii yli koko matriisin.:
- loopeila saa tehtyä nopeahkon skriptin joka tulostaa matriisin jokaisen rivin halutun kentän

echo ${muuttuja[1]%palloveikot*} tulostaa mitä lukee toisella rivillä sanan: palloveikot edessä
echo ${muuttuja[@]%palloveikot*} tulostaa matriisin jokaiselta riviltä mitä on kaikilla riveillä sanan palloveikot edessä.

echo ${muuttuja[@]//köksä/gastronominen_taitelija} muuttaa koko matriisissa kaikki sanat köksä sanaan gastronominen_taitelija
echo ${muuttuja[@]//ä/$(tput blink)ä$(tput sgr0)}  ennen englanniksi kirjoitetun väitöskirjan luovuttamista voi tarkistaa onko sinne jäänyt ä-kirjaimia -> koko teksti tulostuu siten että ä-kirjaimet vilkkuvat jolloin näkee hyvin jos niitä jossain on. Google&kumppanit on miljoonakertaa parempi tarkistamiseen - paitsi silloin harvoin kun se ei ole.

- kaikki nämä käskyt toimivat erittäin nopeasti - usein melkein yhtä nopeasti kuin sed. Miksei niistä puhuta että edes tiedettäisiin että tuomoisiakin käskyjä on? Koska  ne ovat tulosta monien virtuoosien vuosikausien ponnisteluista niin kuinka on mahdollista että niiden on annettu vaipua unholaan - kukaan ei ole edes jupissut vastaan? Ovatko ne liian tehokkaita - sillä valmisohjelmien reviiriähän nopeus ja tehokkuus ovat. Näissä rinkuloissa BASH:in omia valmisohjelmia ovat esimerkiksi sed ja awk - toki myös regex:ät grepin avulla - nykyäänhän ne kaikki ovat lähes kuolleet mutta aikoinaan niiden tekeminen on taannut leivän sadoille ihmisille.

9
Paniikki iski kun edellisten skriptien saaminen toimiaan useammissa koneissa oli kovin hidasta ja aloin ihmetellä minkä takia - onko jotain ihan perusteellista? Toki sitä perusteellista löytyi - mutta oikeastaan jo kauan sitten ilmennyttä: toimiminen olion kanssa jota luulee esimerkiksi matriisiksi mutta se onkin tekstijono.

Asiaa sotkee se että BASH:issa kaikki muuttujat ovat matriiseja ja niin on myös tekstijono - tekstijono on saman-nimisen matriisin jäsen nolla - ei käsittelyssä ole mitään ristiriitaa sillä matriisin missähyvänsä osoitteessa olevaa tekstijonoa käsitellään eritavalla kuin matriisia - koko homma saa alkunsa siitä että jos matriisin jäsentä ei määrätä niin oletetaan että kyseessä on jäsen nolla. Esitetään asia esimerkillä: tehtävänä on tulostaa muuttuja mutta siitä ei tiedetä onko se tekstijono vaiko matriisi - siis jos se muodostuu numeroista niin onko sen kuvaaja:
1
2
3
vaiko: 1 2 3. Tosin käytännössä molemmat tulostetaan yleensä: 1 2 3 jokatapauksessa koska käytännön isojen matriisien kunnollinen pystysuora esitys veisi niin paljon tilaa että se sotkisi tulosteen ihan käsittämättömäksi - joten tulosteestakaan ei voi päätellä onko tulostettu tekstijono vaiko matriisi. Ja koska tekstijonojen ja matriisien käsitely on erilaista täytyy kummallekin tehdä skriptistä oma versio ja päätellä aina mitä versiota kutsutaan. Mutta on asiaan toinenkin ratkaisu: muutetaan siellä matriisin jäsenessä nolla oleva tekstijono matriisiksi ja myöhemmin kutsutaan aina vain matriisi-versiota. Mutta tälle muutokselle tehtävän funktion tulee käyttää nimiparametria. Senjälkeen jokaisen tekstijonon tai matriisin paikalle kirjoitetaan: $(matrixsize muuttujan_nimi) ja sitten käsittellään aivankuin se olisi aina ollut matriisi.

- ensimmäinen vaikeus on tehdä matrixsize-funktio - mutta se on tehty jo ja toimivaksi havaittu. Mutta paljon pahempi vaikeus on saada virtuoosit uskomaan että se toimii sillä he eivät epä-uskossaan edes kokeile. Mutta tässä se on:
Koodia: [Valitse]
function matrixsize () { matriisi=($( echo ${apu#*=} | tr \" '\n' | sed '/^[[:space:]]/d' | sed 1d | sed '$ d')); read<<<${matriisi} $1 ;}

koe="kissa kuumalla katolla"   # tämä on koeteksti1 ja: koe=(kissa kuumalla katolla) # on koeteksti2. Siis matrixsize:lle voidaan antaa matriisikin.
matrixsize koe; echo ${koe[1]} # kutsussa tuo numero on joko matriisin jäsen numero tai tekstijonon sananumero

-------------------------------------

Mutta on asiaan helpomminkin hyväksyttävä osa-ratkaisu. Esimerkiksi kun joudut tulostamaan jotakin josta et tiedä onko se tekstijono tai matriisi :
Koodia: [Valitse]

function tulosta () {
apu=$(declare -p $1) 
case ${apu:9:1} in
a ) matriisi=($( echo ${apu#*=} | tr \" '\n' | sed '/^[[:space:]]/d' | sed 1d | sed '$ d')); echo ${matriisi[$2]} ;;
* ) tekstijonomatriisi=($(eval echo \$${1#*=})); echo ${tekstijonomatriisi[$2]}  ;;
esac ;}

koe="kissa kuumalla katolla" # tämä on koeteksti1 ja: koe=(kissa kuumalla katolla) # tämä on koeteksti2
tulosta koe 0 # kutsussa tuo numero on joko matriisin jäsen-numero tai tekstijonon sananumero (= monesko välilyöntien erottama sana se on)


- skripti on tavallaan vain ohje tulkille kuinka toimia - 'ratapihakaavio: kuinka asettaa vaihteet oikein' - joten toiminta hidastuu vain vähän.
- jos tämä skripti olisi kirjastossa niin tulostaminen olisi unelmaa.
- mikäli määräät:
Koodia: [Valitse]
matriisi=({1..10}); matriisi="1 2 3 4 5 6 7 8 9 10"; echo ${matriisi[@]} niin tulostuu: 1 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10
- katsopa millainen sotku BASH:in kirjanpidossa vallitsee käskemällä: declare -p matriisi  niin tulostuu:
Koodia: [Valitse]
declare -a matriisi=([0]="1 2 3 4 5 6 7 8 9 10" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10") -> status on edelleen matriisi ja mariisin jäsen nolla on tekstijono ja loput jäsenet vanhan matriisin arvoja.
- echo $matriisi kylläkin tulostaa ihan oikein ja muutenkin tuntuu sitä että kyseessä on vain kosmeettinen haitta.

10
Nyt täytyy laittaa foorumille talteen keskeneräinen työ sillä olen rähmäkäpälä ja tuhoan kotona oleat toimivat skriptit

Tarkoitus on tehdä jakauma edellsestä ja omassa koneessani se toimiikin hyvin.
Koodia: [Valitse]
rm -f ~/nano/data2; rm -f ~/nano/data3; touch ~/nano/data2; touch ~/nano/data3; readarray -t apu < ~/nano/data; for (( n=0; n<${#apu[@]}; n++ )); do echo $((${apu[$n]}/2000)) >> ~/nano/data2; done; sort -n ~/nano/data2 | uniq -c >~/nano/data3
# mutta tiedostossa  ~/nano/data3 ovat y ja x väärässä järjestyksessä joten niiden järjestys täytyy vaihtaa:
while read -r line; do set $line; echo $2 $1; done < ~/nano/data3 > ~/nano/data4 # siis saman lauseen sekä sisäänmeno että ulostulo voidaan uudelleensuunnata yhtaikaa
# tiedostossa ~/nano/data4 on paljon turhia rivejä joten poistetaan ne jotta käyrästä saisi paremmin selvää
cp <(sed -n '160,460 p' ~/nano/data4) ~/nano/data5
gnuplot -p -e 'set terminal postscript eps color enhanced;  set output "~/nano/pohjienjakauma.eps";set xlabel "aika"; set ylabel "aikojen jakauma"; plot "~/nano/data5"'

11
Olen jo pitkään epäillyt että BASH:in suoritusaikojen vaihteluissa on jonkinsortin säännöllisyyttä - siis että tulos hidastuu monta kertaa peräkkäin, siten taas nopeutuu - hidastuu - nopeutuu - ... nikotellenkin silointällöin - keskiarvon pysyessä melkolailla vakaana. Vaihtelu on havaittavissa time-käskylläkin mutta yleensä sen erotelu-kyky ei ihan riitä. Ei asialla enää nykyään merkitystä ole mutta se on mielenkiintoista että miksi time-käskyn erottelukyky loppuu juuri siihen mistä ongelmat alkavat.

Tutkitaan kuitenkin se vähä mikä pystytään - skripti kestää muuten varttitunnin - ensimmäiseksi tutkitaan kuinka pitkään BASH:illa kestää tehdä koodi sille annetusta skriptistä - muuten muut kielet polkevat paikallaan paljon kauemmin kuin BASH. Ja tämä tulkkaamiseen kuluva aika vaihtelee myös. 
- aluksi laitetaan laitetaan suoritukseen todella yksikertainen skripti ja mitataan aika siihen kun työ on valmis. Yksinkertaisin skripti on kaksoispiste elikä käsky olla tekemättä mitään -  mutta ei sitäkään prosessoriin kaksoispisteenä syötetä vaan tulkin täytyy tulkata kaksoispistekin ryhmäksi bittejä joten kyllä silloinkin lähdetään toimiin tulkin kautta - käskyn suorittamien ei sitten kestä juuri mitään. Funktio nano mittaa sitten kauanko tulkkaus ja sen jälkeinen käskyn suorittaminen kestävät. Grafiikka-ohjelma gnuplot esittää sitten tulokset graafisena.
- laitettuasi skriptin toimimaan voit rauhassa vaikka surffata.
- erottelukyky ajoitus-ohjelmalla nano on hyvä - mutta joku kyllä joskus höpertää - onko se nano itse selvinnee aikanaan.
- jokainen mittaustulos esitetään graafisessa kuvaajassa merkillä: + .
- tulos riippuu kaikesta mahdollisesta: kuinka hyvä tietokone on, mikä on lämpötila, sorsiiko onnetar ...
- tietokneen kaikilla sovelluksilla on pärstäkerroin jonka perusteella käyttöjärjestelmä arvostaa itseään yli kaiken, laittaa toisista jotkut pikakaistalle jossa odottelua on vähä ja jotkut joutuu odottamaan useammin ja pikkuisen kauemmin. Ja BASH kuuluu niihin pikkuisen kauemmin odottaviin. Vähäsen apua tuo käsky: sudo renice -20 -u käyttäjänimi. Myös sleep-käskyt autavat sillä ne sanovat käyttöjärjestelmälle: nyt olisi minun kannaltani sopiva aika odotella muiden työskennellessä - mutta aikaahan sleep-käskyt vievät.
- reaaliaika-käyttöjärjestelmillä ei tätä vikaa pitäisi olla - kokeiliskohan BASH:in reaaliaika-versiota?
- aika mitataan kirjoittamalla alku- ja loppuhetki kovalevy-tiedostoon. Kovalevylle kirjoittaminen ja sieltä lukeminen ovat kylläkin hitaita toimenpiteitä, mutta itseasiassa tässä ainoastaan annetaan kirjoitusmääräykset ja laitetaan kirjoitettava nopeaan bufferiin ja kaikki muu toimii ajoituksen jälkeen eikä se silloin enää tulokseen vaikuta.
- anna varmuuden vuoksi käsky: sudo apt install gnuplot   - ja tee mitä se määrää.
- jos kokeilet skriptien toimintaa niin kopioi koko koodi keralla päätteeseen sillä odotusajat ovat pitkiä.
- tuloksia voit katsoa tiedosto-selaimella. Jos et ole sortunut 'hajoita ja hallitse' tekniikkaan toisilla versioilla, toisilla työpöydillä, toisilla ... 'onhan ne paljon parempia'.
- näilä arvoilla mittaus kestää noin varttitunnin.
Koodia: [Valitse]
function nano () { date +%s%N > alkuhetki; $@ ; date +%s%N > loppuhetki; sleep 0.01; echo $(($(cat loppuhetki) - $(cat alkuhetki))) >> ~/nano/data ;}

kertoja=7500; rm -f ~/nano/data; mkdir -p ~/nano; touch ~/nano/data ; time for (( n=1; n<=$kertoja; n++ )); do nano : ; sleep 0.09; done

rm -f ~/nano/data2; rm -f ~/nano/data3; touch ~/nano/data2; touch ~/nano/data3; readarray -t apu < ~/nano/data; for (( n=0; n<${#apu[@]}; n++ )); do echo $((${apu[$n]}/2000)) >> ~/nano/data2; done; sort -n ~/nano/data2 | uniq -c >~/nano/data3
# mutta tiedostossa  ~/nano/data3 ovat y ja x väärässä järjestyksessä joten niiden järjestys täytyy vaihtaa:
while read -r line; do set $line; echo $2 $1; done < ~/nano/data3 > ~/nano/data4 # siis saman lauseen sekä sisäänmeno että ulostulo voidaan uudelleensuunnata yhtaikaa

# tiedostossa ~/nano/data4 on paljon turhia rivejä joten poistetaan ne jotta käyrästä saisi paremmin selvää
cp <(sed -n '160,460 p' ~/nano/data4) ~/nano/data5

gnuplot -p -e 'set terminal postscript eps color enhanced;  set output "~/nano/pohjienjakauma.eps";set xlabel "aika"; set ylabel "aikojen jakauma"; plot "~/nano/data5"'
Tuloksia voit selata työkalupalkin Files selaimella ( se on se näytön reunalla olevien kuvakkeiden joukko ja hiirellä osoittaen oikea kuvake kirjoittaa viereensä: Files).

12

Tein uuden version skriptistä joka laskee neliöjuuren kaikista sille annetuista reaaliluvuista mukaan lukien luvut tyyppiä:
1.234567e-96  elikä 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567
1.234567e96   elikä 1234567xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Varsinkin yksi ominaisuus skriptissä on erinomainen: sqrt 4 on 2, sqrt 9 on 3, sqrt 16 on 4 , sqrt 25 on 5 .... sqrt 2**58 on 536870912 -> ne ovat aina kokonaislukuja. Ihminen kyllä käsittää että kun ohjelma sanoo 9:n neliöjuureksi 2.99999999 niin todellinen arvo on 3 mutta onhan se luotettavampaa jos jo alunperin sanotaan sen olevan 3.
- tasaluku tulee myös kun juurrettava on 0.00000000000000000000000000000000000016 sillä tulos on silloin .000000000000000004 tai:4.0e-18
- sitämukaa kun skriptien toimintaa saa parannettua selviävät myös tulevat parannukset - nytkin seuraava parannus on vain toteutusta vailla.
- ja ennenkaikkea: numeromäärä on vain noin 9 numeroa mutta nopeus on yhtähyvä kuin parhaimmilla BASH:in matematiikkaohjelmilla.

- muuten kun olet tulkannut funktion (=ajanut sen kertaalleen) niin saat siitä intendoidun listauksen käskyllä: type -a funktion_nimi -> esimerkiksi: type -a to_sci. Siinä ovat jopa kommentit mukana oikeissa kohdissa jos niitä alunperinkään oli. Toisaalta kun olet ajanut funktion niin tulkki tekee koneen muistiin tällaisen 'yksirivisen' esityksen funktiosta - nämä minun esitykseni ovat juuri tällaisia 'yksirivisiä' sillä ne säästävät tilaa ja aikaamyötä niitä oppii lukemaan ja myös tekemään paljon nopeammin. Tässävaiheessa kommentit ovat vielä mukana, mutta kun tulkki tosiaan ajaa funktion niin kommentit stripataan kuutamolle ihan ensimmäiseksi.   
- funktioiden ja käskyjen etsimiseen kannattaa tehdä funktio:
Koodia: [Valitse]
hae_funktio () ( apu=$1; shopt -s extdebug; declare -F $1>/tmp/delme; type -a $1 >>/tmp/delme; shopt -u extdebug; apu=$(cat /tmp/delme); printf "%s\n" "${apu[@]//'is /'/' koneesta löytyy versio paikasta: '/}";apu=$(which $1);[[ $apu ]] && echo ja tätä käytetään:$apu ;)
[/code
-esimerkki kutsu: hae_funktio to_sci
funktioita ympäröivät normaalisti {...} sulut. Tämmöisissä funktioissa kannattaa käyttää ( ... ) sulkuja sillä ne erottavat toiminnan omaan  prosessiinsa joten jos sotkuja tulee niin ne jäävät sinne. . Varsinkin tämmöisissä tapauksissa kun samalla nimellä kuin käsky on myös funktio on käsky hyödyllinen - funktiota käytetään jos se on määritelty - siis funktion olemassaolo ei riitä. Ohjelmat ovat sensijaan aina olemassa ja niitä käytetään mikäli samalla nimellä ei ole tulkattua funktiota. 

Neliöjuuren laskeminen:'
[code]
function sqrt () { apu=${1##+(0)}; [[ $apu ]] || { echo 0; return ;} # lausetta tarvitsee vain kun lasketaan:  sqrt 0  . Tai: sqrt 00000....
function to_sci () { # funktion voi määritellä funktiossa. Kommentti alkaa merkillä # ja loppuu joko kaksoispisteeseen tai rivinsiirtoon.
[[ $1 =~ e ]] && { echo $1; return ;} || { apu=$1; apu=${apu//[-+]/}; apu=${apu##+(0)}; int=${apu%%.*};[[ ${apu//[^.]/} ]] && desimaaliosa=${apu##*.} || desimaaliosa=0; [[ $int ]] && { expo=${#int}; echo ${int:0:1}.${int:1}$desimaaliosa'e'$(($expo-1)) ;} || { apu=${desimaaliosa%%[1-9]*}; ekspo=$((${#apu}+1)); desimaaliosa=${desimaaliosa:$(($ekspo-1)):1}.${desimaaliosa:$(($ekspo))}; desimaaliosa=${desimaaliosa%.}; echo $desimaaliosa'e'-$ekspo ;} ;} ;}  # oli: $(($ekspo))}; echo $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<32;i++)); do # binääri-haun looppi alkaa
   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
apu=00000000000000000000000000000000000000000000000000$sqrt'000000000000000000000000000000000000000000000';apu=${apu:0:$((51+$expo))}.${apu:$((51+$expo))}
luku=${apu##+(0)}; [[ ${luku//[^.]/} ]] && luku=${luku%%+(0)}; luku=${luku%.}

echo $luku; to_sci $luku;}
   
juurrettava=16;time sqrt $juurrettava
echo "seuraavalla rivillä on bc:n varmasti oikea tulos vertaamista varten" # koska bc on vasta laskun jälkeen ei se voi vaikuttaa skriptin tulokseeen
bc -l <<< "sqrt($juurrettava)"

13
Parantelin 36-numeron liukuvan pilkun kertolaskua - sen kerrottaviksi voi antaa kaksi liukuvan pilkun 18-numeroista kerrottavaa. Poistin vanhan koodin ja laitoin tähän uuden.

Ja vaikka bc antaa oikean tuloksen johon verrata niin muuten bc ei osallistu laskentaan ollenkaan - sentakia kutsut ovatkin monimutkaisia. Mutta normaalisti koko bc:n voi jättää pois ja silloin kutsu on: kerro18 $luku1 $luku2

- esimerkiksi tulos laskusta: .00900007567899123 * 900.07000012345678  on: 8.1006981175007567491845709040394   - ja lasku kestää vain 1ms - bc:llä lasku  kestää 3ms.
Koodia: [Valitse]


function kerro18 () {
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia. Vaihtoehdot:tulosta=echo ja tulosta=:
[[ ${#1} -gt 18 || ${#2} -gt 18 ]] && echo laskettavissa liikaa numeroita && return
$tulosta "annetut numerot: "$1 $2
[[ ${1:0:1} = - || ${2:0:1} = - ]]  && merkki=- || merkki=''
[[ ${1:0:1} = - && ${2:0:1} = - ]]  && merkki=''
apu1=${1//\-/}; apu2=${2//\-/}
desimaaliosa1=${1##*.};
desimaaliosa2=${2##*.};
[[ ! ${apu1//[^.]/} ]] && desimaaliosa1=''
[[ ${apu1//[^.]/} ]] && { luku1=${apu1:0:18}; kokonaisluku=0 ;} || { luku1=${apu1:0:18}"."; kokonaisluku=1 ;}
[[ ! ${apu2//[^.]/} ]] && desimaaliosa2=''
[[ ${apu2//[^.]/} ]] && { luku2=${apu2:0:18}; kokonaisluku=0 ;} || { luku2=${apu2:0:18}"."; kokonaisluku=$(( 1 & $kokonaisluku )) ;}
 desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2})); $tulosta desimaaliosa1:$desimaaliosa1"   desimaaliosa2:"$desimaaliosa2"   desimaaleja:"$desimaaleja
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18}
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
 
(( $summa2 )) && : || summa2=000000000000000000
(( $kokonaisluku )) && tulos=${summa2/*(0)/}$summa1 || { apu=$summa2$summa1; tulos=${apu:0: -$desimaaleja}.${apu: -$desimaaleja} ;}
echo $merkki${tulos##+(0)}
echo tulos laskusta: $1 \* $2  . Ylärivi on bc:stä ja alarivi tästä skriptistä. ;}

clear
echo kun tulos siirtyy uudelle kymmenluvulle on syytä tarkistaa että desimaaalipisteen paikka siirtyy oikealla hetkellä
luku1=3.16227766; luku2=3.16227766;                  echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2     
luku1=3.16227767; luku2=3.16227767;                  echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2     

luku1=1 ; luku2=1 ;                                 echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=-1 ; luku2=1;                                 echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=1 ; luku2=-1;                                 echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=-1 ; luku2=-1;                                echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=.00900007567899123; luku2=900.07000012345678; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=111111.11111111111; luku2=123456789012345.67; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2 
luku1=10; luku2=10                                ; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=999999999999999999; luku2=999999999999999999; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=.9; luku2=2;                                  echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=.99999999999999999; luku2=.99999999999999999; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=.00000000000000001; luku2=.00000000000000001; echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2   
luku1=10000000000000000; luku2=.0000000000000001;   echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2
luku1=.0000000000000001; luku2=10000000000000000;   echo ------; echo; bc<<<"scale=40; $luku1*$luku2"; time kerro18 $luku1 $luku2   




 

14

Mikäli luvun numerot ylittävät 78:n tai 7.8:n  ei äskeisellä nopealla skriptillä kykene laskemaan edes kymmenenteen potensiin - vaan joutuu turvautumaan isoihin funktioihin. Alku on kuitenkin sama - ensin koetetaan tuota nopeaa ja jos siltä pyydetään liikaa niin siirrytään vapaaehtoisesti isoihin. Niillä saa sentään tuloksen 16 ensimmäistä numeroa oikein kun potensi on alle 60 - eivätkä ne koostaan huolimatta kovin hitaitakaan ole:
- suunnattoman kehnoahan tämä edelleen on - mutta ensisijainen tarkoitus onkin tutkia kuinka BASH saa ongelmat ratkaistua - ja tosiaan tuntuu siltä ettei tämä tähän jää.
- mikä skripteistä tuloksen antaakin niin siinä on noin 16 oikeaa desimaalia
- eihän tämä nopeaa ole, mutta BASH-skriptiksi ihan kelvollisen nopeaa.

Tämä tosiaan painottaa kuinka katala temppu 'kirjastokielto' on: kun muilla kielillä tämä tehdään yhdella käskyllä niin BASH:illa joutuu tekemään 60 lausetta vaikka tulos ei ole vieläkään kovin käyttökelpoinen. Esimerkiksi:
Koodia: [Valitse]
function potenssi () { [[ $1 =~ \. ]] || set -- $1'.0' $2; [[ ${1:0:1} == - ]] && tulos=000000000000000000$((${1//./}**$2)) || tulos=000000000000000000$((10#${1//./}**$2)); des=${1#*.};[[ $(($2*${#des})) -lt 19 ]] && { apu=${tulos:0: -$(($2*${#des}))}.${tulos: -$(($2*${#des}))}; apu=${apu##+(0)}; apu=${apu%%+(0)}; echo ${apu%.} ;} || iso_potenssi $1 $2 ;}

function kerro18 () {
tulosta=: # yhdessä paikassa päätetään tulostetaanko monessa paikassa välituloksia. Vaihtoehdot:tulosta=echo ja tulosta=:
luku1=${1:0:18}; luku2=${2:0:18}
# [[ ${#1} -gt 18 || ${#2} -gt 18 ]] && echo laskettavissa liikaa numeroita && return
$tulosta "annetut numerot: $1 $2"
[[ ${1:0:1} = - || ${2:0:1} = - ]]  && merkki=- || merkki='' 
[[ ${1:0:1} = - && ${2:0:1} = - ]]  && merkki='' 
apu1=${luku1//\-/}; apu2=${luku2//\-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.};
[[ ! ${apu1//[^.]/} ]] && desimaaliosa1=''
[[ ${apu1//[^.]/} ]] && { luku1=${apu1:0:18}; kokonaisluku=0 ;} || { luku1=${apu1:0:18}"."; kokonaisluku=1 ;}
[[ ! ${apu2//[^.]/} ]] && desimaaliosa2=''
[[ ${apu2//[^.]/} ]] && { luku2=${apu2:0:18}; kokonaisluku=0 ;} || { luku2=${apu2:0:18}"."; kokonaisluku=$(( 1 & $kokonaisluku )) ;}
 desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2})); $tulosta desimaaliosa1:$desimaaliosa1"   desimaaliosa2:"$desimaaliosa2"   desimaaleja:"$desimaaleja
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18}
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
# echo; echo tulos laskusta: $1 \* $2'  . Ylärivi on bc:stä ja alarivi tästä skriptistä:'
# bc<<<"scale=40; $1*$2"
(( $summa2 )) && : || summa2=000000000000000000
(( $kokonaisluku )) && tulos=${summa2/*(0)/}$summa1 || { apu=$summa2$summa1; tulos=${apu:0: -$desimaaleja}.${apu: -$desimaaleja} ;}
echo $merkki${tulos##+(0)} ;}

function iso_potenssi () {
apu=$( kerro18 $1 $1 )
for (( n=2; n<$2; n++ )); do
apu=$( kerro18 $apu $1)
done
[[ $1 = 0 ]] && { apu=1; [[ $2 = 0 ]] || apu=0 ;} # sopimus on että:  0 ^ 0 = 1 -> mutta 0 mihin tahansa muuhun potenssiin on nolla
echo; echo ---; echo ; echo "lasku on: $1^$2"
echo -n "bc:n laskema varmasti oikea tulos: ";bc -l<<<"$1^$2"
echo    "tämän  skriptin   laskema   tulos: "${apu:0:19} ;}

a=2; b=10; time potenssi $a $b      # 1024
a=2.0; b=10; time potenssi $a $b    # 1024
a=4.9999; b=4; time potenssi $a $b  # 624.9500014999800001  # time bc -l <<< "4.9999^4"
a=0.11; b=10; time potenssi $a $b   # .000000002357947691
a=0.9; b=9; time potenssi $a $b     # .387420489
#
# edellä esitetyt on ratkaistu aikaisemmin esitetyllä nopealla skriptillä ja tämänjälkeen laskenta siirtyy siihen isoon ja hitaaseen - mutta kyvykkäämpään. 
#
time potenssi 2 60 # jo 61 höpertää ja siitä ylöspäin vain tusina numeroita tuloksen alussa on oikein mutta suuruusluokkakin väärin
time potenssi 2.5 30
time potenssi -2 2
time potenssi -2 3
time potenssi 9.99999 18
time potenssi 0.987654321098765 10

15
En ole aikaisemmin laittanut foorumille skriptiä desimaalilukujen potenssiin korottamista varten sillä yksinkertaisen skriptin lukualue on onnettoman pieni eikä se korkeampia potensseja pysty käsittelemään ollenkaan - skripti on kylläkin tosi-nopea. Myöhemmin lisään koodiin suurempien lukujen ratkaisemiseksi tarvittavat osat.
- jos potenssiin korotettava luku on kokonaisluku niin tässä skriptissä sen perään lisätään .0 . Elikä: [[ $1 =~ \. ]] || set -- $1'.0' $2 -> set muodostaa uuden parametrijoukon (elikä nuo $1 $2 $3 $4 ... nimiset) sille annetuista muuttujista - nollaten kaikki vanhat - siis kaikki jolle halutaan joku arvo on liitettävä mukaan - joten jos tässä ei olisi lopussa tuota $2:ta niin $2 jäisi määrittelemättä - mutta tällätavoin uusi $2 saa vanhan $2:n arvon.
- kaikilla käskyillä voi olla parametreja ja niin myös käskyllä: set. Tässä heti set:in jälkeen on -- ja se kertoo tulkille että set:in omat parametrit on tässävaiheessa jo annettu joten seuraavassa luvussa mahdollisesti oleva - on miinusmerkki.
- muuten: ehto && teko1; teko2 || teko3 täytyy yleensä merkitä: ehto && { teko1; teko2 ... ;} || teko3 tai se merkitsee ihan muuta kuin on tarkoitus.
- laskettavien edessä on usein:10# . Se johtuu siitä että luvun ensimmäinen merkki saattaa desimaaleilla toimittaessa olla nolla ja kun luvussa on etunolla siirryttään laskuissa oktaali-järjestelmään ellei ole käskyä pysyä desimaalijärjestelmässä. Sitä kutsutaan oktaali-ansaksi ja sen inhottavin piirre on se että mikäli laskettavissa ei ole numeroita 8 tai 9 lasku onnistuu ihan hyvin - mutta tulos on oktaaliluku vaikka sitä luullaankin desimaaliluvuksi. Ja koska kyse on BASH:ista niin tottakai tuolla 10#:llä on poikkeuskin: negatiiviset luvut eivät sitä tarvitse ja peräti aiheuttavat virheen. Siksi tuo pikä litania:
Koodia: [Valitse]
[[ ${1:0:1} == - ]] && tulos=000000000000000000$((${1//./}**$2)) || tulos=000000000000000000$((10#${1//./}**$2))

Ja sitten itse skripti:
Koodia: [Valitse]
function potenssi () { [[ $1 =~ \. ]] || set -- $1'.0' $2; [[ ${1:0:1} == - ]] && tulos=000000000000000000$((${1//./}**$2)) || tulos=000000000000000000$((10#${1//./}**$2)); des=${1#*.};[[ $(($2*${#des})) -lt 19 ]] && { apu=${tulos:0: -$(($2*${#des}))}.${tulos: -$(($2*${#des}))}; apu=${apu##+(0)}; apu=${apu%%+(0)}; echo ${apu%.} ;} || echo liian iso luku;}
 
# tarkistuksia:
a=-.2; b=10; time potenssi $a $b    # .0000001024
a=.2; b=10; time potenssi $a $b     # .0000001024
a=-2; b=10; time potenssi $a $b     # 1024
a=2; b=10; time potenssi $a $b      # 1024
a=2.0; b=10; time potenssi $a $b    # 1024
a=4.9999; b=4; time potenssi $a $b  # 624.9500014999800001  # time bc -l <<< "4.9999^4"
a=0.11; b=9; time potenssi $a $b    # .000000002357947691
a=0.9; b=9; time potenssi $a $b     # .387420489
a=2.11111; b=4; time potenssi $a $b # liikaa numeroita

16
Virtuoosit haukkuivat aikoinaan maanrakoon sen tavan jolla BASH:in direktiivi: eval on toteutettu. Eihän taviksilla ole mitään mahdollisuutta tarkistaa haukkumien oikellisuutta vaan on pakko uskoa ja käyttää toisia keinoja. Tässä eval on korvattu käskyllä:
Koodia: [Valitse]
$(declare -p $1); declare ${apu2:8:2} apu=${apu2#*=}
ja muuttamalla käskyssä myöhemmin olevat $1:t $apu:ksi ja poistamalla turhiksi tulleita keno-viivoja. Muuten skripti on sama kuin edellinen.
- tosin toiminta näillä pidemmillä määrityksillä on hieman hitaampaa.
- muuten vielä 20% nopeuden-muutokset hukkuvat BASH:in epämääräisyyksiin:
Koodia: [Valitse]
# ensin tehdään taulukko josta sitten etsitään - ja vaikka taulukko muodostetaan satunnaisesti tiedetään suunnilleen mitä siellä on.
#
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
#
# selvitystä tuosta | xargs -n$(($(tput cols)/35)) | column -t:sta: tulostettaessa taulukkoa BASH:issa ei yleensä kannata yrittääkään muotoilla tulosteita vaan kirjoittaa
# jokaisen tulostetun numeron perään välilyönti ja kirjoittaa skriptin perään: | column -t . 'column -t' on BASH:in muotoilu-ohjelma joka lisää jokaisen välilyönnin
# kohdalle niin monta välilyöntiä lisää että tulostuksesta tulee muotoiltu siten kuin jos tulostuskäskyissä olisi ollut hyvä muotoilu.
#
# mutta tässä tuo xargs vie homman astetta pidemmälle ottamalla huomioon sen näytön leveyden joka on sillä monitorilla jossa se toimii:  koska xargs ei viittaa mihinkään
# ohjelmaan niin oletetaan että se viittaa echoon. Tavallaan 'xargs -n' laskee välilyöntejä ja muuntaa -n:än jälkeisen muuttujan määräämin välein välilyönnin
# rivinsiirroksi. Tässä tuo muuttuja on: $(($(tput cols)/35)) joka ensin selvittää sen monitorin rivinpituuden jossa se annetaan ja kertoo siten kuinka monta 35:n merkin
# pituista ryhmää riville mahtuu. Näin tulee millä näytöllä tahansa siisti taulukko - tai ainakin sellainen josta selviää välittömästi numeroiden erilaisuus - muuten
# tällainen muotoilu ei taulukoilla juuri koskaan epäonnistu. 

   function tulosta_kokonaiset () { apu2=$(declare -p $1); declare ${apu2:8:2} apu=${apu2#*=}; printf "%s\n" ${apu[@]%.*} ;}
   function tulosta_desimaalit () { apu2=$(declare -p $1); declare ${apu2:8:2} apu=${apu2#*=}; printf "%s\n" ${apu[@]#*.} ;}
function tulosta_koko_matriisi () { apu2=$(declare -p $1); declare ${apu2:8:2} apu=${apu2#*=}; printf "%s\n" ${apu[*]} ;}
function minmax (){
echo -n 'matriisin desimaali-osien maksimi on: .'; tulosta_desimaalit $1 | sort -d | tail -1 ;
echo -n 'matriisin kokonais-osien maksimi on: '; tulosta_kokonaiset $1 | sort -n | tail -1 ;
echo -n 'koko matriisin maksimi on: '; tulosta_koko_matriisi $1 | sort -n | tail -1 ;
echo
echo -n 'matriisin kokonais-osien minimi on: '; tulosta_kokonaiset $1 | sort -n | head -1 ;
echo -n 'matriisin desimaali-osien minimi on: .'; tulosta_desimaalit $1 | sort -d | head -1 ;
echo -n 'koko matriisin minimi on: '; tulosta_koko_matriisi $1 | sort -n | head -1 ;}; time minmax koematriisi

17
Aikoinaan väitin että funktion koodin voi kirjoittaa kutsun paikalle joten nyt mukaelin edellistä skriptiä sillätavoin - tai tässä tapauksessa päinvastoin erotin koodipätkiä funktioihin. Vaihto onnistui heti ja hämmästyksekseni laskenta myös nopeutui hieman - kuvittelisin että nopeutus on seurausta siitä ettei yksittäisiä käskyjä voida lukea välimuistiin - tai ei kannata sillä oikeellisuutta ei voi todeta - mutta funktiot voidaan lukea välimuistiin. 
- lisäksi koodi on paremmin jäsennelty - joten koodin toiminnan ymmärtää helpommin.
- esimerkiksi kun matriisin koko on 170000 jäsentä kestää varsinainen lasku vain nelisen sekuntia - sekin kyllä aivan liian pitkä aika. Mutta aikaisemmin kukaan ei ole edes kuvitellut tekevänsä tämmöistä BASH:illa koska se kestäisi normaalisti käytettävillä keinoilla tunteja.
Koodia: [Valitse]
# ensin tehdään matriisi josta sitten etsitään:
clear; koematriisi=($(for n in {1..170000}; 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

# ja sitten etsitään:
function tulosta_kokonaiset () { eval printf "%s\\\n" \${$1[*]%.*} ;} #1
function tulosta_desimaalit () { eval printf "%s\\\n" \${$1[*]#*.} ;} #2
function tulosta_koko_matriisi () { eval printf "%s\\\n" \${$1[*]} ;} #3
function minmax (){ echo -n 'matriisin desimaali-osien maksimi on: .'; tulosta_desimaalit $1 | sort -d | tail -1 ;
echo -n 'matriisin kokonais-osien maksimi on: '; tulosta_kokonaiset $1 | sort -n | tail -1 ;
echo -n 'koko matriisin maksimi on: '; tulosta_koko_matriisi $1 | sort -n | tail -1 ;
echo
echo -n 'matriisin kokonais-osien minimi on: '; tulosta_kokonaiset $1 | sort -d | head -1 ;
echo -n 'matriisin desimaali-osien minimi on: .'; tulosta_desimaalit $1 | sort -n | head -1 ;
echo -n 'koko matriisin minimi on: '; tulosta_koko_matriisi $1 | sort -n | head -1 ;}; time minmax koematriisi

- funktiot #1 , #2 ja #3 muodostuvat yhdestä käskystä - eval ei ole käsky vaan se kehottaa kääntäjää tulkitsemaan eval:ia seuraavan käskyn 2 kertaa - keno suojaa ensimmäisellä kerralla seuravaa merkkiä tulkinnalta mutta suojaamattomat tulkitaan - jonka jälkeen suojana olevat kenot poistetaan joten toisella kerralla tulkitaan kaikki. Muuten \\\n tulkitaan seuraavasti: \n on n ja \\ on \ elikä yhdessä \n elikä rivinsiirto.
 

18
Skriptaajat on peloteltu uskomaan että BASH:illa ei oikeastaan matikkaa olekaan - mutta itseasiassa BASH:in matikallakin on terve perusta - sitä ei vaan kaivata kilpailemaan valmis-ohjelmien kanssa - ja nykyään se on käytön-puutteessa surkastunut.

- käsitelläänpä välillä BASH:in matematiikan nopeutta:annapa käsky: apu=$( seq -s+ 1000000); time echo $(($apu)) - se laske yhteen luku kerrallaan yhdestä miljoonaan ja kestää minun huonolla koneellani 129ms elikäs 129 nanosekuntia yhteenlaskulta - plus, miinus, kerro, jaa ja ota jakojäännös kestävät kaikki suurinpiirtein samanverran. Suoritus-ajan yläraja on käsitykseni mukaan noin 50 mikrosekuntia jos laskee yhteen vain kaksi lukua. Joten tällä välillä liikutaan - aika riippuu siitä kuinka lasku määrätään suoritettavaksi. Luvuissa voi olla korkeintaan 18 numeroa ja nekin pelkästään kokokonaislukuja - mutta 36 numeroinen liukuvan pilkun kertolasku, todella moninumeroinen desimaalilukujen jakolasku ja 72 numeroinen desimaalilukujen yhteenlasku toimivat omissa funktioissaan nopeudella luokkaa 1ms. Yhdeksän numeroinen neliöjuuri vie kyllä jo 1-3ms.

Ja sekin erinomainen piirre BASH:in numeron-murskaamisessa on että jokainen kuviteltavissa oleva tehtävä on ratkaistavissa lukemattomilla täysin erilaisilla tavoilla. Mutta tämä aikaansaa sellaisen ongelman että kun alkaa tutkia jotakin niin päätyy usein tutkimaan jotakin ihan muuta.
Aloin tutkia kuinka BASH selvittää virhetilanteita ja päädyin tutkimaan desimaali-matriiseita. Ja osoittautui että kun esimerkiksi nyt esitettävä tehtävä muodostuu 6:sta osatehtävästä joista jokainen kestää 10 ms on kokonaisaika silti alle 20ms - ja samaa aikaa tarjoaa myös ajoitus-ohjelma nano - selitys nopeudelle löytynee time-käskyn tulosteesta: user+sys=2*real - ja ainakin sys-alueella tehdään rinnakkais-prosessointia.

Tarkoituksena on tutkia kuinka desimaali-luvuista muodostuneesta matriisista kannattaa etsiä maksimeita ja minimeitä:
Koodia: [Valitse]
### aluksi muodostetaan matriisi josta sitten etsitään:
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 muotoilee jo tehdyn tulosteen - se siis nappaa tulosten näyttö-puskurista, muotoilee sen silmää miellyttävään kuntoon ja palauttaa takaisin näyttöpuskuriin josta se aikanaan päätyy näyttöön - tulos on kotikoneella ajettuna paljon parempi kuin miltä foorumin tulosteesta näyttää.

### sitten aloitetaan varsinainen etsintä:
function minmax (){ echo -n 'matriisin desimaali-osien maksimi on: '; eval printf "%s\\\n" \${$1[*]#*.} | sort -d | tail -1 ;
echo -n 'matriisin kokonais-osien maksimi on: '; eval printf "%s\\\n" \${$1[*]%.*} | sort -n | tail -1 ;
echo -n 'koko matriisin maksimi on: '; eval printf "%s\\\n" \${$1[*]} | sort -d | tail -1 ;
echo
echo -n 'matriisin desimaali-osien minimi on: '; eval printf "%s\\\n" \${$1[*]%.*} | sort -d | head -1 ;
echo -n 'matriisin kokonais-osien minimi on: '; eval printf "%s\\\n" \${$1[*]#*.} | sort -n | head -1 ;
echo -n 'koko matriisin minimi on: '; eval printf "%s\\\n" \${$1[*]} | sort -n | head -1 ;}; time minmax koematriisi


- 'sort -d':n -d on selväkielisenä: --dictionary-order. Kielitieteilijä-matemaatikko saisi siitä ehkä tolkkua mutta netit ja sanakirjat eivät saa. Toiminnaltaan se on desimaali-sorttaus - siis se ottaa huomioon että desimaaleilla numeroiden paikka-arvo on laskeva - mutta kokonaisosan numeroiden paikka-arvo on nouseva.


19
 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


20
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 ;}
}

Sivuja: [1] 2 3 ... 35