Kirjoittaja Aihe: Skriptiajuri  (Luettu 133137 kertaa)

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #80 : 01.05.24 - klo:17.54 »
Harrastuksena ja mieleisenä ajankuluna BASH alussa oli - mutta olin siinä luulossa että BASH on hidas ja kyvytön. Mutta enpä tiedä enää - alkaa tuntua siltä että kaiken saa toimmaan kymmeniä kertoja nopeammin kuin ennen  - ja paljon sellaista saa toimimaan jonka ei pitäisi toimia ollenkaan.

Sehän tuli kerrottua jo aikaisemminkin että yksi virtuoosi aikoinaan sanoi että jos todellista nopeutta etsii niin awk:it, sed:it ja semmoiset saa unohtaa. Silloin pidin sitä melkoisena liioitteluna mutta virtuoosi taisikin humalapäissään kertoa salaisuuksia ?

---

Miksi BASH:ista esittään kaikkialla niin vähän esimerkkejä? Ja miksi kaikki lähetyvät ongelmia samalta suunnalta vaikka BASH on niin laaja että kaikkeen on lukemattomia lähestymistapoja? Johtuuko se siitä ettei ohjeita tehdä vaan ne kopioidaan eikä niitä ohjeitakaan päivitetä koskaan - tosin joillain harvoilla verkkosivuilla mainitaan puolihuolimattomasti että 'tämmöistäkin uutta surkeutta löytyy'. BASH:ia tosiaan kehittää vain yksi ukko - ansiokkaasti kehittääkin ja varmaankin apujoukon kera - mutta mieleen tulee ettei hänkään halua sohia ampiaispesää tarpeettomasti.

Esimerkiksi 1+1 voidaan laskea ainakin kahdella yleisessä käytössä olevalla tavalla: ensinnäkin se normaali 'echo $((1+1))', se toinen tapa on : + 1 1  .
- mattematiikamoottori on kummassakin sama - tuo: $(($1+$2))
- kummessakin laskutavassa apuna täytyy olla funktio: siinä normaalissa on BASH:in sisäinen funktio nimeltään echo ja toisessa itsetehtävä funktio nimeltään +   .

Funktion nimeksi voidaankin määrätä melkein mitähyvänsä, esimerkiksi:
Koodia: [Valitse]
function + () { echo $(($1+$2)) ;}
- laskussa $(($1+$2)) + on matemaattinen merkki eikä funktiokutsu joten kyseessä ei ole rekursio (eli funktio ei kutsu itseään)
- kun annetaan käsky: + 1 2  niin se tulostaa 3 - mikäli löytyy tuo funktio nimeltään: +  .

Nimeltään tuo juohea laskutapa '+ 1 2' on käänteinen Puolalainen ja koska se vaatii BASH:issa käyttäjää tekemään itse tarvittavat funktiot niin käänteistä Puolalaista kannattaa käyttää vain kun matematiikkaa on tosipaljon. Ei tuota käänteistä Puolalaista tosin enää juuri koskaan käytetä.
- esimerkiksi laskuohjelman bc isä nimeltään dc oli käänteistä Puolalaista - se on koneelle helpompaa mutta ihmiselle niin vaikeaa että sille tehtiin kuori-ohjelma nimeltään bc joka muuttaa ihmisen ajatukset koneelle sopivammaksi käänteiseksi Puolalaiseksi. Onkohan käänteinen Puolalainen parempikin matikassa koska noin täytyi toimia?
« Viimeksi muokattu: 06.05.24 - klo:05.41 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #81 : 04.05.24 - klo:12.17 »

Yksi menetelmä etsiä tekstistä jotakin on muodostaa tuosta etsittävästä muotti ja ajaa teksti muotista läpi jolloin kaikki muotin mukainen tarttuu muottiin. Käytännössä toimitaan seuraavasti:

- etsittävän rakennetta kuvataan oliolla nimeltä regex: regex on muotti jonka määräysten mukaan etsittävän tyyppiset muuttujat on koottu - esimerkiksi päivämäärän muotti on: [0-9]+:[0-9]+:[0-9]+
- tässä paikassa merkki + merkitsee: yksi tai useampi samanlainen kuin on edessäkin.
- monille merkeille tulkki antaa asiayhteydestä riippuvan  erikoismerkityksen - useimmilla merkeillä on aina sama merkitys mutta esimerkiksi merkillä: * niitä on monia. Jos ei haluta tulkin antavan merkille erikoismerkitystä kirjoitetaan sen eteen kenoviiva - siis esimerkiksi: \*
- kun tekstin jokaista sanaa sovitellaan tuohon muottiin niin kun muotti sopii niin kyse on päivämäärästä.
- regex:ää on totuttu käyttämään jonkun apu-ohjelman avulla - esimerkiksi grep:in avulla ja kyllähän sillä omat etunsa onkin, mutta BASH kykenee useisiin regex:iin itsekin ja silloin kun BASH kykenee niin grep jää surkeasti kakkoseksi.
- myös tuo regex-maailma on valtavan suuri - katsopa verkkosivua: https://www.regexbuddy.com/. Ja samatyyppisiä sivuja on monia muitakin.

haku kokonaisuudessaan:
Koodia: [Valitse]
teksti="höpinää 17:06:23 lisää höpinää"; regex="[0-9]+:[0-9]+:[0-9]+"
[[ "$teksti" =~ $regex ]] && echo "tekstissä on päiväys:$BASH_REMATCH"
- tuosta regexään:n sopivasta muodostetaan aina BASH:issa muuttuja $BASH_REMATCH.

---

Mutta päiväys voidaan jakaa osiin: tuntiin, minuuttiin ja sekuntiin - ja mikäli regex on jakamista silmäälläpitäen muodostettu niin kukin näistä osista talletetaan omaan BASH_REMATCH[numero] nimiseen muuttujaan. BASH_REMATCH:ia selvitetään kaikkialla juurta jaksain - ja ehkäpä joku saa niistä selityksistä selvääkin. Mutta esimerkkejä ei esitetä; siis sellaista yksinkertaista skriptiä jossa tulostetaan BASH_REMATCH, BASH_REMATCH[1], BASH_REMATCH[2], BASH_REMATCH[3] ... jotta asiasta tulisi rautalankamalli jota ei voi käsittää väärin - tai oikeastaan että asian voisi nopeasti edes käsittää. Koetetaanpa nyt esitää asia ymmärrettävästi:

- BASH_REMATCH:ia käytettäessä regex:ät on jaettu suluilla osiin ja jokainen sulkupari muodostaa uuden muuttujan nimeltään:  BASH_REMATCH[1], BASH_REMATCH[2], BASH_REMATCH[3] ... ja niiden kokonaisuuden nimeltä: BASH_REMATCH. Esimerkiksi päivämäärää kuvaava regex jaetaan:
Koodia: [Valitse]
([0-9]+):([0-9]+):([0-9]+)   

Esimerkki:
Koodia: [Valitse]
teksti="höpinää 17:06:23 lisää höpinää"; regex="([0-9]+):([0-9]+):([0-9]+)"   
[[ "$teksti" =~ $regex ]] && echo "tekstissä seuraava palanen täytti koko regex:än: $BASH_REMATCH - ja sen osat: tunti:${BASH_REMATCH[1]} minuutti:${BASH_REMATCH[2]} sekunti:${BASH_REMATCH[3]}"
- mikäli koko regex ei täyty niin ei tulosteta mitään - elikä 2013:06 ei tulosta mitään.
- siis jokaisesta regex:än (....)-kentästä muodostuu uusi BASH_REMATCH[numero] - ja numerointi alkaa ykkösestä.
- muuten teksti voi olla matriisikin:
Koodia: [Valitse]
${teksti[@]}    - silloin tosin saadaan vain ensimmäinen joka sopii.
- kieliasetukset vaativat skriptiin omat muutoksensa - näillä määrityksillä tämä toimii Suomessa.
 
toinen esimerkki:
Koodia: [Valitse]
[[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]] && {
      echo "- kolmeriviä selitystä: REMATCH:it tehdäån kohdassa:[[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]]."
      echo "  Regex muodostuu useasta regex:stä. Tässä esimerkissä on kolme regex:ää ympäröity kaarisuluilla - huomio että"
      echo "  [0-9] on kaksikin kertaa BASH_REMATCH:in ulkopuolella vaikka kuuluvatkin regex:ään - siis valintajoukkoon ne kuuluvat mutta eivät BASH_REMATCH:iin."
      echo "BASH_REMATCH on koko regex alue= "$BASH_REMATCH
      echo "BASH_REMATCH[1] (elikä regex:än ensimmäisessä ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[1]}"
      echo "BASH_REMATCH[2] (elikä regex:än toisessa      ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[2]}"
      echo "BASH_REMATCH[3] (elikä regex:än kolmannessa   ()-osassa olevan regex:n hyväksymä arvo)= ${BASH_REMATCH[3]}" ;}


kolmas esimerkki, kenttien kääntäminen - vaikkapa Suomalaisen päiväyksen muuttaminen jonkun muun maan esitystapaan sopivaksi:
Koodia: [Valitse]
[[ 1.2.2013 =~ ([0-9]+).([0-9]+).([0-9]+) ]] && echo ${BASH_REMATCH[3]}.${BASH_REMATCH[2]}.${BASH_REMATCH[1]}
- kyse on nopeudesta. Työ olis helpompaa awk:lla tai semmoisilla mutta myös hitaanpaa. Ero on on millisekunneissa mitättömän pieni eikä muiden hitauteen edes vihjata missään - mutta itseasiassa nopeusero on yli kymmenkertainen. 

---

Esimerkiksi matriisien tarkistaminen regex:ällä senvaralta että siinä olisi kirjaimia:
Koodia: [Valitse]
function tarkista_matriisi () { [[ $@ =~ [[:alpha:]] ]] || (( $BASH_REMATCH )) && echo matriisissa ensimmäinen vastaantuleva kirjain:$BASH_REMATCH || echo "matriisisssa ei ole kirjaimia";}; matriisi=({1..100000}); matriisi[5000]=7A7e; time tarkista_matriisi ${matriisi[@]}

- tuo [[:alpha:]] on tässä se regex. Regex:t voivat olla tällaisia pieniäkin mutta esimerkiksi IP6-verkko-osoitteen tarkistamiseen sopiva regex on monen rivin hirviö - mutta silti erittäin nopea.

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #82 : 08.05.24 - klo:14.27 »
Funktioiden kuvaukseen tulee usein uusia huomioita joten funktioiden ohjeistus taitaa olla viisainta kirjoittaa silloin-tällöin uusiksi:
- esimerkiksi nimiparametrit ovat toimneet jo vuosia - ja ne myös poistivat tarpeen parametrien palauttamiseen. Mutta uudet käskyt muuttivat nimiparamtrien käyttämisen aina samaksi - kun siinä oli aikaisemmin paljonkin taiteilua.

Rajoitetaan sana funktio käsittämään vain niitä koodinpätkiä joiden edessä on sana function:
Koodia: [Valitse]
function funktion_nimi () { tässä on ihan normaali skripti ;}
- tulkki ei vaadi sanaa function kirjoitettavaksi sillä tulkki käsittää ilmankin mistä on kyse mutta ihmisten takia on viisainta kirjoittaa sana: function.
- se normaali skripti voi olla kirjoitettu yhteen tai useampaan riviin.
- kun skriptin koodissa on missähyvänsä sana joka on sama kuin jonkun funktion nimi niin mikäli tulkki ei keksi sille sanalle mitään muuta tehtävää niin silloin se on funktiokutsu. Tulkki vaihtaa kutsun ja sen perässäolevien muuttujien paikalle funktion koodin ja toimittaa muuttujat tähän siirtämäänsä funktioon. Muuttamatta pilkkuakaan kutsun kummallakaan puolella. Muuttujia ei tarvitse olla yhtään tai niitä voi olla vaikka kuinkamonta. Muuttujan paikalla voi olla myös vakio.
- kun päätteessä määritellään funktio niin jää se päätteen muistiin niin pitkäksi aikaa kun päätettä ei sammuteta. Mutta sammuttamisen yhteydessä kaikki katoaa peruuttamattomasti.
- funktion tarkoituksena on käsitellä muuttujia jotka funktioon on siirretty - siirrettyjä muuttujia kutsutaan parametreiksi.
- muttujasta voidaan siirtää sen arvot tai nimi - puhutaan arvoparametreista ja nimiparametreista. Pahantahtoiset ihmiset ovat kehittäneet tarun ettei BASH nimiparametreja tunne mutta se on legendaa - tosin BASH:issa nimiparametrit täytyy käsitellä toisellatavalla kuin muissa kielissä.
- arvoparametreja käytettässä voidaan siirtää vain yksi matriisi ja sen täytyy olla viimeisenä. Nimipaametreja käytettäessä nimisssä saa olla vaikka kuinkamonta matriisia eikä nimien sijaintipikallakaan ole väliä.
- sen merkiksi että parametreja saattaa funktioon tulla kirjoitetaan funktiomäärittelyyn () - näiden sulkujen väliin ei koskaan kirjoiteta mitään. Nuo sulut kannattaa kirjoittaa vaikka funktioon ei siirrettäisi yhtään parametria.
- funktiossa siihen siirretyillä parametreilla ei ole nimeä vaan vain siirtonumero - siis $1, $2 ...$n.
- $0 on funktion nimi.
- parametrien numerointi alkaa aina ykkösestä ja kasvaa parametrien välillä aina yhdellä. Toisaalta vaikka oisi siirretty muuttujanimi ei siitäkään funktiossa puhuta nimenä vaan numerona - ihmiselle vähän sotkuinen homma mutta koneelle ilmiselvä.
- parametri voi olla myös laskukaava tai funktiokutsu (= jonkun funktion nimi - myös funktion oma nimi kelpaa) - harvoin jotain muuta vaikka mahdollista se on.
- jokainen funktio vo kutsua nollaa tai useampaa ali-funktiota. Matemaattisilla funktioilla on yleensä useampia muiden funktioiden kanssa yhteisiä ali-funktioita mutta tekstinkäsittely tehtävillä usein ei ole yhtään - tosiaan paremmin pärjää kun ei jaa toimintoa funktioihin.
- tulkille on ihan selvää mikä kaikki funktiokutsun perässäoleva tauhka kuuluu parametreihin mutta tottumattomalle ihmiselle se ei ole aina selvää.
- parametrien lukumärää ei kirjoiteta mihinkään sillä tulkki haluaa laskea parametrien lukumäärän itse. Parametreja ei tarvitse olla välttämättä yhtään ja toisaalta niitä voi olla miljoona.
- nimiparametreja käytettäessä matriisejakin voi olla parametrien seassa useampia ja ne voivat sijaita misähyvänsä parametrien luettelossa - arvoparametreillahan matriiseja voi sirtää vain yhden ja sen tulee sijaita viimeisenä prametriluettelosa.
- funktioiden kutsuessa toisia funktiota täytyy kutsuista ylläpitää kutsupinoa - jotta virheen sattuessa tiedettäisiin mitä reittiä virheeseen on päädytty ja mikäli virhettä ei ole tapahtunut niin tiedettäisiin mihin pitää mistäkin palata - pinot toimivat automaattisesti eikä niihin käyttäjän tarvitse puuttua.

Funktiota ktsuttaessa BASH ei siirrä toimintaa funktioon vaan hakee funktion sinne missä toimitaan - siksi parametrien palauttaminenkin on turhaa.

Funktioiden pitää olla kirjastossa eikä sotkemassa skriptiään - kirjastojen olemassaolo onkin yksi BASH-kielen peruspilareista. Mutta koska BASH:ista halutaaan eroon niin sen kirjasto-osoitin poistettiin mikä tekee kirjastojen käyttämiestä hankalaa. Samalla alettiin mustamaalata funktioiden käyttöä. Mutta ennen kirjasto-osoittimen poistamista kerkisi nettiinkin tulla muutamia kirjastoja - siellä ne nyt kituvat unohdettuina.

Matikka kärsii kirjastojen keinotekoisesta puutteesta paljon mutta tekstinkäsittely vain vähän - ja matematiikkahan haluttiin estää kokonaan sillä sehän on valmisohjelmien reviiriä.

Tekstinkäsittely kärsii sekin vähän. Ja tekstinkäsittelyä opetetaan suorittamaan väärillä komennoilla: se hidastaa toimintaa ja aiheuttaa runsaasti epämääräisyyksiä. Lisäksi sitä vähättellään mahdottomasti mutta esimerkiksi BASH:in hakufunktiot toimivat aivan toisessa ulottuvuudessa kuin google tai mikään muukaan. Samoin oman koneen hoitamisen mahdollisuus on ensiluokkainen. Verkkotoiminnotkin ovat hyvät. Ainoa ongelma on se että kaikki on tehtävä itse, valmiita ohjelma-kokonaisuuksia on runsaastikin mutta niitä vartioidaan mustasukkaisesti - vallitseva mielipide tuntuu olevan: koko Linux saa mieluummin mennä turmioon kuin toiset käyttää töitäni. Ja nyt kaikki on hapantunut pilalle eivätkä loistavien töiden tekijät enää kehtaa töitään esittääkään.

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #83 : 09.05.24 - klo:17.28 »
BASh ei toimi niin että se palauttaisi funktioista jotakin. Tätä virtuoosit jaksavat jauhaa ikäänkuin se olisi jotenkin huono asia - mutta hölmöä puhetta se on sillä arvoparametreja ei edes voi palauttaa vaan se on yksinomaan nimiparametreille mahdollista. Ja virtuoosit eivät tunnusta BASH:in nimiparametreja tuntevankaan.

Mutta BASH:issa nimiparametritkaan eivät palauta mitään - valitettavasti niitä ei voi yksinkertaisin keinoin tulostaakaan vaan niillä voi ainoastaan muuttaa muuttujia (siis BASH:in funktiot ovat WriteOnlyMemory - muuttujien arvot voi tulostaa vasta pääohjelmassa). Sentakia BASH:issa tulostusta on tarkoitus tehdä yksinomaan pääohjelmassa ja muuttujien muutokset suoritetaan funktioissa.

Kerrottuna toisella tavalla: parametrien palauttaminen ei ole päämäärä. Päämäärä on se että samaa funktiota voitaisiin kutsua mistävaan, minkänimisillä_ja_tyyppisillä muuttujilla vaan ja funktio muuttaisi oikeat muuttujat. Mikäli muutos voidaan tehdä palauttamatta parametreja niin sehän on loistavaa. Mutta ei suinkaan ole sattumaa että BASH toimii näin - toiminto on aikaansaatu monien miestyövuosien tuloksena - ja sitten toiset heitivät toiminnon roskakoriin - hölmöyttään vai roistomaisuuttaan? Jokatapauksessa toiminta on seuraavanlaista:

Numeromuuttujan kanssa:
Koodia: [Valitse]
function koe () { read<<<$(($1*99)) $1;}; muuttuja=2; koe muuttuja; echo $muuttuja
- matikkamoottori siis hallitsee nimiparametrinkin tulostuksen:
Koodia: [Valitse]
function koe () { echo $(($1+0)) ;}; muuttuja=2; koe muuttuja

Tekstijono-muuttujan kanssa:
Koodia: [Valitse]
function koe () { read<<<"kova koitos" $1 ;}; muuttuja=alkutilanne; koe muuttuja; echo $muuttuja
Numeromuuttujista kootun matriisin kanssa:
Koodia: [Valitse]
function koe () { read<<<$(($1[3]*2)) $1[2];}; matrix=(1 2 3 4 5 6 7 8 9); koe matrix; echo ${matrix[@]}
Tekstijono-muuttujista kootun matriisin kanssa:
Koodia: [Valitse]
function koe () { read<<<$3 $1[$2] ;}; matrix=(yksi kaksi kolme neljä); koe matrix 2 loisto-uutinen; echo ${matrix[@]}

---

numero-muuttujan ja tekstijono-muuttujan raja täsäkin työssä yhtä epämääräinen kuin muutenkin - ja voidaan kirjoittaa:
Koodia: [Valitse]
function kerro9 () {
luku1=$1
luku2=$2
int1=${luku1%%.*};
int2=${luku2%%.*};
apu=$(($int1*$int2))
kokonaisia=${#apu}
tulos=$((${luku1//./}*${luku2//./}))
echo ${tulos:0:$kokonaisia}.${tulos:$kokonaisia} ;}

function koe () { read<<<$(kerro9 $1 11.22) $1;}; muuttuja=2; time koe muuttuja; echo $muuttuja
« Viimeksi muokattu: 11.05.24 - klo:07.02 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #84 : 12.05.24 - klo:22.00 »
Kuinka nimi-parametrin saa toimitettua funktioon ja palautettua funktiosta muutettuna:

- vaikka itseasiassa BASH ei palauta mitään: kaikki kielet pitävät kirjaa muuttujistaan. Muut kielet osaavat päivittää kirjanpitonsa vain pääohjelmassa joten niiden tarvitsee palauttaa parametrinsa funktiosta - mutta BASH osaa päivittää kirjanpitonsa jo funktiossa joten sen ei täydy parametreja palauttaa.
- aluksi esitetään vain lopputulos selittämättä vielä mitään.
- yksinkertaisuuden vuoksi parametreja on tässä vain yksi matriisi.
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; apu2[2]=""oh hoh""; unset $1; read<<<${apu2[@]} $1;}; matrix=(1 2 3 4 5 6 7 8 9); koe matrix; echo ${matrix[@]}
- vastaava funktio on kaikissa kielissä pitkä hirviö - muut kielet vain saavat inhottavuudet kätkettyä kirjastoihin mutta BASH:ilta se on kielletty. Nopeuteen se ei kumminkaan vaikuta - vaikka eihän BASH ole muutenkaan kovin nopea. 
- tässä funktiossa on hyvin paljon vakio-osia: vain käsittely täytyy muuttaa sellaiseksi kuin käsiteltävä vaatii - tässä sillä paikalla on: apu2[2]="ohhoh". Kaikki muu kopioidaan. Siis esimerkiksi tavallisella muuttujalla funktio on ihan samanlainen lukuunottamatta muuttujan käsittelyä:
Koodia: [Valitse]

function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; apu2="oikea arvo on: "$(($apu2*2)); unset $1; read<<<${apu2[@]} $1;}; muuttuja=57; koe muuttuja; echo $muuttuja


Yksi asia on varma: jos virtuoosit sanovat joukolla jotakin mahdottomaksi ei yksikään järjissään oleva ihminen tee vakavia yrityksiä sen valheeksi osoittamiseksi. 

Tässä on kymmeniä todella edistyneitä yksityiskohtia ja yhdenkin välipalikan puuttuminen romuttaisi kokonaisuuden täysin, elikä kokonaisuus on varmasti tarkoin suunniteltu. Miksi ponnistelut on valheilla tuhottu? Kunnia menisi väärälle henkilölle?

- käskysarjat ovat todella pitkiä - mutta ne ovat aina samanlaisia joten ei niitä kirjoiteta vaan kopioidaan jostakin. Kirjastot on tehty jotta koodatessa kopiointi onnistuisi yhdellä sanalla eikä tarvitsisi leikata-liimata pitkää litaniaa - mutta kirjastojen 'käyttökielto' pakottaa tuohon leikkaamiseen-liimaamiseen ja monen sivun skripteihin?
« Viimeksi muokattu: 14.05.24 - klo:18.18 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #85 : 15.05.24 - klo:08.42 »
Sitten niitä selityksiä. Ensin parametrien toimittaminen funktioon:

1. Alkeellisin tapa toimittaa parametrit funktioon on lähettää sinne funktioon parametrien arvot. Yksinomaan tätä tapaa käytetään nykyään. Pahin ongelma niiden kanssa on se että niitä pitää osata käyttää ja virtuoosit eivät osaa. Tai ainakin väittävät niin, sillä BASH:ista halutaan eroon ja jos puolitotuuksilla eron saa aikaiseksi niin siitävaan.
- esimerkiksi puhutaan parametrien palautuksesta ja sanotaan ettei BASH osaa parametreja palauttaa. Välittämättä siitä että ei tässä siitä ole kyse.
- mutta on arvoparametreilla todellisiakin puutteita: esimerkiksi kun niitä käytetään niin parametreissa saa olla vain yksi matriisi ja sen täytyy sijaita viimeisenä.
 
2. Kehittyneempi tapa on lähettää parametreista funktioon ainoastaan nimet. Kun BASH:issa funktioon tulee nimi ei sillä ole saman-nimisen muuttujan kanssa mitään tekemistä vaan se on ainoastaan tekstijono - tästä eteenpäin ei ole päästy ja niinpä on luultu että nimiparametrit eivät BASH:issa toimi - tai taas kertaalleen: BASH:ista halutaan päästä eroon ja .....

Muissa kielissä tulkissa/kääntäjässä on automatiikka nimen yhdistämiseksi saman-nimisen muuttujan arvoihin - BASH:in tulkista tuo automatiikka puuttuu ja se täytyy itse lisätä, esimerkiksi näin:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2=${apu2%\"}; apu2=${apu2#\"}; echo ${apu2[@]} ;}; matrix=(1 2 3 4 5 6 7 8 9);  mitrix=(7.7 joo); koe matrix; koe mitrix; koe matrix

Taas selitystä:
- ensin teoria: BASH:issa on jokaisella prosessilla vain yksi kirjanpito muuttujille - ja se päivitetään aina välittömästi kun jonkun muuttujan arvo muuttuu. Kirjanpidossa ei voi olla kahta saman-nimistä muuttujaa sillä jos muuttuja määritellään uudestaan niin uusi arvo ja sen typpimääritykset korvavat välittömästi kirjanpidossa sen aikaisemman kuvauksen. Kirjanpito on sama pääohjelmassa, funktioissa, funktion-funktioissa, funktion-funktion-funktioissa .... joten jos jossakin muutetaan jotakin muuttujaa muuttuu se kaikkialla - sillä ei ole väliä jos muutos tehdään sijais-nimen välityksellä. Vasta kun siirrytään uuteen prosessiin ovat sen muuttujat vain sen omia.
- sitten kuinka käytännössä toimitaan: BASH antaa skriptaajan lukea kirjanpitonsa. Funktioon siirryttyä luetaan apu1:een kirjanpidosta sen muuttujan kuvaus jonka nimi on sama kuin parametrinä tullut muuttujanimi.
- senjälkeen muuttujaan nimeltään apu2 tehdään parametrista täydellinen kopio yhdistämällä kirjanpidosta nimeen apu2 parametrinä tulleen muuttujan kuvaus.
- ei se kopio tosin vielä ole ihan täydellinen sillä kuvauksestahan siirtyvät myös lainausmerkit arvoja ympäröimään ja ne täytyy poistaa käskyillä:
Koodia: [Valitse]
apu2=${apu2%\"}; apu2=${apu2#\"}
- mutta poistaminen tapahtuu vain mikäli niiden pakoilla tosian on lainausmerkki - aina ei ole, muut merkit saavat jäädä - lainausmerkien edessä täytyy olla kenot jotta tulkki käsittelisi lainausmerkkejä tavallisina aakkosina.
- asiasta kirjoitetaan kolme riviä tulostusta jotta olisi pakko uskoa että tämä tekee sen mitä halutaan - selitykset voi aina käsittää väärin jos kovasti yrittää mutta tulosteet ovat yksiselitteisiä ja ne on pakko ymmärtää oikein.
« Viimeksi muokattu: 16.05.24 - klo:11.07 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #86 : 17.05.24 - klo:07.07 »
Matriisit ovat BASH:issa vähän omituisia. Esimerkiksi matriisin: jokunimi=(1 2 3 4 5 6 7 8 9) kuvaus on: 
Koodia: [Valitse]
declare -a jokunimi=([0]="1" [1]="2" [2]="8" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9")
Siitä erotetaan tekstijono joka kuvaa muuttuja arvoja: ([0]="1" [1]="2" [2]="8" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9"). Tekstijono on nykykäsityksen mukaan kummallinen koska on totuttu siihen että matriisin jäsenistä ilmoitetaan vain niiden arvo - mutta tosiasiassa matriisin jokaisella jäsenellä on sekä arvo että osoite. Muitten kielien matriisit vaan ovat sellaisia että niiden osoitteet alkavat aina ykkösestä ja kasvavat yhdellä seuraavalla jäsenellä joten osoite on sama kun jäsenen järjestysluku. Mutta BASH:issa ei ole näin vaan matriisin jokaisella osoitteella voi olla ihan mikä positiivinen arvo tahansa joten sekä arvoa että osoitetta täytyy raahata perässään - kuvatulla tavalla muodostetuilla matriiseilla on erinomaisia lisä-ominaisuuksia mutta noiden lisä-ominaisuuksen soveltaminen käytäntöön on ihmisille niin vaikeaa käsittää että käytännössä niitä ei nykyään enää sovelleta - mutta jokatapauksessa tässä toiminta on otettu huomioon eikä rikottu BASH:in matriisien rakennetta.
- joten luetaan matriisin kuvaus muuttujaan apu1. 
- muuttujan apu2 muodostamiseksi annetaan käsky:
Koodia: [Valitse]

declare ${apu1:8:2} apu2=$tekstijono

- tämänjälkeen apu2 on klooni alkuperäisestä muuttujasta - muuten paitsi nimi on apu2 - siis sillä on sama tyyppi ja samat arvot olipa se numero- tai tekstimuuttuja, matriisi tai assosiatiivinen matriisi.


Jälleen selitystä:
käsky:declare täytyy kirjoittaa itse, se ei kopiosta toimi - muu osa kuvauksesta toimii kopiosta mutta muuttujanimi täytyy muuttaa. Koska tuon -a paikalla on muuttujan määreet niin sillä paikalla lukee milloin mitäkin, mutta koska se on aina samalla paikalla niin sillä paikalla oleva kopioidaan - se on tuo ${apu1:8:2}
- muuttujan määreitä ovat: onko kyseessä tavallinen muuttuja vaiko matriisi - tai assosiatiivinen matriisi - ja ovatko parametrien alkiot read-only, integer ...   
- jokaista skriptiin lähetettävää muuttujaa kohden täytyy muodostaa oma apu1 ja apu2, apu3 ja apu4, apu5 ja apu6 ... . Ensimmäisen muuttujan nimi on apu2. Seuraavista muuttujista tulisi apu4, apu6, apu8 .... Muuttujat: apu1, apu3, apu5 ... ovat vain väliaikaisia talletuspaikkoja.
- matriiseja saa nimiparametreissa olla kuinkamonta vaan ja ne voivat sijaita kutsussa missävaan.

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #87 : 18.05.24 - klo:08.56 »
Muuttujien arvot on nyt siirretty funktioon - ja millä nimellä ne ovat funktioon tulleetkin niin funktion sisällä niillä on aina sama nimi joten niitä voidaan käsitellä tulonimestä riippumatta aina samoilla käskyillä - muutokset tulevat aina sille 'samalle nimelle'.

Ennenkuin skriptistä palataan nuo 'aina saman nimiset' muuttujat kopioidaan takaisin niihin parametreina tulleisiin muuttujanimiin - käskyllä:
Koodia: [Valitse]
unset $1; read<<<${apu2[@]} $1
- tässä on uutta vain tuo 'unset $1' - se tarkoitaa: nollaa $1 - koska $1:ssä on se vanha arvo ja jos sen lisäksi kirjoitettaisiin muuttunut arvo niin se alkuperäinen tulisi tulosteessa sen muutetun jälkeen.
- muuten: jokainen muuttuja on sama kuin saman-niminen matriisi jonka osoitteessa 0 on muuttujan arvo - siksi käsky:
Koodia: [Valitse]
read<<<${apu2[@]} $1 
toimii tavallisellakin muuttujalla.

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #88 : 20.05.24 - klo:18.50 »
kokeillaan toimivatko assosiatiiviset matriisit myös:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; echo ${apu2[@]} ;}; matriisi[555]=2; matriisi[17]=joo; matriisi[ei]=4; koe matriisi
   
tulostus: 4 joo 2
Joten toimivathan ne - ainakin jollain tavalla. Assosiatiivinen matriisi muuten aakkostaa itsekseen jotenkin.

---

Miksi parametrien palauttamisesta puhutaan jatkuvasti? - tai alkaahan puhe jo harveta kun BASH on tuomittu jo kauan sitten. Onko syy se että jo silloin aikoinaan alettiin suunitella BASH:in romutusta sillä se on virtuoosien kannalta nolo opetettava - jokainen lyö kirveensä kiveen liian usein. Joten ei tosiaan haluttu ihmisten käsittävän mihin kaikkeen BASH kykenee sillä kykenevästä BASH:ista ei päästäisi koskaan eroon. Sillä niinkuin äsken esitettiin niin BASH saa funkktio-parametrien palauttamisessa aikaan saman lopputuloksen - nopeammin ja sillätavalla millä asia pitäsikin hoitaa. Samoin ne hyvin toimivat desimaalilaskut? Ja monia muitakin mahdottomiksi väitettyjä toimintoja olen saanut toimimaan - mutta niiden merkitys on toistaiseksi ainakin minulle epäselvä sillä siitä on jo kauan kun teoriat kunnolla hallitsin.

---

Mikäli tarvitsee 'palauttaa' funktiosta vain toiminnan lopputulos kannattaa se tehdä niin että se lopputulos tulostetaan funktiossa ja pääohjelmassa se napataan johonkin muuttujaan käsky-rakenteella: muuttuja_nimi=$(funktiokutsu parametrit) - palautus tapahtuu siis näyttöpuskurin kautta - toisella tavalla kuin muissa kielissä mutta yhtä tehokkaasti. Myös matriisi voidaan palauttaa tällä tavalla:
Koodia: [Valitse]
function koe () { apu1=$(declare -p $1); declare ${apu1:8:2} apu2=${apu1#*=}; apu2[2]=ohhoh; echo ${apu2[@]} ;}; matrix=(1 2 3 4 5 6 7 8 9); mutrix=($(koe matrix)); echo ${mutrix[@]}
- mutrix on pesunkestävä matriisi vaikka se tulostetaankin tässä yhteen riviin.
- käsky: mutrix=($(koe matrix)) on tarpeen vain jos matriisi nimeltään:mutrix halutaan aikaansaada. Mutta usein halutaan vain tuo tulostus ja sehän on funktiossa tehty jo. Enää ei ole tarvetta 'palauttaa' yhtään mitään - kutsua voi lyhyesti:  matrix=(1 2 3 4 5 6 7 8 9); koe matrix .
- näyttöpuskurin lukeminen rakenteella: muuttuja_nimi=$(funktiokutsu parametrit) tai lukemalla sen putkeen nollaa näyttöpuskurin eikä mitään kirjoiteta näytölle, muuten se kyllä näytölle aikanaan päätyy.

Aina ei tarvitse sitä näyttöpuskuria edes lukea vaan sitä voi käsitellä suoraankin, esimerkiksi:
function koe () { echo 123456789012345678901234567890; echo 1110000; echo 12345678901234567890 ;}; koe | wc -L
Tässä funktion kaikki tulosteet menevät näyttöpuskuri-matriisissa omalle rivilleen, näyttöpuskuri työnnetään putkeen joka johtaa käskyyn: wc -L joka tulostaa näyttöpuskurin pisimmän rivin pituuden joka on 30, toiset rivit ovat pituudeltaan 20 ja 9. Rakennetta: echo $(koe) ei siis tarvita.

- ja yksi keino parametrien palautuksessa jää jotenkin pimentoon: funktiossa voi myös kirjoittaa kovalevylle - ja myös kovalevyn nopea vastine RAM-levy kuuluu BASH:in perusvarustuksiin - ja funktiosta tultua kovalevy luetaan. Levyjen käsittelyyn on buffereita, tiedosto-kuvaajia, pelejä ja pensseleitä - ihan oma maailmansa - monimutkainen kylläkin sillä käskyt ovat kauheaa merkkisotkua - kirjastot on tarkoitettu muunmuassa muuntamaan käskyjä ja käskyryhmiä helposti muistettaviksi - mutta koska BASH:in ei haluta toimivan hyvin niin kirjastoille on määrätty käyttökielto - se onkin paras keino kielen tuhoamiseksi. Kovalevyllehän asiat yleensä muutenkin päätyvät - onko sillä niin väliä koska se tehdään?

---

- loppuyhteenveto kaikesta edelläesitetystä: kyllähän tämä vähän turhauttaa koskei asialle voi tehdä enää mitään. ChatGpt ehkä voisi tehdä jotain vieläkin joten ChatGpt:n tekemä koodi kiirehdittiin tekemään Linuxissa 'laittomaksi'. Mutta kukaan ei voi edes aavistaa mihin BASH pystyisi jos sen annettaisiin kehittyä - kolmenkymmenen vuoden hyljeksintää ei kyllä luultavasti koskaan saa kurottua kiinni. Tehdyt esteet ovat useimmiten vain tahallista väärin ymmärtämistä - sillä eihän se ole rikollista etteivät alan johtavat virtuoosit alansa kaikkia muttereita tunne? Varmasti aikoinaan tunsivat, mutta nykyään se on kylläkin vain opittu asenne. Asenne joka on minullekin opetettu - ja paljon kunnollistakin särkyy kun oppeja hylkää.

petteriIII

  • Käyttäjä
  • Viestejä: 673
    • Profiili
Vs: Skriptiajuri
« Vastaus #89 : 29.05.24 - klo:14.11 »
Silloin kun BASH luotiin sen käskyt tulkattiin muistista ja ne olivat nopeita - ihan niinkuin muissakin kielissä.
Nykyiset käskyt luetaan levyltä ennen tulkkaamista - epäilen kylläkin että ne ovat jo käännettyä koodia eikä niissä enää mitään tulkkaamista ole - mutta levyn lukeminen niissä kestää.

Ei sitä yksiomaan tuomita voi sillä on sillä paljon hyviäkin seurauksia. Mutta esitetäänpä yks esimerkki huonoista puolista: erotetaan tekstijononsta joku määräätävä sana. Totunnaiseti aikaansaadaan jotakin seuraavankaltaista:
Koodia: [Valitse]
echo '9 87 654 3210' | awk '{ print $3 }'
# tai:
awk '{ print $3 }' <(echo '9 87 654 3210')
#tai:
echo '9 87 654 3210' | cut -d' ' -f3
# tai:
cut -d' ' -f3 <(echo '9 87 654 3210')
- awk on tosi nopea ja monipuolinen - ja tämä awk:ille epä-edullinen esimerkki. Muuten cut on nopeampi.

Nopeilla käskyillä se on esimerkiksi (tämmöisiäkin tapoja on ziljoonia täysin erilaisia ja kullakin on omat erikoisominaisuutensa - mutta kaikki ne ovat nopeita):
Koodia: [Valitse]
function tavu () { apu=$1; set ${@:2} ; eval echo \$$apu ;}
tavu 3 "9a 87b 654c 3210d"
Nopeusero on niin suuri ettei sitä saa kunnolla  mitattua millään konstilla. Yksi perusteltu arvio on: 50* nopeus.

---

Tein funktiosta version jonka toiminta-nopeus on sama mutta se toimii ikäänkuin parametri palautettaisiin eikä tulosta tarvitse pää-ohjelmassa lukea rakenteella $(kutsu). Skripti ja varsinkin sen kutsu ovat niin selväpiirteisiä että on käsittämätöntä ettei sellaisia käytännössä näy:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}
etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"
echo $sana # tämä echo on ihan vain varmistuksena että skripti on tehnyt sen mitä pitäisikin.
- heittomerkit voivat olla kovia, pehmeitä tai ne voivat puuttua kokonaan.
- etsintä  voi alkaa edestä tai takaa - suunta riippuu etsintä-numeron merkistä. Toiminnan nopeuteen se ei kuitenkaan vaikuta.
- etsittävässä saa olla suurinpiirtein mitätahansa merkkejä - paitsi samanlaisia heittomerkkejä kuin alussa ja lopussa. Nykymuotisessa BASH:issa ei saa.

- Puhutaan vain ettei BASH osaa palauttaa funktioista parametreja ihan niinkuin se merkitsisi BASH:issa jotakin huonoa - päinvastoin se tarkoittaa hyvää. Jos esimerkiksi skriptissä on rivi:
etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"
niin jos funktio:etsi on oikein tehty niin seuraavalla rivillä muuttujalla:sana on oikea arvo - eikä varmasti ole palautettu mitään - nyt voi vedota siihen ettei BASH osaa. Paikalle:sana voi kirjoittaa minkä tekstin hyvänsä ja seuraavalla rivillä se on saman-niminen muuttuja. Voi se alunperinkin olla muuttuja jonka arvo on aikaisemmin ollut mitävaan tai peräti ollut toisentyyppinen.

- suurta nopeutta ei voi mitata suoraan - eikä se oikein kannattaisikaan sillä skriptin suoritus-aika vaihtelee aina vähäsen - mutta yksi ratkaisu on muodostaa keskiarvoa sillä antaahan se ainakin jonkinlaisen ajan:
Koodia: [Valitse]
function etsi () { sana_matriisina=(${@:4}); read<<<${sana_matriisina[$2]} $1 ;}
time { for n in {1..100000}; do etsi sana 3 lauseesta "9a 87b 654c 3(21)0d'ille!"; done ;}; echo $sana
- tämmöisessä looppi-virityksessä pitäisi laskea loopin vaikutus pois - mutta sen arvoksi voi olettaa tässätapauksessa 10% - mitattu on.
- välimuisti sotkee oikeaa tulosta, mutta pakkohan keskiarvoa on muodostaa koska tuollaista nopeutta ei voi mitata mitenkään toisin - eikä se välimuisti nopeuta sen enempää kuin 50%. Eikä se edes vaikuta täysmääräisenä heti ensimmäisellä kerralla. Kokeile. Sen kokeileminen tosin edellyttää ziljoonaa skripiä ja viikkojen pähkälyjä.

- mikäli sanoja erottaa joku muu merkki kuin välilyönti voi sen määrätä - seuraavassa sana-välinä on \n  (elikä rivinsiirto). Mutta voi se tekstiäkin olla. Skripti ja sen kutsu ovat silloin:
Koodia: [Valitse]
function etsi () { apu=${@:4}; sana_matriisina=(${apu//$5/ }); read<<<${sana_matriisina[$2]} $1 ;}
etsi sana 3 lauseesta "9a\n87\n654c\n3(21)0d'ille!" \n ; echo $sana
- jos raakaa nopeutta haluaa niin tässä on 20 mikrosekuntia kestävä versio, mutta muita ominaisuuksia sillä ei montaakaan olet:
Koodia: [Valitse]

function hae_tavu () { sana_matriisina=(${@:2}); echo ${sana_matriisina[$1]} ;}
hae_tavu 3 "9a 87b 654c 3(21)0d'ille!"

« Viimeksi muokattu: 09.06.24 - klo:15.54 kirjoittanut petteriIII »

nm

  • Käyttäjä
  • Viestejä: 16292
    • Profiili
Vs: Skriptiajuri
« Vastaus #90 : 29.05.24 - klo:14.20 »
Silloin kun BASH luotiin sen käskyt tulkattiin muistista ja ne olivat nopeita - ihan niinkuin muissakin kielissä.
Nykyiset käskyt luetaan levyltä ennen tulkkaamista - epäilen kylläkin että ne ovat jo käännettyä koodia eikä niissä enää mitään tulkkaamista ole - mutta levyn lukeminen niissä kestää.

awk ja cut eivät liity Bashiin, vaan ne ovat erillisiä ohjelmia. Awk:n historia ulottuu alkuperäistä Bourne shelliäkin kauemmas, Bashista puhumattakaan.

Ulkoista komentoa kutsuttaessa aikaa menee aina uuden prosessin käynnistämiseen. Binäärin ja mahdollisten oheiskirjastojen lukeminen kestää toki myös hetken, mutta vain ensimmäisellä kerralla. Sen jälkeen ohjelma on käytännössä valmiina kernelin levyvälimuistissa. Prosessi sen sijaan luodaan aina uudelleen, kun ulkoista komentoa kutsutaan bash-skriptissä.

Näin on aina ollut, eli tilanne on Bashin kannalta sama kuin vuonna 1989.
« Viimeksi muokattu: 29.05.24 - klo:14.25 kirjoittanut nm »