Kirjoittaja Aihe: Ohjeita shell-skriptaukseen (bash)  (Luettu 636223 kertaa)

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #340 : 01.02.26 - klo:13.36 »
Tässä on 16 numeroinen neliöjuuri-laskun tarvitsema summaaja-vähentäjä. Aluksi tehdään versio joka toimii ainoastaan kokonaisluvuilla ja vasta pitkien testauksien jälkeen sitä voi alkaa sovittamaan desimaaliluvuille. Inhottavan näköistä koodia siinä on paljon - mutta BASH:iksi se on kuitenkin hyvin nopea vaikkakin muiden kielien kannalta todella hidas - tämä funktio vie aikaa alle millisekunnin - ja bc tai awk:kin kuluttaa aikaa 2-3 millisekuntia samaan laskuun - se 3ms kestää ennenkuin ne aloivat toimia.

BASH:in toiminnan tarkoitus on opettaa muunmuassa kuinka vaikeita alkeis-tehtävätkin ovat ratkaista siten että ne toimisivat aina moitteettomasti - ja nehän ovat samat kaikissa kielissä. Ja jokainen kieli on samojen matematiikan sääntöjen sitoma ja kaikki ratkaisevat tämmöisiä olemattoman kokoisia tehtäviä sammallatavoin - muut kielet tosin ratkovat ne kirjastojensa sokkeloissa ja prosessoriensa langoituksessa - joten varmaankaan et ole noita ratkaisuja nähnytkään. Mutta riittää kun tietää että niitä on.

- eihän matematiikkaa voi millään kielellä toteuttaa moitteettomasti kokonaan vaan aina tulee tilanteita joissa nikotellaan. Ja koska BASH:in on valehdeltu olevan täysin kykenemätön mihinkään eikä ole tehty ohjelmia alkeis-ongelmia ratkomaan niin että kun BASH alkaa nyt siitä missä toiset kielet olivat yli kolmekymmentä vuotta sitten niin BASH nikottelee alkuunsa ihan kaikissa tehtävissä. Mutta se hyvä puoli näissä ohjelmallisissa ratkaisuissa on että vaikka ne ovat todella hitaita niin kaiken saa lopulta toimimaan.

- kielen toiminnassa arvostetaan usein joustavuutta nopeutta enemmän - joten jos kielen perusteet ovat kunnossa niin joustavampi voittaa ilmanmuuta - ja vanhoilla käskyillä BASH on erittäin joustava - ja siitähän se riemu repesi sillä sellaista ei voi sallia että harrastelijan työkalu olisi parempi kuin ammattilaisella joten BASH:in vanhemmat veivät lapseltaan hampaat. Onneksi he eivät uskaltaneet kajota tulkkiin ja vanhat käskyt ovat tulkissa.
 
Pikku-tehtävistä tämä oli ehdottomasti elämäni työläin tehtävä - koska en kuunnellut omia puheitani siitä että vähennyslasku käsitellään ensimmäisenä - silloinkin kun toiminnan aikana saattaa jostain väli-tuloksesta tulla negatiivinen:
Koodia: [Valitse]
function summaa36 () { # ensimmäisen luvun tulee olla positiivinen ja suurempi mutta toinen pienempi luku voi olla negatiivinenkin

tulosta=: # yhdessä paikassa päätetään tulostetaanko skriptissä välituloksia missään. Vaihtoehdot:tulosta=echo ja tulosta=: . : on käsky olla tekemättä  mitään ja kuitenkin sillä voi olla parametreja aivan niinkuin echo:llakin.

luku1=$1
luku2=$2
[[ ${1:0:1} = - ]]  && merkki1=- || merkki1='+'
[[ ${2:0:1} = - ]]  && merkki2=- || merkki2='+'
luku1=${luku1//\-/}; luku2=${luku2//\-/} # poistetaan merkki -

luku1="0000000000000000000000000000000000000"$luku1; luku1=${luku1: -36:36}
luku2="0000000000000000000000000000000000000"$luku2; luku2=${luku2: -36:36}   

luku11=${luku1:0:18}; luku12=${luku1:18:18} #; [[ $luku11 -eq 0 ]] && luku11=0
luku21=${luku2:0:18}; luku22=${luku2:18:18} #; [[ $luku22 -eq 0 ]] && luku22=0                             
$tulosta annettu_luku           18_ensimmäistä_numeroa     18_viimeistä_numeroa
$tulosta $luku1 $luku11    $luku12 
$tulosta $luku2 $luku21    $luku22

alivuoto=0; ylivuoto=0
apu1=$((10#$luku12$merkki2 10#$luku22))
# [[ ${#apu1} -eq 19 ]] && { apu1=${apu1:0:18}; ylivuoto=1 ;} ; [[ $apu1 -lt 0 ]] && { apu1=$(($apu1+"100000000000000000")); alivuoto=1 ;}  # epäilyttävä
[[ $apu1 -lt 0 ]] && { apu1=$(($apu1+"1000000000000000000")); alivuoto=1 ;}; [[ ${#apu1} -eq 19 ]] && { apu1=${apu1:1:19}; ylivuoto=1 ;} ;   # epäilyttävä
apu1="000000000000000000000"${apu1#[-+]}; apu1=${apu1: -18:18}; $tulosta vähemmän merkitsevien lukujen summa: $apu1
apu2=$((10#$luku11$merkki2 10#$luku21+$ylivuoto-$alivuoto)); $tulosta enemmän merkitsevien lukujen summa: $apu2 ylivuoto: $ylivuoto alivuoto: $alivuoto

apu=$apu2${apu1#0}; echo ${apu##+(0)} ;}

# esimerkkilaskuja:
echo -e '\n\n'; time summaa36 22222222222222211111111111111111 2222222299999999999922222222221   
echo -e '\n\n'; time summaa36 22222222222222211111111111111111 -2222222299999999999922222222221
echo -e '\n\n'; time summaa36 555 -6
echo -e '\n\n'; time summaa36 555 6

-tulokset voi tarkistaa vaikka bc-llä esimerkiksi käskyllä: bc<<<"22222222222222211111111111111111+2222222299999999999922222222221"

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #341 : 02.02.26 - klo:20.53 »
Tämä on vasta raakile ja päivitetään aikanaan:

Tietokoneen jokainen toiminto määritellään funktioilla - yksittäiset käskytkin ovat funktioita mutta yleisemmin funktio on käskyryhmä. BASH:issa voidaan määritellä uusia funktioita tai määritellä vanhoja funktioita uudestaan - vaikka määritellä sama funktio uudestaan kerran päivässä maailman tappiin asti.

BASH:issa on kymmenissä paikoissa erilaisia asioita käsitteleviä käskyjä kymmenintuhansin ja niistä useimmilla monenlaisia toimintoja kymmenittäin joten luotettavan kirjastofunktion tekeminen yksitäisistä käskyistä on BASH:issa toivottoman hidasta koska niin suuresta joukosta on hidasta valita oikeat käskyt tai edes päätellä mikä on paras tapa toimia - ja toimintatapojen nopeuserot ovat usein suuria. Tai semmoinenkin ilmiö on että tuhannesta niistä käskyistä jotka teoriassa saattaisivat skriptissäsi toimia on vain sata sellaista jotka tosiaan toimivat, niistä vain kymmenen on sellaista jotka toimivat hyvin mutta vain yksi tai kaksi sellaista jotka toimivat moitteetta.

Muissa kielissähän käyttäjät eivät yleensä saa kirjastofunktioita tehdäkään vaan kyllä se on melkolailla yksinomaan BASH:in ominaisuus.

Kunnolliset funktiot ovatkin toiminnalle välttämättömiä joten niiden soisi löytyävän aina - joten paras säilytyspaikka niille on kirjasto. BASH:in kirjastoja ei vaivaa mikään muu kuin mustamaalaus.
BASH:in kirjastoja on mustamaalattu niin kauan että täytyy kertoa kuinka niitä käytetään: kirjasto on ihan normaali tiedosto johon talletettu funktion muotoon kirjoitettuja skriptejä ja joissa BASH:in kyseessä ollen ei tarvita lausetta: #!/bin/bash eikä suorituoikeuttakaan tarvita. Kaikki tuon tiedoston funktiot saa käyttöönsä skriptiä tehdessä kirjoittamalla skriptin alkuun:
. sen_tiedosto_nimi_polkuineen_johon_funktio_on_talletettu
- siis tiedostonimen edessä on piste - sillä haluttiin antaa lyhyt nimi toiminnolle jota käytetään aina - kaikissa muissakin kielissä kirjastot esitetään alussa ja niitähän on ylensä useampiakin. Voi pisteen paikalle kirjoittaa myös sanan source - jolloin toiminta muuttuu aavistuksen verran.
- käytännössä kirjastoja pitäisi olla erityyppisille hommille omansa - ihan niinkuin muissakin kielissä.
- tai on siinä väitteessä huonosta tietoturvasta merkki perääkin sillä tietokonehan on tietoturvariski tekee sitten mitä vaan - vaikka maalaisi PC:n vihreäksi ennen mereen heittämistä.

Voit itse miettiä kirjastojen tarpeellisuutta: kun haluat tehdä jotakin niin haluatko:
1. Ilmoittaa halusi yhdellä sanalla - kirjastosta sana ja sitä vastaava toiminto löytyy varmasti ja toiminto on valmiiksi testattukin.
2. Liittää skriptiisi kymmeniä-satoja-tuhansia lauseita etsittyäsi niitä jostain huitsin nevadasta jos niitä ylipäätään löydätkään. Ja elää sitten sotkun keskellä ja pähkäillä kuinka jatkossa toimitaan sillä eivät nuo lauseet ihan semmoisenaan skriptiin sovi. Ja testata sitten aikaansaannostasi kauan ja elää sittenkin epävarmuudessa siitä että toimiiko skripti aina oikein. Ja tapellen funktion sivuvaikutusten kanssa?

Aikoinaan BASH:illa olikin käytössään kirjasto mutta silloin BASH:illa ei vielä ollutkaan kilpailijoita. Kun BASH:ille alkoi tulla kilpailijoita niin BASH:in omat virtuoosit siirtyivät välitömästi käyttämään niitä ja yrittivät tehdä tulokkaasta uuden skriptikielten kuninkaan - sillä ei käsitetty ensinäkin sitä ettei kukaan pysty tekemään merkittävämpää BASH-skriptiä järkevässä ajassa ellei hae sen kriittisiä osia kirjaston funktioista ja toiseksen että BASH:ista on mahdotonta luopua kokonaan.

Mutta jotta jostain uudesta skriptikielestä saisi skriptikielten kuninkaan täytyy vanha kuningas elikä BASH ensin tuhota.

Minkätahansa tietokone-kielen tuhoamiseksi kirjastojen kieltäminen on ylivertaisen pätevä keino - mutta kirjastot ovat BASH:inkin perusta eikä niitä voi kokonaan kieltää käyttämästä vaan ainoastaan tehdä niiden käyttämisestä hankalaa poistamalla funktioiden tiedostopolkuun viittaava kirjasto-osoitin. Mutta kun lisäksi alettiin mustamaalata BASH:in kirjastoija niin ne harvat jotka kirjastoja olivat käyttäneet eivät enää uskaltaneet kirjastoja käyttää. Ja nopeasti kehittyikin luulo ettei BASH:illa edes ole kirjastoja.

Mutta ne jotka BASH:ia edelleen kehittävät ja ylläpitävät eivät virtuoosien puheista välitä vaan he käyttävät kirjastoja ihan niinkuin ennenkin.
- esimerkiksi kun mählit skriptinteossa niin koneesi näytölle tulee usein viesti: command not found. Viestin kirjoittaa kirjastossa oleva funktio -> skriptit ovat pääte-ohjelman lapsia ja kun lapsi mählii niin pääte havaitsee sen ja kutsuu kirjastosta vikaselvitykseen funktiota nimeltä command_not_found_handle - joka sitten kirjoittaa näytölle: command not found - ellei muuta ole määrätty.
- itseasiassa on tarkoitus että skriptaaja kirjoittaan tuon: command_not_found_handle funktion uusiksi - sellaiseksi kuin senhetkisen skriptin mahdoliten vikatilanteiden selvittäminen vaatii. Mutta asia on liian monimutkainen virtuoosien käsittää ja niinpä he vain haukkuvat BASH:in vianselvitystä olemattomaksi.
- koneessasi on noin 84 muutakin tällaista myöhemmin määriteltyä kirjastofunktiota BASH:in omiin käyttötarkoituksiin - kehittäjät siis haistattavat huilua virtuooseille.
- funktioita voidaan määritellä ohjelmallisestikin mutta niin tehdään käytännössä hyvin harvoin - joskus kuitenkin: BASH voi siis opettaa itseään.

Ei BASH tuhoutunut niissä virtuoosien meingeissä mutta invalidi siitä kyllä tuli. Vaan ei niistä aikomuksista saada uusi kuningas myöskään tullut mitään.

Joten virtuooseilla oli ongelma: halusivatko he olla hyvin pieniä kaloja isossa Python-järvessä vaiko isoja vonkaleita pienessä BASH-lammessa? Moni valitsi BASH:in ja alkoi opettaa BASH:ia niiden uskottelujen kanssa joita he olivat BASH:ista aikoinaan puhuneet. Kyllä virtuoosit silti pääasiassa asiaa puhuu - mutta he kertovat vain alkeita siitä kuinka sinun tulisi toimia eivätkä ollenkaan sitä kuinka jaat tietojasi toisille ja otat tietoja toisilta. Sillä skriptaaminen ei ole yhden miehen juttu vaan kyllä se on aina ryhmätyötä - siis ei niin että skriptissäsi on jostain nyysitty idea vaan siinä täytyy olla useita nyysittyjä ideoita ja lisäksi omasikin - ja lopuksi jaat sriptisi toisillekin jotta he saisivat nyysiä siitä ideoita. Jos en olisi pakana niin sanoisin: hiellä sinun pitää leipäsi hankkiman - se ei ole rangaistus vaan ohje kuinka saat onnellisen elämän.

- totta vai tarua? Jokatapauksessa väitteissä on paljon perää niinkuin jatkossa selviää.

***

Kyllä virtuoosit siis ilmanmuuta tajusivat kirjastojen tarpeen mutta heillä oli jo toinen skriptikieli kiikarissa ja he halusivat tästä uuden skriptaamisen kuninkaaan - sillä BASH oli jo niin mennyttä ja vaikeata. Voidakseen osoittaa BASH:in olevan surkimus oli estettävä sitä kehittymästä. Helppo nakki - tehtiin uusi käskykanta joka oli kammottavan hidas. Vanhoilla käskyillä monet toiminnot ovat tuhansia? kertoja nopeampia kuin uusilla käskyillä - siis jos vanhoilla käskyillä sai aikaan millisekunnissa toimivan niin uusilla se kestäisi sekunteja. Tämä johtaa siihen että uusia käskyjä käytettäessä testauksesta tulee niin hidasta että luullaan ettei homma edes toimi.

Noiden vanhojen käskyjen kirjoitusasu on kummallinen joten ne ovat vaikeita kirjoittaa ja muistaakin. Mutta ne ovat nopeita ja ennenkaikkea kääntäjä-ystävällisiä -> kääntämisessä on käytössä  sama cache-menettely kuin kaikessa muussakin. Ja cachen koolla on rajansa ja kun sinne käänetään käskyjä niin noita vanhoja ja pieniä mahtuu sinne tusinoittain mutta uusia ja suuria käskyjä vain muutama. Joten uusilla käskyillä tehdyssä skriptissä kutsutaan kääntäjää vähänväliä mutta yksinomaan vanhoista käskyistä tehdyssä vain kerran.

Mutta eivät vaikeudet kirjastokelpoisen funktion valmistumiseen tähän lopu sillä kirjastojahan kielletään käyttämästä - siis jos teet itsellesi kirjaston niin ei se muiden BASH:iin noinvain siirry.

---

Kirjasto on sama asia kuin funktiovarasto - mutta voi kirjastossa toiminnallistakin koodia olla - esimerkiksi tuloste: kirjasto sejase liitetään koodiin.

Kaikki ohjelmointikielet tarvitsevat funktioita ja ne ovatkin yksi BASH:in tukijaloista. Mutta tätä ei skriptaajille ole kerrottu - vaan päinvastoin vietiin BASH:ilta kirjasto-osoitin mikä tekee kirjastojen käyttämisen vaikeaksi ja vähentää funktioidenkin arvoa. Mutta ei tuo kirjastojen "käyttökielto" kehittäjiä koske ja niinpä koneessasikin on kirjaston alku elikä noin 85 funktiota - mutta ne helpottavat ainoastaa kehittäjien hommia ja skriptaajat hypätköön sementtiin.

BASH:in omatkin käskyt ovat tavallaan funktioita. Ja ne mitä normaalisti kutsutaan kytkimiksi ovat tavallaan funktion parametreja.

Funktio on määriteltävä ennenkuin siihen viittaa - joko se on kirjoitettava koodin alkuun tai luettava muistiin ennen skriptin ajamista.

Kun funktio on suoritettu niin palataan skriptiin funktiokutsun perään - jos funktiokutsu on ollut keskellä lausetta niin samaan lauseeseen heti kutsun perään. 

Saat määritellyt funktiot näkyviin käskyllä: declare -f. Toki voit käyttääkin niitä jos tahdot: esimerkiksi anna päätteessä käsky: quote hilivinkkeli jolloin vastaksena tulee: 'hilivinkkeli'. Jos teet funktioita koneeseesi niin noiden joukkoon nekin ilmestyvät.

Kirjasto voi olla yksittäinen tiedosto jossa on monia funktioita mutta yleensä kirjasto on kansio jonka jokaisessa tiedostossa on samaa asiaa käsitteleviä funktioita.

Kirjastossa voi olla myös kutsuja kirjaston toisiin funktioihin - eteenpäin viittauksetkin sallitaan.

Kirjastofunktioista monet ovat yksinkertaisia ja niistä onkin helppo saada käsitys mistä on kyse. Esimerkiksi:

function alku () { echo 'tästä funktionteko alkaa' ;};
- tämänjälkeen jokakerran kun kirjoitat skriptiisi sanan: alku  kirjoittuu tulostukseen: tästä funktionteko alkaa. Siis yksinkertaisimmassa muodossaan funktio on sama kuin alias.

Mutta jotkut kirjastofunktiot ovat pitkiä ja loogisestikin monimutkaisia - ja varsinkin BASH tarvitsee niitä - kirjastofunktioista saisi edes esimerkin siitä miten skriptit tehdään.

Kirjastojen lukumäärä ja skriptien lukumäärä niissä on viite skriptikielen käyttökelpoisuudesta. Niinpä kumpiakin tarvittaisiin tuhansittain.

***

Vakaan skriptin tekeminen ilman funktiokutsujen apua on melko hyödytön yritys. Koska annetaan ymmärtää ettei BASH:issa funktiotioita kannata käyttää niin BASH kituu - vaikka onkin täysin selvä että Pythonin harrastajat väittää näin niin pistää tosiaan ihmettelemään minkätakia BASH:in omat "virtuoosit" eivät laita näitä väittäjiä ruotuun. Funktiot helpottavat skriptitekoa suunnattomasti sillä ne tekevät pitkän toimintosarjan yhdellä käskyllä - ja usein tavalla jota et ole tullut ajatelleeksikaan ja jota yleisesti väitetään mahdottomaksi - ja joskus jopa nopeasti ja hyvin. Hyvät funktiot ovat usein työläitä kirjoittaa, mutta yleensä ne kirjoittaakin joku toinen - ja jos joudut itse kirjoittamaan funktioita niin ne kirjoitetaan vain kerran ja sijoitetaan senjälkeen kirjastoon jonka jälkeen ne ovat aina käytettävissä.

Maailmalla on valmiita ja hyvälaatuisia funktioita noin ziljoona joten voisihan niitä toimittaa alkuasennuksenkin yhteydessä - mutta niin ei tehdä sillä BASH:ista halutaan eroon mutta siihen ei toistaiseksi pystytä - mutta sitä ei missään nimessä haluta että kukaan käyttäjistä käyttäisi BASH:ia - joten sen tiestä tehdään kivikkoinen.

Varsinkin kun aloittaa skriptaamisen kannattaisi käyttää virtuoosien kirjastoja - kaikki siitä hyötyisivät, nuo virtuoosit itsekin. Sillä jos virtuoosien tekemiä funktioita ei käytetä niin käy aivan niinkuin nyt on käynyt: BASH kituu hengiltä. En tiedä kuinka yleisiä yksityiset kirjastot ovat mutta se on kyllä varmaa että niitä on.

Koska aloittelijalla ei ole kirjastoja käytettävissään niin joutuu kirjoittamaan erittäin paljon ja kokeilemaan mahdottomasti - sillä BASH:in logiikkaa ei hallitse kukaan ihan täysin vaan kaikki on kokeiltava tyyliin: onnistuiskos näin? Ja kun onnistuu niin: mitä vikaa tässä on?

Onhan niitä yksittäisiä funktioita netissäkin monessa paikassa - mutta kaikkien logiikka on hieman erilainen ja niiden yhteen sovittaminen on varsin työlästä.

Joten kun skriptinsä saa toimimaan siitä kannattaa tehdä siitä kirjastoonsa funktio jottei joutuisi samaa toimintoa kehittämään monesti uudestaan - ja uudestaan keksiessä tuska on paha sillä hukkaa aikaansa sellaiseen jonka on jo kerran tehnyt eikä tuloskaan ole silti aina hyvä.

***

Funktiot tuovat melkein aina lisää nopeutta ja niitä käyttämällä oppii paljon nopeammin tekemään vakaita skriptejä. Teoriassahan funktion käyttäminen vain hidastaa, mutta
koska funktiot ovat yleensä jonkun virtuoosin tekemiä ovat ne myös nopeita ja  virheettömämpiä.

Funktiolla voi olla vain ne ominaisuudet jotka saa revittyä irti BASH:in omista käskystä - ja siitä voit olla varma että moni muu saa käskyistä irti paljon enemmän kuin sinä - tämä koskee ihan meitä kaikkia, mikään ei ole koskaan lopullista vaan jopa kaukaa historiastakin tulee yllätyksiä - useinkin muuten.

On erittäin vaikeaa tehdä BASH:iin C-kielisiä ohjelmia - mutta mahdollista se kyllä on. Sensijaan Pythonin ja Perlin käyttäminen ongelmissa on helppoa.

Ennenkuin olet koonnut itsellesi taidon tehdä omia funktioita voi käyttää virtuoosien tekemiä funktioita - tekijänoikeuksia ei kenelläkään ole joten ota irti mitä saat mutta jaa myös yhteisölle mitä itse kehität. Virtuoosien funktiot ovat yleensä nopeampia kuin mitä itse saat kasattua mutta ennenkaikkea ne eivät kompastu BASH:in omituisuuksiin.

Funktioita kuvaavat skriptit talletetaan kovalevylle - yleensä useita samaan aihepiiriin kuuluvia funktioita samaan tiedostoon. Yhdessä tiedostossa voi olla vaikka kuinkamonta funktiota. Funktion koodin voi kopioida skriptinsä alkuun tai lukea sen muistiin joko senhetkiseen pääteistuntoon tai peräti liittää päätteeseen jokakerran kun pääte avataan laittamalla funktion tai kirjaston liitoskäsky tiedostoon ~/.bashrc .

Ennen käyttämistä kaikki yhden tiedoston funktiot luetaan muistiin käskyllä joka on muotoa:
. sen_kovalevytiedoston_nimi_polkuineen_jossa_funktiot_ovat
huomioi piste alussa. Kaikki tiedostossa olevat funktiot kopioidaan muistiin jonka jälkeen funktioihin voi viitata samoin kuin niiden ollessa kirjoitettu skriptiin. Niinpä ei täydy jokakerran funktiota tarvitessaan tehdä samoille asioille uutta koodia - hölmöillen jokaisella tekokerralla hieman eritavalla.
- muuten kirjaston jokaisen skripin tulee olla käännöskelpoinen, joten kirjastoa kasattaessa on viisainta lisätä sinne funktioita yksi kerrallaan.

Koska muisti on halpaa kannattaa muistiin lukea  funktioita runsaaasti - sillä vaikka niitä olisi paljonkin niin ei se toiminnan nopeuteen juuri vaikuta.

Tosin suuresta funktioiden määrästä voi seurata inhottavuuksiakin - samalla nimellä muistiin ladatuista vain viimeinen on voimassa - joten kun joku funktio ei toimi niinkuin pitäisi niin saattaa korjata väärää funktiota ja repiä hiukset päästään kun  mikään ei auta. Tästä tulee muuten se hyöty että voit tehdä funktion esimerkiksi nimellä ls jolloin se korvaa BASH:in oman ls-käskyn.

BASH:in omatkin käskyt ovat funktioita - mutta ne on yleensä kirjoitettu C-kielellä ja niissä on omat sisäiset matriisinsa, looppinsa ja vaikka mitä joten jos niitä osaa hyödyntää niin saa käyttöönsä todella nopeita toimintoja. Mutta parhaat noista  C-kielisistä ohjelmista on tehty tietokoneen hoitamiseen joten sellaiselle jota ei tietokoneen hoitaminen kiinnosta on BASH paljon huonompi.
« Viimeksi muokattu: 08.03.26 - klo:07.10 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #342 : 03.02.26 - klo:17.42 »
Seuraavaksi keskityn jakolaskuun. Sen varsinainen laskurutiini on hyvin yksinkertainen - joten tottakai se paranisi matemaatikon käsittelyssä. Ihan raakile jakolasku vielä on ja kaipaa paljon työstämistä.

Nyt alkaa tarkentua syy miksi BASH:ia on mollattu sillä tämä jakolaskukin on nopeampi kuin bc, awk ja semmoiset. Sillä eihän noilla mitään tee jos BASH itse on parempi. Kuvottava teko se mollaaminen aikanaan oli - mutta nykyään raadoksi mollatun BASH:in mollaaminen edelleen on vain typeryyttä.

Silti tulosta verrataan tässä bc:n tulokseen sillä onhan se bc:n tulos sentään varmasti oikein - ja bc muuten toimii muuten niinkuin lasku-ohjelman pitääkin - mitä muut eivät yleensaä osaa.

Eihän tämmöisillä enää käytännön merkitystä ole mutta onhan se kiva lisämauste kun voi haistattaa Pythonille muutamissa pikkuhommissa - ja kuka tietää jos joskus monessa merkittävässäkin tehtävässä.

Nimittäin kaikki mitä olen kokeillut - anonyymeistä funktioista alkaen - on toiminut erittäin nopeasti kun ottaa huomioon että BASH:in toimimisnopeus alkaa 0.5 ms:sta. Täytyy kyllä myöntää että BASH:issa on paljon huonoakin kummallisen logiikkansa lisäksi - eihän sitä ole paljoakaan kehitetty - muuten taitaa olla todella hyvä ettei olekaan kehitetty sillä koska ei ole tajuttu mitään niin luultavasti olisi 'kehitetty' se vainaaksi.

Skripti tulostaa noin 36 desimaalia jos ei lasku-käskyssä määrää tarkkuutta - tarkkuuden voi määrätätä rajoittamattomaksi mutta eihän se vielä toimi aina virhettömästi. Esimerkiksi:
jaa 1 3
jaa 1 3 360000    # 360000 desimaalin laskeminen kestää noin 15 sekuntia.

- paljon siinä on vielä hiottavaa - esimerkiksi tuo case-looppi on toistaiseksi ihan kummallinen.
Koodia: [Valitse]

function jaa () {
desimaaliluku=36; [[ $3 ]] && desimaaliluku=$3
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia.  Vaihtoehdot:tulosta=echo ja tulosta=:

[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."

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

apu=$((${#int2}-${#int1})); $tulosta $apu  # kokonaisten lukumäärä? tämä on kelvoton
etunollia=''
case $apu in
-{1..18} ) etunollia='';; # desimaaleja=$((-1*$apu+1 )) }  ;;
0)  etunollia='';; #desimaaleja=0 ;; # apu=85; printf "%${apu}s" | tr " " 0
1)  etunollia='' ;;
2)  etunollia=0 ;;
3)  etunollia=00 ;;
4)  etunollia=000 ;;
5)  etunollia=0000 ;;
6)  etunollia=00000 ;;
7)  etunollia=000000 ;;
8)  etunollia=0000000 ;;
9)  etunollia=00000000 ;;
10) etunollia=000000000 ;;
11) etunollia=0000000000 ;;
12) etunollia=00000000000 ;;
13) etunollia=000000000000 ;;
*) desimaaleja=$((-1*$apu-1 )) ;;
esac
$tulosta etunollia:$etunollia'  desimaaleja:'$desimaaleja

luku1=$int1$desimaaliosa1
luku2=$int2$desimaaliosa2

$tulosta #luvut ilman desimaalipistettä:  ";$luku1' '$luku2

tulos='' # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
until [ ${#tulos} -gt $desimaaliluku ] ; do # muodostetaan tulos-palasia n merkkiä kerrallaan  #

apu=$(($luku1/$luku2)); [[ $apu -eq 0 ]] && apu=''; [[ ${apu:0:1} -eq 9 && ${tulos: -1} -eq 9  ]] && apu="0"$apu; tulos=$tulos${apu} ; $tulosta a$luku1' '$luku2' '$apu
luku1=$(($luku1%$luku2))'000000000000000000000000'; luku1=${luku1:0:18} ; $tulosta z$luku1
done

$tulosta "oikea tulos 140 desimaalilla esitetynä on päällä ja alla tulos tästä laskusta:"

[[ $etunollia ]] && echo .$etunollia${tulos:0} || echo ${tulos:0:$desimaaleja}.${tulos:$desimaaleja} ;}


# esimerkkilaskuja - kuten huomaat niin paljon on ihan keskeneräistä:
a=75.1234567 b=1233457; echo $(bc<<<"scale=140; $a/$b" | tr -d '\\\n')'  tämä rivi on bc:stä'; echo; time jaa $a $b 168; echo; echo
a=1233457890123.23; b=.92345678999999; echo $(bc<<<"scale=140; $a/$b" | tr -d '\\\n  tämä rivi on bc:stä'); echo; time jaa $a $b 170; echo; echo
a=1233457890123.23 b=.1234567; echo $(bc<<<"scale=140; $a/$b" | tr -d '\\\n  tämä rivi on bc:stä'); echo; time jaa $a $b 170; echo; echo
a=.1234567 1233457890123.23; echo $(bc<<<"scale=140; $a/$b" | tr -d '\\\n  tämä rivi on bc:stä'); echo; time jaa $a $b 170; echo; echo


petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #343 : 05.02.26 - klo:11.16 »
Yksi uusi käsky on hitaampi kuin vanhoista käskyistä kasatut isot skriptit. Uudet käskyt ovat niin tautisen hitaita ettei ole mielekästä niillä numeroita käsitellä. Ja sopii ihmetellä niitä yleisesti hyväksyttyjä harhaluuloja BASH:in kyvyttömyydestä joita vielä opetetaankin edelleen - sillä tosiasiassa BASH kykenee vanhoilla käskyillään mihin hyvänsä. 
 
Kehittämisen arvoisia näillä vanhoilla käskyillä toteutetut skriptit tosiaan ovat sillä skriptien rivimäärän kasvaessa suoritus-aika kasvaa hyvin hitaasti - voi tarvittaessa lisätä ehtoja ja looppeja melko suruttomasti - mutta vaatimus on ettei uusia käskyjä saa skriptissä olla yhtään - tai onhan uusissakin käskyissä nopeita. Eihän lopputulos sittenkään nopea ole mutta tästä elämästä kerkiää hautaan hitaamminkin - sillä eihän näillä rahaa tehdä vaan ajankuluahan nämä vain ovat. Ja saa samalla haukkua BASH:in opettajia.

---

Edellä esitetyn jako-skriptin epäilyttävin kohta on siinä tehtävässä suuruusluokan määrityksessä ( esitetyssä skriptissä se oli käskyrivi: apu=$((${#int2}-${#int1})) ).

Tosin toistaiseksi paranneltu jako-skripti toimii vain alle 18-numeroisille kokonaisluvuille ja vielä pienemmille desimaaliluvuille. Eihän tämä vielä moitteton ole mutta yritetään sentään:
Koodia: [Valitse]


function suuruusluokka () { # kuinka monta numeroa tuloksen kokonais-osassa tulee olemaan - tai etu-nollaa.
# set -x; trap "echo paina: return;read x" DEBUG # skriptin suoritus käsky kerrallaan
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia.  Vaihtoehdot:tulosta=echo ja tulosta=:

[[ $1 = ${1/./} && $2 = ${2/./} ]] && { apu=$(($1/$2)); echo ${#apu}; return ;}    # jos kummatkin ovat kokonaislukuja 

[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"; $tulosta luku1:$luku1
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"; $tulosta luku2:$luku2

merkki=''; koko1=${luku1%%.*}; koko2=${luku2%%.*}; desi1=${luku1##*.}; desi2=${luku2##*.}
(( $koko1 < $koko2 )) && { apu=$koko1; koko1=$koko2; koko2=$apu; apu=$desi1; desi1=$desi2; desi2=$apu; merkki=- ;}
#[[ $koko1 = $koko2 ]] && [[ $desi1 > $desi2 ]] && { apu=$koko1; koko1=$koko2; koko2=$apu; apu=$desi1; desi1=$desi2; desi2=$apu; merkki=- ;}



# desimaaliosien tulee olla yhtäpitkät. Lyhyemmän perään kirjoitetaan nollia sillä desimaaliosan lopussa ne eivät muuta arvoa.
(( ${#desi1} >= ${#desi2} )) && desipituus=${#desi1} || desipituus=${#desi2}
desi1=$desi1'00000000000000000000'
desi2=$desi2'00000000000000000000'
desi1=${desi1:0:$desipituus}; $tulosta desi1:$desi1
desi2=${desi2:0:$desipituus}; $tulosta desi2:$desi2

luku1=$koko1$desi1; luku1=${luku1:0:18}; $tulosta luku1:$luku1
luku2=$koko2$desi2; luku2=${luku2:0:18}; $tulosta luku2:$luku2

(( 10#$luku1 > 10#$luku2 )) && kokonaisia=$((10#$luku1/10#$luku2)) || kokonaisia=$((10#$luku2/10#$luku1)) ; echo  $merkki${#kokonaisia} ;
}
 
suuruusluokka 19.99999999999999 2
suuruusluokka 20.0 2
suuruusluokka 77.9 0.00001

suuruusluokka 2 19.99999999999999
suuruusluokka 2 20.00000000000000
suuruusluokka 0.00001 77.9

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #344 : 16.02.26 - klo:12.08 »
Edellinen on hyvä esimerkki siitä kuinka päästään tilanteeseen jossa saa ikuisesti tehdä toinen toistaan vaikeampia korjauksia - sillä alkuperäinen syy on ihan muualla:

Kyse oli siis siitä että jako-laskuja vaivaa 'katoavien nollien syndrooma´ - tuloksesta tipahtaa siellä-täällä nolla pois vaikka kaikki muut numerot ovat aina oikein. Se taas johtuu siitä että jakojäännöstä ei käsitellä oikein: otetaanpa esimerkiksi lasku 1001%100. Aivan varmasti jokainen sanoo että yksihän se ilmanmuuta on. Katsotaanpa - kirjoita tavalliseen kalkulaattoriin 1001/100 ja laskun jäkeen se tuloksessa pisteen jälkeen tuleva on se jakojäännös. Mikä on 01. Matemaattisessa mielessä 01 on sama asia kuin 1 ja matematiikka jättääkin tulostuksestaan aina etu-nollat pois - olettaen että ne ovat turhia ja hankaloittavatkin joskus.

- BASH:issa asioita hankaloittaa se vielä lisää että jos luvun edessä on 0 tajutaan se oktaaliluvuksi ja laskusta tulee omituisia tuloksia ja joskus virhekin. Koska sillekin on tehty estotapa on varmaa että nämä etu-nollat ovat kiusanneet aikoinaan paljonkin - joten  tämänkaltaisia desimaalisia jakolaskuja ovat aikoinaan jotkut vamasti tehneet sillä ei näitä muuten eteen tule.

- yksi keino jota BASH kykenee käyttämää tuon etu-nollan löytämiseksi on sittenkin yksinkertainen - käske : echo $((10*1001/100)) - vastaukseksi tulee 100 eikä 10. Mutta itse asiassa se merkitsee että: 1001/100 jakojäännös on 01 eikä 1. Koodina tämmöinen on:
Koodia: [Valitse]
jaettava=111453800000000000; jakaja=1233457; tulos=$(($jaettava/$jakaja)); apu2=$((10*$jaettava/$jakaja)); [[ ${apu2: -1} -eq 0 ]] && tulos=${apu2: -1}$tulos; echo $tulos

- lisäys korjasikin ilmeisesti kaiken näissä helpoissa tapauksissa. Pitää kuitenkin jatkaa harjoituksia sillä ei tämä vielä valmis ole - eikä tavallaan voi koskaan valmistukaan - vaikeus alkaa siitä että BASH:in matikka tuntee vain alle 18 numeroiset luvut joten vaikka uutta löytyisikin niin kovin hyvää tästä ei saa - toimintakin menee yhtä hitaaksi kuin matematiikka-ohjelmilla. Mutta onhan se kiva pystyä ratkomaan parissa millisekunnissa jakolaskuja joiden tulos on:
.00006090480389669035888563606189757729697914074021226520259725308624459547434568047366061403032290546002008987747444783239302221317808403535
.00006090480389669035888563606189757729697914074021226520259725308624459547434568047366061403032290546002008987747444783239302221317808403535  sama lasku bc:llä

- lukujen etumerkitkin pitäisi ottaa laskuissa huomioon. Mutta sen voi kopioida kertolaskusta. Enpä jaksa nyt.

- muuten tässäkin tehtävässä toimisi kaksois-tarkkuus mutta kaavat ovat niin vaikeat etten minä osaa niistä koodia tehdä. Nopeus kylläkin säilyisi.

- mihin tässä BASH:issa katseensa kääntääkin niin tuntuu siltä että moni muukin on aikoinaan tehnyt samaa sillä aina löytyy joku omituinen käsky joka ratkaisee kaiken - niin omituinen ettei sillä muuta käyttöä voi olla. Siis aina on tiedetty että esimerkiksi BASH on tehokas numeron-murskain - aikoinaan liian tehokas taviksien käsiin.
« Viimeksi muokattu: 17.02.26 - klo:02.19 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #345 : 18.02.26 - klo:02.04 »
Toimintansa alussa kaikki skriptit kutsuvat tulkkia ja myöhemminkin aina tarvittaessa ja aikaahan jokainen kutsu vie. Tulkkaukseenkin kuluu aikaa, mutta koska vanhat komennot kuuluvat tulkkiin on tulkkaaminen niille muutenkin nopeampaa ja nopeutuu vielä enemmän jos skriptin kaikki komennot ovat niitä vanhoja sillä ne tulkataan silloin kaikki kerralla - uusien komentojen hitaus taitaa johtua siitä että ne tulkataan yksitellen.

BASH:in vanhat komennot ovat yksinäänkin yli kymmenen kertaa nopeampia ja ryhmässä yli sata kertaa nopeampia kuin uudet komennot joten uudella käskykannalla ei matematiikan toimimista ole pystynyt kehittämään - sillä vaikka desimaali-laskujen periaatteet ovat yksinkertaisia niin toimivan koodin kehittäminen vaatii miljoonia kokeita ja jos yksi koe kestääkin satakertaa kauemmin venyisi kehitysaika kuukausista vuosikymmeneen.
- miljoonia kokeita siksi että vielä siinävaiheessa uskoni virtuooseihin oli vankkumaton ja vain hitaasti pystyin kokeilemaan jotakin jota he väittivät mahdottomaksi.
- nyt skriptit olisivatkin yksinkertaisia kirjoittaa sillä havaittuani että myös nimi-parametrit toimivat automaattisine parametrin palautuksineen ja paljon muutakin sellaista toimii joita virtuoosit väittävät mahdottomaksi. Usko BASH-virtuoosien puheisiin on nykyään negatiivinen - ja virtuoosien on turha urputtaa vastaan sillä skriptini toimivat.
- kyllä uusillakin käskyillä desimaalimatematiikan saisi toimimaan sillä toiminnallisuus on jokseenkin sama - tosin uusilla käskyillä paljon-paljon hitaammin ja hankalammin.

Ilmeisesti ihan alussa BASH:in luomisen jälkeen joku BASH:in iso-kenkäinen huomasi desimaali-laskujen toimivan nopeasti kunhan rutiinit tekee sitävarten - ja koska BASH:ista haluttiin eroon jo silloin ja kyvykkäästä BASH:ista eroon pääseminen olisi mahdotonta niin tehtiin uusi käskykanta joka oli miellyttävämmän näköinen ja toimi tiedostonkäsittelyssä vielä kelvollisesti mutta koska matematiikkassa jokaiseen toimeen kuuluu vähintään kymmeniä käskyjä tulisi siitä sika-hidas - joten kukaan ei edes yrittänyt suorittaa desimaalilaskuja - tai tosiasiassa monikin on yrittänyt ja epä-onnistunut surkeasti joten vuosikymmenien kuluessa kehittyi luulo ettei BASH desimaali matematiikkaan kykene.

Kaikki pienet perus-laskutoimitukset:  + - * / ja '8-numeroinen neliöjuuri'
toimivat BASH:in vanhoilla komennoilla nopeammin kuin mikään matematiikka-ohjelma jonka voit liittää BASH:iin - sillä BASH alkaa laskemisen heti ja on valmis jo ennenkuin toiset voivat aloittaa.

Eihän laskuilla sittenkään käytännön toimia tee mutta protoyyppien tekemiseen BASH:illa ne ovat loistavan hyviä.

Nyt viimeisenä desimaali-laskuista valmistui jakolasku - jakolaskua oli vaikea saada toimimaan oikein, sillä jakolaskun tuloksesta tipahti siellä-täällä nolla pois. Syy oli yksinkertainen kun se vuoden etsimisen jälkeen löytyi - jakojäännös operaattori % toimii matemaattisessa mielessä aivan oikein mutta oikeaa tulosta se ei anna  - tämän kun otti huomioon niin jakolasku alkoi toimimaan aina oikein.

- siis desimaalinen 36 numeroinen perus-laskenta toimii ajassa millisekunti ja kymmenessä millisekunnssa saa laskettua vaikka mitä.

- uusilla komennoilla desimaalilaskut olisivat todella hitaat - ne saisi kyllä toimimaan nyt kun menetelmät ovat selvillä mutta aikaisemminhan toimintaa on ollut mahdotonta kehittää.
« Viimeksi muokattu: 18.02.26 - klo:15.08 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #346 : 19.02.26 - klo:19.57 »
Aloin jo kauan sitten selvittää syytä miksi väitetään ettei BASH desimaalilaskuihin kykene - ja paljastui että väitetty syy on sama kuin miksi hiihtokilpailuissa hävitään: voitelu oli väärää - tai siis BASH:in suhteen sanottiin että eihän BASH:issa edes ole desimaali-tyyppiä eikä sen matematiikka-moottorikaan desimaalipilkkua hyväksy. Ei kummallakaan ole desimaalilaskujen kanssa muuta tekemistä kuin että ne yleensä helpottaa ja joskus romahduttaa - mutta täyyhän ne silti kumota:

1. desimaali-tyyppimäärittely on suurten lukujen suhteen rajoittava tekijä eikä siitä tyyppimäärityksestä ole mitään iloa. Esimerkiksi luku:
12345678901234567890123456789012345678901234567890123456789012345678901234567890.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901. Onko se desimaaliluku? Mahtuuko se johonkin? Sitäpaitsi BASH tuntee lukujärjestelmät joiden kantaluku on välillä 2-60 joten merkeissä löytyy - joten oikeastaan ne olisivat tekstijonoja koska kaikki aakkosetkin kelpaavat. Siinäpä muuten salasana: $((60#petteriIII)) elikä kun sen kirjoittaa päätteeseen saa: 254376408104369084: command not found

2. ei semmoista asiaa olekaan kun desimaalilaskin sillä kaikki laskimet ovat kokonaislukulaskimia - sillä numerot tulevat laskuissa oikein kun desimaali pisteen poistaa. Siinä desimaali-laskimeksi kutsutussa on lisäosa pilkun paikan määrittämiseen. BASH:issa siis joutuu töihin kaksi kertaa - mutta koska kumpikin kerta on nopeudeltaan satakertainen uusilla käskyillä suoritettuihin verrattuna niin nopea se lopputuloskin vielä on.

Esitetäänpä niiden desimaali-laskujen nopeuksia:

- vähänumeroiset laskut ovat niin nopeita ettei oikeastan voi sanoa minkä nopeutta mitataan - linuxin, tulkkin vaiko skriptin.
- sillä Linux myöntää todella pieniä toiminta-aikoja kaikille toimille - ja jotkut toimethan täytyy suorittaa aivan ehdottomasti ja loput hommat laitetaan tärkeysjärjestykseen - kaikille annetaan toiminta-aikoja mutta toisille useammin ja esimerkiksi BASH:ille tosiharvoin. Tämä paitsi hidastaa niin tekee myös toimimisen nopeudesta epämääräistä. BASH-skriptien toiminta-ajat vaihtelevatkin liian paljon vaikka ottaisi väli-muistienkin toiminnan huomioon. Siitä päätellen niitä on hidastettu tälläkin tavoin vähintään kymmenkertaisesti ja ehkäpä paljon enemmänkin. Sillä BASH:istahan halutaan päästä eroon.

- 36 numeroinen liuku-luvun kertolasku - siinä on 30 käskyä ja aikaa kuluu millisekunti joten yhden käskyn suoritus kestää 33 mikrosekuntia. 
- jakolasku on omituinen - esimerkiksi: 75.1234567 / 1233457 = 
.00006090480389669035888563606189757729697914074021226520259725308624459547434568047366061403032290546002008987747444783239302221317808403535753577
sen satakunta käskyä se käy läpi millisekunnissa joten yhden käskyn suoritusaika on 10 mikrosekuntia. Tulos on tarkka numerolleen. Skripti on rekursiivinen ilman rekursiota - siinä se omituisuus.

- sitten potenssiin korottaminen - siis laskujen tyyppiä: 10.987654321098 ^ 10 - siinä on yli 300 käskyä ja aikaa kuluu 3 millisekuntia elikä yhden käskyn nopeus on parempi kuin 10 mikrosekuntia.

- sitten logaritmointi - siis laskujen tyyppiä: ota briggsin logaritmi luvusta: 17.35178533715 . Käskyrivejä tulee 1965 ja nopeudella 12ms tulee käskyä kohti noin 6 mikrosekuntia.

- kelvottoman huonoja aikoja muille kielille mutta BASH:ille ajat ovat erinomaisia sillä pienet perus-laskut ovat nopeampia mitä matematiikka-ohjelmilla saisi - ja tulevaisuudestahan ei tiedä - vaikkka jotain vielä mullistavampaa löytyisi.
- näin alussa on ehdottoman tärkeää voida todeta tuleeko laskuista oikea tulos. Siksi noissa laskuissani esitetään aina myös bc:n tulos välittömästi edellisellä rivillä tulosten desimalipisteet samalla kohtaa jotta vertaileminen olisi nopeaa ja varmaa - sillä bc:n tulos on varmasti oikea ja lisäksi se esittää tulokset oikealla tavalla. Miksei siis käytetä bc:tä kokoajan? Koska pienissä perus-laskuissa se jää toiseksi ja muut laskut ovat vain leikkimistä numeroilla.
- senkin takia noita lasku-rutiineja kannattaa tehdä koska opittuaan niiden toiminnan voi samoilla menetelmillä määritellä muitakin aikaisemmin mahdottomiksi luultuja toimia.

- alkeellisimmista toteutuksista saa toistaiseksi vain vajaat 18 oikeaa numeroa, kaksoistarkkuudestan 36 numroa, nelois-tarkkuudesta 72 numeroa .... ainakin kaksoistarkkuus on melkein yhtänopeaa kuin tavallinenkin ja eiköhän ne muutkin ole melkein yhtä nopeita - mutta skriptien tekeminen niihin olisi niin vaivalloinen ja puuduttava tehtävä etten enää pysty semmoisiin tehtäviin - vaikka periaate on sama kuin ennenkin ja onnistuminen varmaa.
- ohjelmalliset toteutukset häviävät kyllä rautalangasta tehdyille tuhatkertaisesti ilmanmuuta - mutta BASH:in tarkoitus on vain tehdä mahdolliseksi tehdä kohtuullisessa ajassa toimivia prototyyppejä eikä toimia käytännössä.
- ne alkuperäisetkin komennot oli tehty tekstinkäsittelyyn eikä niillä numeron-murskausta noinvaan pysty tekemään. En tiedä varmasti mutta todennäköiseltä tuntuu että monikin on noita vanhoja komentoja desimaalilaskuihin käyttänyt. 
- koska myös vanhat komennot ovat tekstinkäsittely-käskyjä nopeutuisi kaikki muukin skriptaaminen niitä käytettäessä.

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #347 : 20.02.26 - klo:11.32 »

Funktioista
===========

Tietokoneen jokainen toiminto määritellään funktiolla - yksittäiset käskytkin ovat funktioita mutta yleisemmin funktio on käskyryhmä. BASH:issa voidaan määritellä uusia funktioita tai määritellä vanhoja funktioita uudestaan - myös kaikkia käskyjä vaikka se ei olekaan viisasta - vaikka määritellä sama funktio uudestaan kerran päivässä maailman tappiin asti.

Kaikki ohjelmointikielet tarvitsevat funktioita ja ne ovatkin yksi BASH:inkin tukijaloista. Koska BASH halutaan tuhota vietiin siltä kirjasto-osoitin. Kirjastoja ei voida estää käyttämästä mutta kirjasto-osoittimen poistaminen hankaloitti käyttöä. Mustamaalaus hoiti loput.

Funktio on määriteltävä ennenkuin siihen viittaa - BASH:in omista käskystä useimmat ovat tiedostoja kovalevyllä ja pieni osa on määritelty tulkin yhteydessä. Ensin dr.Jekyll teki joitain eriomaisia käskyjä jollaisiin jokainen pyrkii vaikkei voikaan saavuttaa - mutta sitten tuli mr.Hyde ja pilasi koko homman ihan totaalisesti - lopuista käskyistä tuli hitaita ja kyvyttömiä.

Funktiota tehdessä se koodataan ensi normaali skriptri ja muutetaan sitten se funktiomuotoon - omat funktiot talletetaan myös sinne kovalevylle, mutta niillä on yksi erityispiirre; ne voidaan lukea levyltä keskusmuistiin jo ennen skriptiä jolloin ne säilyvät nopeina mikäli ne muodostuvat vanhoista käskyistä - uusilla käskyillä ne eivät ole koskaan nopeita olleetkaan.

Kun funktio on suoritettu niin palataan skriptiin funktiokutsun perään - jos funktiokutsu on ollut keskellä lausetta niin samaan lauseeseen heti kutsun perään. 

Saat määritellyt funktiot näkyviin käskyllä: declare -f. Toki voit käyttääkin niitä jos tahdot: esimerkiksi anna päätteessä käsky: quote hilivinkkeli niin vastaksena tulee: 'hilivinkkeli' - siis sama sana heittomerkkien välissä. Jos teet funktioita koneeseesi niin noiden joukkoon nekin ilmestyvät.

BASH:issa on kymmenissä paikoissa erilaisia asioita käsitteleviä käskyjä tuhansia ja niistä useimmilla monenlaisia toimintoja kymmenittäin joten luotettavan kirjastofunktion tekeminen yksitäisistä käskyistä on BASH:issa toivottoman hidasta koska niin suuresta joukosta on hidasta valita oikeat käskyt tai edes päätellä mikä on paras tapa toimia - ja toimintatapojen nopeuserot ovat usein suuria. Tai semmoinenkin ilmiö on että tuhannesta niistä käskyistä jotka teoriassa saattaisivat skriptissäsi toimia on vain sata sellaista jotka tosiaan toimivat, niistä vain kymmenen on sellaista jotka toimivat hyvin mutta vain yksi tai kaksi sellaista jotka toimivat moitteetta.

BASH:in käskyluvun vätetään olevan noin tuhat. Mutta se on uusien käskyjen luku. Tiedoston käsittelyssä jotkut niistä ovat  ehdottomasti parempia kuin kukaan saa itse aikaiseksi ja niiden ansiosta BASH ei tule kuolemaan. Mutta ehdottomasti suurin osa niistä toimii kyllä hitaassa tiedoston-käsittelyssä melkohyvin mutta ne ovat katastrofi nopeassa tiedonkäsittelyssä.

Funktioiden soisi löytyvän aina - paras säilytyspaikka funktioille on kirjasto.

---

Kirjastoista
============

Kirjasto on sama asia kuin funktiovarasto - mutta voi kirjastossa toiminnallistakin koodia olla - esimerkiksi tuloste: kirjasto sejase liitetään koodiin.

Kirjasto voi olla yksittäinen tiedosto jossa on monia funktioita mutta yleensä kirjasto on kansio jonka monssa on oma tiedosto eri asia-tyypeille.

Kirjaston fuktioissa voi olla myös kutsuja kirjaston toisiin funktioihin - eteenpäin viittauksetkin sallitaan.

Kirjastojen lukumäärä ja skriptien lukumäärä niissä on viite skriptikielen käyttökelpoisuudesta - elikä halutaanko kieltä pitää hengissä.

Koska BASH:ista halutaan eroon niin sen kirjastoja mustamaalataan. BASH:in kirjastoja ei vaivaa mikään muu kuin mustamaalaus. Tai ei niitä enää mustamaalatakaan sillä ne on 'unohdettu' - joten täytyy kertoa kuinka niitä käytetään: kirjasto on ihan normaali tiedosto johon on talletettu funktion muotoon kirjoitettuja skriptejä ja joissa ei tarvita lausetta: #!/bin/bash eikä suorituoikeuttakaan tarvita. Kaikki tuon tiedoston funktiot saa käyttöönsä skriptiä tehdessä kirjoittamalla skriptin alkuun:
. sen_tiedosto_nimi_polkuineen_johon_funktio_on_talletettu
- siis tiedostonimen edessä on piste - sillä haluttiin antaa lyhyt nimi toiminnolle jota käytetään aina - kaikissa muissakin kielissä kirjastot esitetään alussa ja niitähän on ylensä useampiakin. Voi pisteen paikalle kirjoittaa myös sanan source - jolloin toiminta muuttuu aavistuksen verran.
- käytännössä kirjastoja pitäisi olla erityyppisille hommille omansa - ihan niinkuin muissakin kielissä.
- on siinä väitteessä huonosta tietoturvasta merkki perääkin sillä tietokonehan on tietoturvariski tekee sitten mitä vaan - vaikka maalaisi vihreäksi ja heittäisi mereen.

Vakaan skriptin tekeminen vai kielen omilla käskyillä ilman ulkoisten funktiokutsujen apua on tuhoon tuomittu yritys - tuloksesta tulee melkein varmasti hidas ja virheellinen.

Koska annetaan ymmärtää ettei BASH:issa funktiotioita kannata käyttää niin BASH kituu - vaikka onkin täysin selvä että Pythonin harrastajat väittää näin niin pistää tosiaan ihmettelemään minkätakia BASH:in omat "virtuoosit" eivät laita näitä väittäjiä ruotuun.

Funktiot helpottavat skriptitekoa suunnattomasti sillä ne tekevät pitkän toimintosarjan yhdellä käskyllä - ja usein tavalla jota et ole tullut ajatelleeksikaan ja jota yleisesti väitetään mahdottomaksi - ja joskus se tekee tehtävänsä nopeasti ja hyvin.

Hyvät funktiot ovat usein työläitä kirjoittaa, mutta yleensä ne kirjoittaakin joku toinen - ja jos joudut itse kirjoittamaan funktioita niin ne kirjoitetaan vain kerran ja sijoitetaan senjälkeen kirjastoon jonka jälkeen ne ovat aina käytettävissä.

Maailmalla on valmiita ja hyvälaatuisia BASH:ille funktioita noin ziljoona joten voisihan niitä toimittaa alkuasennuksenkin yhteydessä - mutta niin ei tehdä sillä BASH:ista halutaan eroon mutta siihen ei toistaiseksi pystytä - mutta sitä ei missään nimessä haluta että kukaan käyttäisi BASH:ia tosissaan - joten sen tiestä tehdään kivikkoinen.

Varsinkin kun aloittaa skriptaamisen kannattaisi käyttää virtuoosien kirjastoja - kaikki siitä hyötyisivät, nuo virtuoosit itsekin. Sillä jos virtuoosien tekemiä funktioita ei käytetä niin käy aivan niinkuin nyt on käynyt: BASH kituu hengiltä. En tiedä kuinka yleisiä yksityiset kirjastot ovat mutta se on kyllä varmaa että niitä on monta.

Koska aloittelijalla ei ole kirjastoja käytettävissään niin hän joutuu kirjoittamaan erittäin paljon, kokeilemaan mahdottomasti eikä tulos sittenkään ole yleensö kovin hyvä - sillä BASH:in logiikkaa ei hallitse kukaan ihan täysin vaan kaikki on kokeiltava tyyliin: onnistuiskos näin? Ja kun onnistuu niin: mitä vikaa tässä on?

Onhan niitä yksittäisiä funktioita netissäkin monessa paikassa - mutta kaikkien logiikka on hieman erilainen ja niiden yhteen sovittaminen on varsin työlästä. Yksi erinomainen paikka on: commandline.fu .

Joten kun skriptinsä saa toimimaan siitä kannattaa tehdä siitä kirjastoonsa funktio jottei joutuisi samaa toimintoa kehittämään monesti uudestaan - ja uudestaan keksiessä tuska on paha sillä hukkaa aikaansa sellaiseen jonka on jo kerran tehnyt ja tuloskin voi olla huonompi.

Omia käskyjään ja varsinaisia funktioita BASH:issa ei talleteta kirjastoiksi kutsuttuihin paikkoihin mutta eivätpä ne kirjastoista mitenkään eroa.

Voit itse miettiä kirjastojen tarpeellisuutta: kun haluat tehdä jotakin niin haluatko:
1. Ilmoittaa halusi yhdellä sanalla - kirjastosta sanaa vastaava toiminto löytyy varmasti ja toiminto on valmiiksi testattukin.
2. Liittää skriptiisi kymmeniä-satoja-tuhansia lauseita etsittyäsi niitä jostain huitsin nevadasta jos niitä ylipäätään löydätkään. Ja elää sitten sotkun keskellä ja pähkäillä kuinka jatkossa toimitaan sillä eivät nuo lauseet ihan semmoisenaan skriptiin sovi. Ja testata sitten aikaansaannostasi kauan ja elää sittenkin epävarmuudessa siitä että toimiiko skripti aina oikein. Ja tapellen funktion sivuvaikutusten kanssa?

Aikoinaan BASH:illa olikin käytössään kirjasto mutta silloin BASH:illa ei vielä ollutkaan kilpailijoita. Kun BASH:ille alkoi tulla kilpailijoita niin BASH:in omat virtuoosit siirtyivät välitömästi käyttämään niitä ja yrittivät tehdä tulokkaasta uuden skriptikielten kuninkaan - sillä ei käsitetty sitä että BASH:ista on mahdotonta luopua kokonaan joten joudutaan ikuiseen häpeäpaaluun.

Mutta jotta jostain uudesta skriptikielestä saisi skriptikielten kuninkaan täytyy vanha kuningas elikä BASH ensin tuhota.

Minkätahansa tietokone-kielen tuhoamiseksi kirjastojen kieltäminen on ylivertaisen pätevä keino - mutta kirjastot ovat BASH:inkin perusta eikä niitä voi kokonaan kieltää käyttämästä vaan ainoastaan tehdä niiden käyttämisestä hankalaa poistamalla funktioiden tiedostopolkuun viittaava kirjasto-osoitin. Mutta kun lisäksi alettiin mustamaalata BASH:in kirjastoija niin ne harvat jotka kirjastoja olivat käyttäneet eivät enää uskaltaneet kirjastoja käyttää. Ja nopeasti kehittyikin luulo ettei BASH:illa edes ole kirjastoja - vaikka onhan niitä maailmalla vieläkin muutama - ihan käyttökellpoisia lisöksi vaikkakin kovin omituisia.

Mutta ne jotka BASH:ia edelleen kehittävät ja ylläpitävät eivät virtuoosien puheista välitä vaan he käyttävät kirjastoja ihan niinkuin ennenkin.
- esimerkiksi kun mählit skriptinteossa niin koneesi näytölle tulee usein viesti: command not found. Viestin kirjoittaa kirjastossa oleva funktio -> skriptit ovat pääte-ohjelman lapsia ja kun lapsi mählii niin pääte havaitsee sen ja kutsuu kirjastosta vikaselvitykseen funktiota nimeltä command_not_found_handle - joka sitten kirjoittaa näytölle: command not found - ellei muuta ole määrätty.
- itseasiassa on tarkoitus että skriptaaja kirjoittaan tuon: command_not_found_handle funktion uusiksi - sellaiseksi kuin senhetkisen skriptin mahdoliten vikatilanteiden selvittäminen vaatii. Mutta asia on liian monimutkainen virtuoosien käsittää ja niinpä he vain haukkuvat BASH:in vianselvitystä olemattomaksi.
- koneessasi on noin 84 muutakin tällaista myöhemmin määriteltyä kirjastofunktiota BASH:in omiin käyttötarkoituksiin - kehittäjät siis haistattavat huilua virtuooseille.
- funktioita voidaan määritellä ohjelmallisestikin mutta niin tehdään käytännössä hyvin harvoin - joskus kuitenkin: BASH voi siis opettaa itseään.

Ei BASH tuhoutunut niissä virtuoosien meingeissä mutta invalidi siitä kyllä tuli.

Joten virtuooseilla oli ongelma: halusivatko he olla hyvin pieniä kaloja isossa Python-järvessä vaiko isoja vonkaleita pienessä BASH-lammessa? Moni valitsi BASH:in ja alkoi opettaa kyvyttömäksi raatelemansa BASH:ia. Siitä raadosta virtuoosit kyllä puhuu oikein - mutta he kertovat vain alkeita siitä kuinka sinun tulisi toimia eivätkä ollenkaan sitä kuinka jaat tietojasi toisille ja otat tietoja toisilta. Sillä skriptaaminen ei ole yhden miehen juttu vaan kyllä se on aina ryhmätyötä - siis ei niin että skriptissäsi on jostain nyysitty idea vaan siinä täytyy olla useita nyysittyjä ideoita ja lisäksi omasikin - ja lopuksi jaat sriptisi toisillekin jotta he saisivat nyysiä siitä ideoita. Hiellä sinun pitää leipäsi hankkiman - se ei ole rangaistus vaan ohje kuinka saat onnellisen elämän.

- totta vai tarua? Jokatapauksessa väitteissä on paljon perää niinkuin jatkossa selviää.
« Viimeksi muokattu: 09.03.26 - klo:09.58 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #348 : 24.02.26 - klo:18.54 »
Liukulukujen kertolasku on yksinkertainen ja salaman-nopea mikäli tulo on alle 18-numeroinen:
Koodia: [Valitse]
function laske () { des1=${1#*.}; des2=${3#*.}; des3=$des1$des2; des=${#des3}; prod=000000000000000$(( 10#${1/./}$2 10#${3/./})); prod=${prod:0: -$des}.${prod: -$des}; echo ${prod##+(0)} ;}

Mutta se syyllistyy joihinkin virheisiin ja jos sen täytyy alkaa selvitä hankalistakin tilanteista niin siihen tulee paljon lisää - mutta nopeus ei pienene pajoakaan.
- muuten lisäykset kopioidaan jostakin jo tehdystä - sillä kaikissa laskuissa ongelmat ovat samantapaisia. Ja vain harvoin täytyy mitään muuttaa paljoakaan:
Koodia: [Valitse]
function laske () { [[ ${1:0:1} = - || ${3:0:1} = - ]]  && merkki=- || merkki=''; [[ ${1:0:1} = - && ${3:0:1} = - ]]  && merkki=''; luku1=${1//\-/}; luku3=${3//\-/}; [[ ${luku1//[^.]/} ]] && luku1=$luku1 || luku1=$luku1".0"; [[ ${luku3//[^.]/} ]] && luku3=$luku3 || luku3=$luku3".0"; des1=${luku1#*.}; des2=${luku3#*.}; des3=$des1$des2; des=${#des3}; prod=000000000000000$(( 10#${luku1/./}$2 10#${luku3/./})); prod=${prod:0: -$des}.${prod: -$des}; echo $merkki${prod##+(0)} ;}

# kokeillaan toimiiko oikein:
laske 5 \* 5
laske 1.123456789 \* 1.123456789
laske .0000000089012 \* -.0000001123456789   # mikä on numerolleen: -.00000000000000100001135702468 

laske 3.162277 \* 3.162277       # kokeillaan muuttuuko desimaalipisteen paikka oikealla hetkellä
laske 3.162278 \* 3.162279 

- kertomerkin eteen on valitettavasti kirjoitettava keno-viiva.
- ajoituskäsky time ei anna sille tulosta ollenkaan - laskun time tyylinen nopeus kun on luokkaa 0.03ms - ja timehän näyttää vain kokonaiset millisekunnit.
---

Ajoituskäsky: time ei anna sille tulosta ollenkaan - sillä sen 'time' tyylinen nopeus  on 0.016ms. Ajoitus-funktio nano antoi ajaksi 2.2 ms sillä se ilmoittaa paljonko aikaa skripti todellisuudessa kuluttaa - sillä myös time-käskyn real jättää pois ajan joka kuluu tulkin kutsumiseen ja tulkkaukseen - ja siihen kuluva aika on aina vähintään luokkaa 2.1ms - ja jokainen skripti tulkataan ainakin kerran. 

Nano:n voi määrätä loopille. Tulos on kyllä silloinkin oikea mutta varsinaisesti looppaava-nano on tarkoitettu suoritusajan keskiarvon määrittämiseen - silloin otetaan huomioon BASH:in välimusitit. Suurilla loopauksilla nano osoittaa myös sen että yksinomaan vanhoilla käskyilä tehdyt skriptit tulkataan koko joukko kerralla mikäli yksikään uusi käsky ei heitä jo tulkattua roskikseen.
- siis pelkästään vahoilla käskyillä tehdyistä skripteistä on jossakin käännetty ja salaman-nopea versio ja sen voisi helposti tallettaa myöhempää käyttöä varten - mutta lisenssi-laki varmaan estää sen käytttämisen. 
 
Selvästi ilmoitettuna: vanhoja käskyjä voi suorittaa luokkaa 10.000 eikä aika sittenkään välttämättä nouse edes sekuntiin - mutta uusilla käskyillä aikaa menisi vähintään minuutteja.

Suorituskertoja       aika
      1                     2.2
      10                   2.4
      100                 2.6
      10000          165.706  - siis itse kertoja-skripti kestää noin 0.016ms ja loput tulee tulkista.
     
Koska nykyään skriptit eivät ole isoja ei tätä välttämättä edes huomaa - mutta jos teet skriptisi avulla protyypin jonkin laitteen ohjaamiseen niin silloin se täytyy huomioida.
       
Vielä 30 vuotta sitten kirjoitetiin tuhansien rivien pituisia skriptejä mutta koska ne kestivät liian kauan sillä niissä oli poikkeuksetta yksinomaan uusia komentoja - eikä parempia toimintojakaan tajuttu niillä tehdä - niin BASH-skriptejä lakattiin tekemästä ja koko BASH:ista melkein luovuttiin. Eivät kaikki uudet komennot silti kelvottomia ole - tai ehkäpä en ole kaikille komennoille vastaavia vanhoja vielä löytänyt - sillä useimmat uudetkin käskyt ovat tekstinkäsittely-käskyjä joten ihan samaan hommaan ne on tehty.

---

Toimintanopeuksien määrittäminen tuntuu olevan niin vaikeaa että palaan sille vanhalle käskyn nopeus linjalle ja laitoin skriptiin looppiin ja mittasin time-käskyllä - ja jos looppi vääristää ajan niin se voi vain liioitella - tässä tapauksessa ilmeisesti noin 10% :
 
time for (( n=1; n<=1000; n++ )); do laske 1.123456789 \* 1.123456789; done

siitä suoritusajaksi tuli 20ms ja siitä yhden kertolaskun ajaksi noin 0.02 ja käskyn ajaksi noin 0.002 ms. Siis varsin hyvin sama tulos kuin toisellakin tavalla - helpommin vaan.
« Viimeksi muokattu: 28.02.26 - klo:07.08 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #349 : 27.02.26 - klo:11.47 »
Kun luvuista tekee exponentti-esityksen on mahdollista suorittaa kertominen riippumatta lukujen arvosta - kunhan ottaa laskuissa huomioon eksponentit. Eksponentti-esitykseen muutamisestakin kannattaa tehdä vanhoja käskyjä käyttävä versio sillä se on paljon nopeampi. Ja sen tulee käyttää sitä mitä muissa kielissä kutsutaan nimellä: parametrien palauttaminen.

Toiminta on muuntamisessa täysin erilainem riippuen siitä millaisen luvun se saa.

1. 0.0000123 = ennen ensimmästä välillä 1-9 olevaa numeroa olevat ja siitä pilkun jälkeen olevien nollien ja muiden numeroiden lukumäärä on negatiivinen eksponentti ja pilkun voi sijoittaa tulosta kirjoitettaessa heti ensimmäisen numeron perään.

2. 123000000 = eksponentti on numeroiden lukumäärä -1 ja pilkun voi sijoittaa tulosta kirjoitetaesa heti ensimmäisen nunmeron perään
 
kumpi tapa on kyseessä päätellään siitä alkaako luku nollalla, desimaali-pisteellä vaiko nuemrolla väliltä 1-9. Tehdäänpä esimerkki:

Koodia: [Valitse]
function to_exp () { apu=$(declare -p $1); apu=${apu#*=}; apu=${apu//\"/}; apu=${apu/\+/}; apu=${apu#0}; merkki=''; [[ ${apu:0:1} = - ]]  && { merkki=- ; apu=${apu:1} ;}; [[ ${apu:0:1} = 0 || ${apu:0:1} = . ]] && { apu=${apu%%+(0)};exp=${apu%%[1-9]*}; exp=${exp#*.}; exp=$((-1*${#exp}-1)); apu=${apu##+(0)}; apu=${apu#.}; export $1=$merkki${apu:0:1}.${apu:1}e$exp $1 ;} || { apu2=${apu%.*}; exp=$((${#apu2}-1)); apu2=${apu/./}; apu=${apu#.}; export $1=$merkki${apu2:0:1}.${apu2:1}e$exp ;} ;}

# tarkistuksia - leikkaa-liimaa kaikki lauseet kerralla päätteeseen.

kultapuppeli=0.0000123; to_exp kultapuppeli; echo $kultapuppeli   
kultapuppeli=-.000012300; to_exp kultapuppeli; echo $kultapuppeli
kultapuppeli=-.000012300; to_exp kultapuppeli; echo $kultapuppeli
kultapuppeli=.12300; to_exp kultapuppeli; echo $kultapuppeli

halinalle=12300057; to_exp halinalle; echo $halinalle
halinalle=-12.30005700; to_exp halinalle; echo $halinalle
halinalle=.30005700; to_exp halinalle; echo $halinalle
halinalle=+12.30005700; to_exp halinalle; echo $halinalle
halinalle=012.30005700; to_exp halinalle; echo $halinalle

- BASH siis opettaa skriptaamaan - mutta useimmillehan se ei merkitse mitään sillä mitäänhän ei enää tehdä itse - ja koeta sitä aitoa iloa jonka onnistuminen aiheuttaa.
- mutta itseasiassa BASH ennenkaikkea opettaa mistä niitä matoja kannattaa etsiä kaupallisista tuotteista - elikä kuinka varmistaa laatu.
- funktion ja pääohjelman muistit ovat siis yhteiset ja kummassavaan muuttujan asettaminen kajahtaa toisen saman nimiseen muuttujaan. Mutta käskyllä : apu=$(declare -p $1) muuttujan apu nimi säilyy ja se saa vain ne arvot jotka muuttujalla $1 on. Jos siis apu-nimisen muuttujan arvoja muutellaan funktiossa niin se muuttaa apu-nimisen muuttujan arvoja pääohjelmassa. Jos funktiossa aseteltaisiin muuttuja $1 niin arvot kyllä palautuisivat oikeaan paikkaa muutta käsky $1= ei toimi, vaan se vaatii omaa ojelmaa: joko read<<< , let tai export. Koska näin monet ohjelmat toimivat niin kyllä siitäkin voi päätellä että aina on tiedetty nimi-parametrien toimivan.

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #350 : 02.03.26 - klo:13.52 »
Jotta aivot pysyisivät kunnossa pitää niille järjestää vaikeuksia ja yrittää ratkoa niitä - ja siinä tehtävässä BASH on ihan hyvä ja se on syynä siihen että harrastan BASH:ia - jopa zsh ratkaisee monet vaikeudet puolestasi ja sehän ei ole tarkoitus. 
 
Perus-BASH:illa ei ole parempia toimintoja sillä alkuperäinen tarkoitus oli että BASH:in harrastajat opettelisivat tekemään itse parempia toimintoja toteuttavia funktioita - niiden tekemiseen tarpeelliset vanhat käskyt olivat tarpeellisen nopeat ja kyvykkäät - vanhoja käskyjä voi suorittaa peränjälkeen 10.000 eikä aikaa kulu sittenkään välttämättä montaakaan millisekuntia. Vanhoilla käskyillä toiminta onkin hämäävän nopeaa: vähintään satakertainen , mutta ehkä tuhatkertainen tai jopa suurempi nopeus verrattuna uusiin komentoihin - eihän sitä voi tietää sillä näitä nopeita on vasta pienehköissä skripteissä - edes  keskikokoisia ei vielä ole tehty yhtäkään.

Mutta ennenkuin parempia funktioita olit tehty niin virtuoosit tekivät uudet hitaat ja kyvyttömät käskyt joten itseasiassa virtuoosit arvostevat itse raatelemaansa käyttöjärjestemää jota ei heidän tekemillään käskyillä pysty saamaan toimintakuntoiseksi.

---

Vanhojen ja uusien komentojen vertauilua:

Uudet komennot ovat kovalevyllä ja ne käsittelevät tiedostoja ja niiden tulkkaaminenkin tökkii joten ne eivät voi ollakaan nopeitä. Sensijaan vanhat komennot käsittelivät keskusmuistisssa olevia matriiseja ja koska ne kuuluvat tulkkiin ne tulkataan heti ja kerralla kaikki joten ne olivat sataja kertoja nopeampia. Parempien funktioiden kanssa BASH joskus jopa voittaa virtuoosien tekemiä ohjelmia - ja estääkseen BASH:ia kilpailemasta tekemiensä ohjelmien kanssa - bc, awk ja sed kuulunevat joukkoon - virtuoosit romuttivat BASH:in tekemällä uudet hitaat ja kyvyttömät käskyt - mutta vanhoja käskyjä ei uskallettu tuhota sillä ne kuuluvat BASH-tulkkiin ja se olisi kärsinyt ja jopa romahtanu - mutta koska niistä puhutaan hyvin harvoin niin ne on puolittain unohdettu. Sitäpaitsi vanhojen käskyjen nopeus ilmenee vasta kun kaikki skriptin käskyt ovat vanhoja ja sellaista skriptiä ei uskottu kenenkää tekevän.
- esimerkiksi yksi vanhoista komennoista tekemäni skripti oli turhan hidas ja kun tutkin sitä selvittääkseeni miksi se oli niin hidas niin siellähän oli yksi uusi käsky ja kun sen korvasi vanhalla niin nopeus nousi kolminkertaiseksi.
- vanhat komennot ovat ihottavaa kirjainsotkua ja uudet sileää kuin silkki - sillä valhe on silkin sileää ja totuus karvasta.

Vanhoissa komennoissa on samat sisäiset loopit ja matriisit kuin uusissakin joten toiminnallisuus on siinä suhteessa sama.

Vanhat komennot tuntevat regex:ät niiltä ajoilta kun vanhat komennot on tehty. Mutta niiden regex:ät lisäksi täyttävät löytämällään muuttujan nimeltä: & . Siten esimerkiksi käsky: apu="tekstiä jossa on useita päivämääriä"; echo ${apu//[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]/$(tput blink)&$(tput sgr0)}
tulostaa tekstin ihan niinkuin se onkin mutta päivämäärät vilkkuen. Eihän se merkittävä asia ole mutta se on että se saa epäilemään että vanhoilla komennoilla on monia muitakin erikoisominaisuuksia jotka tekisivät vanhoista käskyistä vielä paljon parempia - mutta missään ei ole niistä enää minkäänlaisia suunnittelohjeita. Tuon &:n löysin kokeillessani olisiko vanhoissa käskyissä samoja piirteitä kuin sed:issä.

---

Kun BASH:in huippu-virtuoosi sanoo jostain asiasta ettei BASH osaa sitä niin sen epäileminenkin on melkein mahdotonta meille oppimattomille. Lisäksi BASH:in kaikkia ominaisuuksia vähätellään armottomasti ja BASH:lle on tehty monia katalia temppuja jotta estettäisiin se että joku alkaisi epäillä että virtuoosien *kirjaimellisesti* jokainen 'BASH ei osaa' väite on väärä ja alkaisi tehdä toimivia funktioita niihin kaikkiin ja saisi palautettua BASH:in toiminnaallisuuttatta. Ja onnistuivathan ne virtuoosien ketkut sillä BASH on tuhottu eikä ketään enää kiinnosta mitä se olisi voinut olla ja millaiseksi se olisi voinut kehittyä. Se mieltä kutkuttava piirre asiassa kuitenkin on että monet virtuoosit opettavat BASH:ia edelleen. Ja potaskaa puhuva virtuoosi on hilpeä juttu.

Vaikka niitä mätämunia on varmaankin virtuoosien joukossa tosi vähän ja hekin kauan sitten vaikuttaneita niin siitähuolimatta koko ammattikunta on saanut niin pahan kolauksen ettei se koskaan toivu.

---

Ensiksi osoittautui että nimi-parametrit toimivat - siis nimihän siirtyy kyllä ilmanmuuta mutta funktioon tullessaan se on vain tekstijono eikä muuttujan-nimi joten sen täytyy saada myös muuttujan arvot ja muutkin määreet ennenkuin se toimii kuten saman-niminen pääohjelman muuttuja. BASH antaa skriptaajien lukea muuttuja-tauluaan ja hakea ne sieltä. Mutta jos nuo arvot liittää samaan nimeen kuin ennenkin niin mitään ei ole saatu aikaiseksi mutta kun liittää ne jokakerran nimeen apu ja käsittelee kaikkialla funktiossa muuttujaa apu ja vasta ihan lopussa kertoo BASH:ille että apu on sama kuin alkuperäinen muuttuja niin kaikki toimii aina samoin riippumatta siitä alkuperäisestä nimestä eikä mitään tarvitse palauttaa sillä kaikkihan on jo muuttunut.
- funktion ja pääohjelman muistit ovat yhteiset ja muuttujat siis myös. Kummassavaan muuttujan asettaminen kajahtaa toisen saman nimiseen muuttujaan. Mutta käskyllä: apu=$(declare -p $1) muuttujan nimeksi tulee apu ja se saa ne arvot jotka muuttujalla $1 on. Jos siis apu-nimisen muuttujan arvoja muutellaan funktiossa niin se muuttaa apu-nimisen muuttujan arvoja pääohjelmassa. Mutta jos funktion viimeisenä tekona käsketään read<<<$apu $1 ( tai: let- tai export-) niim siirretään osoitin joka viittaa muuttujan apu viittaamaan siihen funktioon tulleseen nimeen. 
 
Seuraavaksi osottautui että liukuvan pilkun desimaali-laskenta toimii erinomaisesti - mutta tarvittavat funktiot saa tehtyä ainoastaan vanhoilla käskyillä. Tosin toistaiseksi matematiikka toimii nopeasti vain pienillä luvuilla sillä eihän tässä ole tarkoitus laskea vaan osoittaa mitä virtuoosit ovat saaneet aikaan BASH:ia tuhotessaan. Edistyneemmät laskut toimivat kyllä nekin, mutte esimerkiksi logaritmin määrittäminen jotain luvusta kestää 10 millisekuntia.
- sellaista asiaa ei olekaan kuin desimaalilaskin vaan kaikki laskimet ovat kokonaislukulaskimia. Nimittäin numerot tulevat oikein kun demaalipisteet poistaa ja suorittaa laskun senjälkeen - ja erillisessä yksikössä laskee mihin se desimaalipiste tuloksessa laitetaan. Muuta ei yleensä tarvitse tehdä mutta esimerkiksi yhteenlaskussa kummankin luvun desimaalien lukumäärät täytyy tehdä samoiksi ennen laskua.
- mutta mikään sääntö ei ole kiveenhakattu vaan aina tulee poikkeuksia.
- muuten vanhoissa käskyissä on yksi joka on tarkoitettu desimaaliluvun peränollien poistamiseen - siis yksi käsky poistaa kerralla kaikki peränollat riippumatta niiden lukumäärästä - joten ihan varmasti jotkut ovat aikoinaan harrastaneet desimaaali-laskentaa.

Uuden käskykannan luominen teki edellisten kaltaisten huomioiden tekemisen mahdottomaksi mutta se ei vielä riittänyt vaan yritettiin estää se ettei mahdollisesti aikaansaatuja uusia toimintoja saisi talletettua kirjastoon vaan tarvittavat funktiot olisi kirjoitettava aina skriptin alkuun ja se johtaa todella pitkiin skripteihin - ja se puolestaan tekee skriptien tekemisestä ja ylläpitämisestä vaikeaa sillä kun teksti on monirivistä 'sanskriittia' niin helposti tekee jonkun pienen virheen ja kirjain muuttuu jossakin - joten jo tehdyn funktion toimivana pitämisestä tulee tosivaikeaa - sillä kun skripti lakkaa toimimasta niin etsipä missa se kirjain on vaihtunut.

Kirjastoja oli mahdotonta tehdä toimimattomiksi sillä ne kuuluvat BASH:in perustoimintoihin - täytyi tyytyä poistamaan BASH:in kirjasto-osoitin ja mustamaalaamaan kirjastokäsitettä. Kaikenlaista muutakin tuhoavaa tehtiin kuten esimerkiksi väittämällä käskyä: eval tieto-turva-riskiksi ja kieltämällä käyttämästä sitä - sekin nimittäin mahdollistaa nimi-parametrit ja muut - BASH:in kekittäjätkään eivät ole väitettä nielleet vaan käyttävät sitä - esimerkiksi tiedostossa ~/.bashrc on kolme eval-käskyä. 

Vanhoja käskyjä käyttäen pienet perus-laskutoimitukset ovat nopeampia kuin bc:llä, awk:illa tai millään muullakaan matematiikka-ohjelmalla - mikä aikoinaan olisi ollut kova juttu - ja lisäksi tupla, tripla .... tarkkuuksia voi saavuttaa helpostikin. BASH:in kehittäjät saisivat toiminnasta pienellä vaivalla täysin kunnollista.
- silti eihän yksi mies voi koko matematiikkaa toteuttaa moitteettomasti joten raakileitahan nämä skriptini ovat. Varsinkin jakolaskussa on pieniä virheitä.
 
Kaikki olisi tosiaan toiminut jo ainakin vuonna 1990. Nyttehdyissä skripteissä kaikki on toteutettu vain pienimuotoisesti sillä tämähän on vain osoitus siitä että kaikki olisi toiminut. Toistaiseksi tosin vain kertolasku on toteutettu kunnolla ja summaaminen ja jakolasku osittain - sillä vasta eilis-aamuna aloin koodata viimeisiä huomioita.

Sillä osottautui että BASH:iin saa ohjelmallisestikin tehtyä melkein kunnollisen matematiikan jonka merkintätavat ovat melkein normaalit. Joudut kyllä kirjoittamaan sanan: laske laskusi eteen, esimerkiksi: laske tulos=4.9999^3 . Ja tulos tulee muuttujan: tulos arvoksi . Laskutyyppi voi olla mikätahansa tunnettu ja kaikki toimii jokaisessa skriptissä kaikkialla. Ja mikäli virtuoosit sallisivat kirjastojen käyttämisen niin kaikki toimisi yksinkertaisesti ja lisäämättä skripteihin mitään.

Alkuunsa virtuoosien ei edes tarvitsisi tehdä kirjastoja kunhan funktiot vain löytyvät jostain. Nimittäin perusasennuksessakin BASH:iin kuuluu noin 63 kirjasto-funktiota - näet ne päätteessä käskyllä: declare -f. Niitä voi vapaasti käyttää kuka hyvänsä vaikka vaikeata meille taviksille on keksiä niille mitään käyttöä - tosin käytät aina funktiota command_not_found_handle sillä se on se joka komennon puutuessa kirjoittaa näytöllesi: commad not found. Ne voi halutessaan myös määritellä uudestaan poistamatta sitä vanhaa määritelmää - itseasiassa voit määritellä uudestaan minkä käskyn haluat.

Koska kirjastoja ei saa käyttää niin nyt me tavikset joudumme kirjoittamaan skriptin alkuun kaikki tarvittavat funktiot - esimerkiksi logaritmin laskevassa skriptissä on 5 funktiota ja 1790 riviä vaikka käsky onkin vain yksi sana.

Jakolasku desimaaleilla on BASH:issa kummallista - hitaampaa kuin muilla laskutavoilla - mutta toisaalta tulokseeen voidaan laskea vaikka kuinkamonta desimaalia. Mutta myöntää täytyy että siinä on omituisia piirteitä kuten esimerkiksi 'katoavien nollien syndrooma' - varmaankin nuo omituiset piirteet ovat matemaatikolle ihan selviä.
« Viimeksi muokattu: 08.03.26 - klo:06.27 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 716
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #351 : 12.03.26 - klo:18.28 »
Aikanaaan väitettiin että BASH:in funktio-parametreja ei voi palauttaa - hölmöä puhetta sillä ei arvoparametreja voi palauttaa - sillä se koettaisi suorittaa seuraava-kaltaisen toiminnon 2=3.

Nimiparametreja taas ei osattu käyttää - mutta yritetty on ja tehty pitkiä, hankalia, epätäydellisiä ja tautisen hitaita skriptejä sitävarten - jotkut kuulemma käsittääkin kuinka ne toimivat. 

Matematiikka-mootori muuten hallitsee nimi-parametrit synnynnäisesti: function kerro () { echo $(( $1*$2 )) ;}; luku1=2; luku2=3; kerro luku1 luku2 # tästä tulostuu: 6

Helpoin menetelmä parametrin 'palauttamiseksi': funktion määritelmä löytyy muuttujataulusta -> siis itseasiassa myös funktiot ovat muuttujia. Muuttujalle voidaan antaa arvo - ja tekstijono voidaan korvata numerolla. Katsohan skriptistä:   
Koodia: [Valitse]
function kerro9 () { # tulo saa olla 18 numeroinen joten jäsenet voivat joustaa keskenään. Kerrottavien siirto nimiparametrina ja tuloksen 'palautus'
eval apu1=\$${1/\.*} # apu=$(declare -p $1); apu=${apu##*=}; luku1=${apu//\"/} # declare is much slower
eval apu2=\$${2/\.*} # apu=$(declare -p $2); apu=${apu##*=}; luku2=${apu//\"/}
kokonaisosa1=${apu1/\.*}
kokonaisosa2=${apu2/\.*}
apu=$(($kokonaisosa1*$kokonaisosa2)) 
kokonaisia=${#apu}; kokonaisia=$(($kokonaisia+$(( $apu < 0 ))))
tulos=$((${apu1/\./}*${apu2/\./}))
[[ ${tulos:1:1} -eq 0 && ${tulos:0:1} -eq 1 ]] && kokonaisia=$(($kokonaisia+1)) # tämä lause varmistaa että desimaalipiste siirtyy oikeala hetkellä
read<<<${tulos:0:$kokonaisia}.${tulos:$kokonaisia} $FUNCNAME ;}

luku1=3.162277; luku2=3.162279; kerro9 luku1 luku2; echo $kerro9

- siis itseasiassa parametrien lukumäärä on sama. Sanoisin jopa että BASH on selkeämpi.

- skriptin nopeus on luokkaa 0.3 millisekuntia.
« Viimeksi muokattu: 13.03.26 - klo:09.27 kirjoittanut petteriIII »