Kirjoittaja Aihe: Skriptiajuri  (Luettu 144742 kertaa)

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #60 : 22.01.24 - klo:21.48 »
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.
« Viimeksi muokattu: 26.01.24 - klo:13.27 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #61 : 09.02.24 - klo:08.36 »
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.
« Viimeksi muokattu: 18.02.24 - klo:15.40 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #62 : 20.02.24 - klo:23.37 »
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.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #63 : 29.02.24 - klo:17.35 »
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.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #64 : 06.03.24 - klo:10.43 »
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:

nm

  • Käyttäjä
  • Viestejä: 16424
    • Profiili
Vs: Skriptiajuri
« Vastaus #65 : 06.03.24 - klo:13.44 »
Mutta eipä BASH:ista ihan kohta taideta eroonkaan päästä päätellen siitä mitä vähäsen taas lueskelin

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.

Esittely suomeksi: https://www.linux.fi/wiki/Zsh
Käyttöopas englanniksi: https://zsh.sourceforge.io/Guide/zshguide.html

Modernimpaa shell-ympäristöä kaipaavalle on vaihtoehtona myös mm. Nushell. Se poikkeaa eri tielle perinteisten Bourne-shellien polulta, mutta tarjoaa esimerkiksi sisäänrakennetun Sqlite-tietokannan sekä erittäin tehokkaita ominaisuuksia tyypitettyjen taulukoiden käsittelyyn ja matematiikkaan. Kehitteillä on myös laajempi standardikirjasto.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #66 : 08.03.24 - klo:17.20 »

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.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #67 : 12.03.24 - klo:08.46 »
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.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #68 : 13.03.24 - klo:06.33 »

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?   

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #69 : 16.03.24 - klo:12.13 »
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

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #70 : 18.03.24 - klo:07.13 »
- 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ää?

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #71 : 18.03.24 - klo:12.22 »
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 


petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #72 : 19.03.24 - klo:17.03 »
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
« Viimeksi muokattu: 23.03.24 - klo:18.38 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #73 : 23.03.24 - klo:18.42 »
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.
« Viimeksi muokattu: 24.03.24 - klo:10.47 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #74 : 28.03.24 - klo:09.48 »
Matemaattista yhtäsuuruuden vertaamista ei kannata tehdä matemaattisesti sillä se rajoittaisi merkkiluvun 18:sta eivätkä  desimaalit toimisi ollenkaan - siksi on parempi suorittaa vertailu tekstijonoilla.

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

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

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

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

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

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

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

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

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

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

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

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

   


« Viimeksi muokattu: 28.03.24 - klo:10.26 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #75 : 06.04.24 - klo:20.05 »
Aikamonta muutosta 8.4.2024

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

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

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

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

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

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

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

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

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

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

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

petteriIII

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

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

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

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

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

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

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

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

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

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

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

petteriIII

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

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

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

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

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

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

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

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

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

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Skriptiajuri
« Vastaus #78 : 25.04.24 - klo:08.55 »
Kymmenen vuotta sitten yksi kaveri täällä foorumilla sanoi: miksi tehdä helpolla kun voi tehdä vaikeastikin? Luulin sitä silloin vitsiksi mutta siinä taisi olla vähän totuuttakin takana, sillä se selittäisi miksi vanhat käskyt hylättiin kolmekymmentä vuotta sitten: skriptit olivat liian helppoja niillä tehdä ja senaikaisen nopeuskäsityksen mukaan myös liian nopeita - ei taviksien käsiin sellaisia saa antaa.

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

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

petteriIII

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

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

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