Ubuntu Suomen keskustelualueet

Ubuntun käyttö => Ohjelmointi, palvelimet ja muu edistyneempi käyttö => Aiheen aloitti: mikk0 - 12.09.05 - klo:21.49

Otsikko: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: mikk0 - 12.09.05 - klo:21.49
Jos tahdot saada hyvää ohjeistusta komentotulkin (bash) skriptaukseen, niin Ubuntun Multiversestä löytyy abs-guide, eli The Advanced Bash-Scripting Guide.
Voin suositella lämpimästi, jos englanti ei tuota ongelmia.

Koodia: [Valitse]
sudo apt-get install abs-guide

Tämän jälkeen opus löytyy koneeltasi kun suuntaat vaikkapa Firefoxilla seuraavaan osoitteeseen file:///usr/share/doc/abs-guide/html/index.html

Mikko  ;D
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Sapetsi - 13.09.05 - klo:10.38
Kiitoksia mikk0. Nappasin eilen tuon paketin ja vilkaisin hiukan, tehokas tietolähde vaikuttaa olevan :)
Ajattelin aloittaa jannen laittamasta "BASH Programming - Introduction HOW-TO: http://www.tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html" pätkästä (kiitokset myös jannelle!) ja jatkaa tähän abs guideen seuraavaksi.

Lukemista näissä riittää ja pään raapimista on todennäköisesti tiedossa mutta eiköhän tämä tästä pikkuhiljaa...  :D
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: peran - 14.10.05 - klo:23.14
Vastaan tähän säikeeseen, koska en keksinyt parempaakaan paikkaa.

Olen tekemässä scriptiä, jolla Exif-datan mukaan määritellään pystykuva/vaakakuva.
Nyt puuttuu ohjelma, jolla pyöräytän kuvan pystykuvaksi.

Asiaan: Mikähän ohjelma pyöräyttää JPEG-kuvan 90 asteiden pykälissä muuttamatta EXIF-dataa paljoakaan (Orientation-tieto tietenkin saa muuttua).

Tietenkin olisi hyvä, jos kuva ei muuttuisi alkuperäisestä.
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 15.10.05 - klo:00.10
Asiaan: Mikähän ohjelma pyöräyttää JPEG-kuvan 90 asteiden pykälissä muuttamatta EXIF-dataa paljoakaan (Orientation-tieto tietenkin saa muuttua).

Tietenkin olisi hyvä, jos kuva ei muuttuisi alkuperäisestä.

jpegtran kääntää jpeg-kuvia häviöttömästi 90 asteen välein, tosin tuohon hommaan on myös ohjelma joka kääntää kuvan kuten jpegtran, mutta tekee sen EXIF-datan määräämällä tavalla. sen nimi on exiftran.
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: peran - 15.10.05 - klo:00.47
Asiaan: Mikähän ohjelma pyöräyttää JPEG-kuvan 90 asteiden pykälissä muuttamatta EXIF-dataa paljoakaan (Orientation-tieto tietenkin saa muuttua).

Tietenkin olisi hyvä, jos kuva ei muuttuisi alkuperäisestä.

jpegtran kääntää jpeg-kuvia häviöttömästi 90 asteen välein, tosin tuohon hommaan on myös ohjelma joka kääntää kuvan kuten jpegtran, mutta tekee sen EXIF-datan määräämällä tavalla. sen nimi on exiftran.
Öh mitenkäs sources.list pitäis olla, kun nykyisessäni en pysty löytämää kumpaakaan en pysty löytämään.
elikkäs tässä on nykyiseni. (En löytänyt synaptecin pakettien hallinnalla).
Valitan en löytänyt kooditageja:
Koodia: [Valitse]
deb cdrom:[Ubuntu 5.04 _Hoary Hedgehog_ - Release i386 (20050407)]/ hoary main restricted


deb http://fi.archive.ubuntu.com/ubuntu hoary main restricted
deb-src http://fi.archive.ubuntu.com/ubuntu hoary main restricted

## Major bug fix updates produced after the final release of the
## distribution.
deb http://fi.archive.ubuntu.com/ubuntu hoary-updates main restricted
deb-src http://fi.archive.ubuntu.com/ubuntu hoary-updates main restricted

## Uncomment the following two lines to add software from the 'universe'
## repository.
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in
## universe WILL NOT receive any review or updates from the Ubuntu security
## team.
# deb http://fi.archive.ubuntu.com/ubuntu hoary universe
# deb-src http://fi.archive.ubuntu.com/ubuntu hoary universe

deb http://security.ubuntu.com/ubuntu hoary-security main restricted
deb-src http://security.ubuntu.com/ubuntu hoary-security main restricted

# deb http://security.ubuntu.com/ubuntu hoary-security universe
# deb-src http://security.ubuntu.com/ubuntu hoary-security universe
#deb http://download.skype.com/linux/repos/debian/ stable non-free
deb http://archive.ubuntu.com/ubuntu warty main restricted universe
deb-src ftp://archive.ubuntu.com/ubuntu warty main restricted universe
#deb http://backports.ubuntuforums.org/backports/dists/warty-backports/universe/binary-i386/ ./
#deb http://debian.meebey.net/ ./

## Elosen sivuilta voi päivittää soikon oppenoffiseen, muuten riskaa peliä
#deb http://elonen.iki.fi/code/unofficial-debs/ ./

deb http://archive.ubuntu.com/ubuntu hoary-backports main restricted universe multiverse
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 15.10.05 - klo:10.24
Öh mitenkäs sources.list pitäis olla, kun nykyisessäni en pysty löytämää kumpaakaan en pysty löytämään.
elikkäs tässä on nykyiseni. (En löytänyt synaptecin pakettien hallinnalla).

mik0n suomentamassa ubuntuguidessa on ihan hyvä setti:
http://koti.mbnet.fi/mikko75/ohjeet/ubuntuguide/index.html#extrarepositories

exiftran näkyy olevan ihan omassa paketissaan ja jpegtran on paketissa libjpeg-progs (vaikka tietysti kirjastona tulee muidenkin softien mukana).
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: E.K.Virtanen - 13.11.05 - klo:18.02
Jos tahdot saada hyvää ohjeistusta komentotulkin (bash) skriptaukseen, niin Ubuntun Multiversestä löytyy abs-guide, eli The Advanced Bash-Scripting Guide.
Voin suositella lämpimästi, jos englanti ei tuota ongelmia.

Koodia: [Valitse]
sudo apt-get install abs-guide

Tämän jälkeen opus löytyy koneeltasi kun suuntaat vaikkapa Firefoxilla seuraavaan osoitteeseen file:///usr/share/doc/abs-guide/html/index.html

Mikko  ;D

Kiitoksia, tuo tuli hyvään tarpeeseen ainakin meikäläiselle.  ;)
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: munakuutio- - 19.03.06 - klo:16.49
No nyt kävi luonnollinen reaktio eli alkoi tuo srkiptaaminen kiinostamaan. Olisko jossian olemassa jotain melko simppeliä howto/faq juttua koskien skriptaamista. Ei välttämättä mitään ohjeita vaan pelkästään että mitä se on, mihin sitä käytetään.

Ja jos kielenä olisi vielä suomi tai englanti niin sen paremi.

munakuutio-
Otsikko: Skriptaus - mitä se on
Kirjoitti: mikk0 - 22.03.06 - klo:13.46
Voin antaa hyvin yksinkertaistetun vastauksen siitä, mitä ovat shell-skriptit.

Käytännössä ne ovat vain peräkkäin laitettuja komentoja, joiden tarkoituksena on saada jokin asia tapahtumaan haluamallasi tavalla.

Unixin (ja Linuxin) filosofia on siinä, että muutaman ison ja yleiskäyttöisen ohjelman sijasta on olemassa miljuuna pientä ohjelmaa, jotka tekevät jonkin yksinkertaisen asian, mutta tekevät sen hyvin. Tästä syystä pitää monesti käyttää useampaa kuin yhtä ohjelmaa jotta päästään olemassa olevasta alkutilanteesta haluttuun lopputulokseen.

Tällaisen komentojen ketjutuksen voi kirjoittaa tiedostoksi ja näin ollen ei tarvitse joka kerta kirjoitella hirveää määrää komentoja jos sama tilanne toistuu.

Kun soppaan lisätään vielä muuttujat ja mahdollisuus muuttaa komentojen suoritusjärjestystä / suoritettavia komentoja erilaisten ehtolausekkeiden avulla, niin voidaan luoda hyvinkin monipuolisia skriptejä, jotka käyttäytyvät eri tavoin eri tilanteissa.

Esimerkkinä voisi olla vaikkapa skripti joka hakee tietyn tiedoston nettipalvelimelta wget-käskyllä ja tuon tiedoston sisällön perusteella lisää vaikkapa jonkin rivin suources.list-tiedostoon ja asentaa ohjelman tuosta juuri lisätystä lähteestä.

Toinen voisi olla kuvien lataaminen kamerasta ja niiden koon muokkaaminen automaattisesti nettijulkaisuun sopivaksi. Tämän jälkeen ne sujahtaisivat automaattisesti ftp-palvelimelle ja niille lisättäisiin linkki sinun nettisivuiltasi. (Tämä idea varastettu osin MikroBitistä  ::))

Periaate siis on, että jos voit tehdä jotain komentoriviltä, niin voit tehdä sen myös skriptin avulla.
Tuo yllä mainittu abs-guide on erinomainen lähdeteos, johon kannattaa tutustua.

Mikko

Lisäys:

Nyt kun alkuun päästiin, niin voisiko joku selventää eroa skriptin ja shell function välillä.
Otsikko: Re: Skriptaus - mitä se on
Kirjoitti: ajv - 27.04.06 - klo:09.51
Nyt kun alkuun päästiin, niin voisiko joku selventää eroa skriptin ja shell function välillä.

Funktiot suoritetaan saman shellin kontekstissa, kun skriptiä varten luodaan uusi prosessi. Elikkä kutsuttavaan funktioon näkyvät samat muuttujat, ja funktiossa määritettävät muuttujat näkyvät ulkopuolelle, ellei niitä eksplisiittisesti määritellä paikallisiksi local sanalla.

Koodia: [Valitse]
#!/bin/sh

function funkkari {
        echo $a
        b="bee"
}

a="aaa"
funkkari
echo $b
Otsikko: Re: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: pjotr - 30.04.06 - klo:20.33
Lainaus
abs-guide, eli The Advanced Bash-Scripting Guide.

Koodia: [Valitse]
sudo apt-get install abs-guide

Onkohan sama kuin tämä:

www.tldp.org/LDP/abs/abs-guide.pdf

Itse ainakin mieluummin tuhlaan paperia ja tulostan kuin tuijotan kuvaruutua.

pjotr

EDIT: No okei, jos ei pysty duunissa ilmaiseksi tulostamaan niin tuossa on 652 sivua...
Otsikko: Vs: Re: Skriptaus - mitä se on
Kirjoitti: DtW - 19.01.07 - klo:01.40
Koodia: [Valitse]
#!/bin/sh

function funkkari {

Kun skripti ajetaan /bin/sh:lla, täytyy käyttää POSIX-sh-yhteensopivaa syntaksia. Yllä on käytetty bashin funktioiden syntaksia, joten skripti ei toimi järjestelmissä, joissa /bin/sh on linkattu johonkin muuhun kuin /bin/bash-tulkkiin. Esimerkiksi Edgyssä se ei toimi. Jos ei itse ole sh:n syntaksista varma ja käyttää bashin ohjekirjoja ohjelmoinnin apuna, kannattaa skriptin alkuun kirjoittaa aina #!/bin/bash . sh-yhteensopiva syntaksi olisi seuraava (toimii toki myös bashilla):

Koodia: [Valitse]
#!/bin/sh

funkkari() { ... }
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nailor - 27.07.07 - klo:11.02
Mikään ei myöskään sido shell-skriptausta bashin käyttöön. Teoriassa tuossa voi käyttää "mitä tahansa", eli esim. python ja php toimivat myös. Itse preferoin pythonia, sen kirjoittaminen on vain niin pirusti helpompaa kuin jonkun POSIX-yhteensopivan sylkeminen :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: E.K.Virtanen - 13.08.07 - klo:20.54
Onkohan kotimaisella tehtyä bash-script opasta missään? Hieman kattavampaa tosin kuin tämä; http://linux.fi/index.php/Bash-skriptaus
Uskoisin mokomalla olevan hyvinkin kysyntää kun tuo minun "arvaa numero väliltä 1 - 100" ohjelmani ei tunnu etenevän kun koko ajan jotain käsittämätöntä probleemaa (:
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: MikkoJP - 15.08.07 - klo:17.30
Onkohan kotimaisella tehtyä bash-script opasta missään? Hieman kattavampaa tosin kuin tämä; http://linux.fi/index.php/Bash-skriptaus
Uskoisin mokomalla olevan hyvinkin kysyntää kun tuo minun "arvaa numero väliltä 1 - 100" ohjelmani ei tunnu etenevän kun koko ajan jotain käsittämätöntä probleemaa (:

En tiedä onko kattavampi vai ei:

* Mika Kousa, Komentotulkkiohjelmointi bash-shellille ( http://users.tkk.fi/~mkousa/shell-doc/shell-doc.html)

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: E.K.Virtanen - 20.08.07 - klo:20.46
Kiitoksia MikkoJP. Näitä bash yms. linkkejä voisi melkein kerätä jonnekin hieman jotta olisi helpottava kokoelma tarjolla.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ubuntus - 06.11.07 - klo:21.06
Kiitos mielenkiintoisesta oppaasta, lueskelin tuossa erästä toista mutta täytyy katsella tämäkin läpi. Tänään sain yhtäkkiä päähäni opetella tekemään skriptejä, mutta sitten tuli ongelma eteen. Miten ihmeessä voin ajaa skriptin testi.sh joka sijaitsee /home/mina/skripti kansiossa kun itse olen kotihakemiston juuressa /home/mina. Yhden ohjeen luin, missä käskettiin luoda bin kansio tuonne kotihakemiston juureen ja kopioida skriptit sinne eli siis,

pwd: /home/mina
mkdir bin
cp testi.sh ~/bin

En kuitenkaan saa ajettua skriptiä juuresta, mitäs tässä ei nyt ymmärrä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 06.11.07 - klo:21.30
Miten ihmeessä voin ajaa skriptin testi.sh joka sijaitsee /home/mina/skripti kansiossa kun itse olen kotihakemiston juuressa /home/mina.

scriptillä pitää olla suoritusoikeudet ja sen pitää joko sijaita hakupolun varrella tai siihen pitää osoittaa joko absoluuttista tai suhteellista polkua käyttäen.

absoluuttinen polku:
Koodia: [Valitse]
$ /home/mina/skripti
suhteellinen polku mistä tahansa (~ osoittaa käyttäjän omaan kotihakemistoon):
Koodia: [Valitse]
$ ~/skripti
suhteellinen polku kotihakemistosta (. osoittaa nykyiseen hakemistoon):
Koodia: [Valitse]
$ ./skripti
suhteellinen polku hakemistosta /home/minä/Desktop (.. osoittaa yhtä pykälää lähempä juurta olevaan hakemistoon):
Koodia: [Valitse]
$ ../skripti
jne.

Yhden ohjeen luin, missä käskettiin luoda bin kansio tuonne kotihakemiston juureen ja kopioida skriptit sinne eli siis,

pwd: /home/mina
mkdir bin
cp testi.sh ~/bin

En kuitenkaan saa ajettua skriptiä juuresta, mitäs tässä ei nyt ymmärrä.

tuo ~/bin on kätevä tapa keskittää scriptit yhteen hakemistoon, mutta sitä käytettäessä pitää tietty varmistaa, että hakemisto on hakupolun varrella. sitä varten tiedostossa ~/.bash_profile on kohta:
Koodia: [Valitse]
# set PATH so it includes user's private bin if it exists
if [ -d ~/bin ] ; then
    PATH=~/bin:"${PATH}"
fi

mutta ko. scriptiä ei lueta kuin sisään kirjautuessa, ellei niin käsketä erikseen tekemään, joten sessio jossa luot hakemiston ~/bin ei sitä vielä automaattisesti tunnista.

voit tarkistaa mitä hakemistoja polussasi on, komennolla:
Koodia: [Valitse]
$ echo $PATH
ja voit lukea .bash_profilen kyseiseen terminaali-istuntoosi komennolla:
Koodia: [Valitse]
$ source ~/.bash_profile
ja muista tosiaan ne suoritusoikeudet ;)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: raimo - 06.11.07 - klo:21.44
ja muista tosiaan ne suoritusoikeudet ;)

Selvennän hiukan lisää, kun kerta kirjoitin jo pitkät pätkät, mutta jannella on edelleen nopeammat sormet!!  :D

Suoritusoikeudet saa vaikkapa näin:
Koodia: [Valitse]
chmod 755 ~/bin/skripti
ja yllättäen skriptin voi ajaa vaikka sillä ei olisikaan suoritusoikeuksia:
Koodia: [Valitse]
sh ~/bin/skripti
mutta tuossa ei sinänsä ole järkeä, eli aseta vaan ne suoritusoikeudet. ;)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ubuntus - 07.11.07 - klo:08.01
Kiitokset Jannelle ja raimolle. Miten sitä ei eilen tajunnut että kone täytyy käynnistää uudelleen niin että tuo kotihakemiston /bin löytyy  ;D.  Kummallekkin hatunnosto ja syvä kumarrus, pääsee taas hieman eteenpäin tässä harjoittelussa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: MikkoJP - 03.01.08 - klo:16.28
Miten sitä ei eilen tajunnut että kone täytyy käynnistää uudelleen niin että tuo kotihakemiston /bin löytyy  ;D. 

Eikös pelkkä komentotulkin käynnistäminen riitä?? Siis kirjoittamalla komentotulkissa
Koodia: [Valitse]
bash tai avaamalla uusi pääteikkuna...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: larpa - 25.02.08 - klo:07.30
Tuolta löytyy myös parit hyvät linkit aiheesta.
http://unix-kurssi.oamk.fi/
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Rautamiekka - 26.05.08 - klo:01.40
Innostuin shell scriptauksesta :D Epäselvää on if-lauseen käyttö, että miksi pitää käyttää hakasulkuja sekä mitä tarkoittaa epälooginen tekstipläjäys tyyliin
Koodia: [Valitse]
if [ "$t" -ne $DUPE_CARD ] ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: dhc7 - 12.06.08 - klo:19.51
Pistetäänpäs ketjun jatkoksi kysymystä.

Yritin väsätä skriptiä, joka ensin muuttaa jonkin hakemiston sisältämien tiedostojen omistajan samaksi ja sitten siirtää fileet toiseen hakemistoon. Kummankin käskyn edessä on sudo. Toimii hienosti, jos ajan skriptin päätteeltä. Mutta kuinkas tuon saisi toimimaan myös valikon kautta (skripti on hakemistossa .gnome2/nautilus-scripts) niin, että hiiren oikealla saisi menusta tuon ajettua? Pitäisi se salasana päästä antamaan, joten yritin moista lisäämällä skriptiin rivin exec gnome-terminal joka kyllä avaa päätteen mutta homma jää siihen.

Kokeilin myös muotoa (tässä vain omistajan vaihto):
Koodia: [Valitse]
#!/bin/bash
exec gnome-terminal --command="sudo chown user2:user2 /home/user1/test/*"


Onko tässä nyt vain joku pikku jippo vai vaatiiko moinen homma jo rivikaupalla lisää koodia? Nyt en niinkään hae vaihtoehtoisia tapoja tehdä haluamaani asiaa (voin kyllä ihan hyvin ajaa skriptin päätteeltä), vaan juttu kiinnostaa noin yleisesti.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Tomin - 12.06.08 - klo:19.59
Eikös kannattaisi sitten ajaa se koko skripti gksu:lla? ::)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: dhc7 - 12.06.08 - klo:20.03
Eikös kannattaisi sitten ajaa se koko skripti gksu:lla? ::)

Niin se varmaan toimisi, mutta kuinka teen sen sieltä valikosta käsin?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 12.06.08 - klo:20.15
Innostuin shell scriptauksesta :D Epäselvää on if-lauseen käyttö, että miksi pitää käyttää hakasulkuja sekä mitä tarkoittaa epälooginen tekstipläjäys tyyliin
Koodia: [Valitse]
if [ "$t" -ne $DUPE_CARD ] ?

tämä on vähän vanha, mutta vastataan silti...

siis tuo if ei itsessään ymmärrä merkkijonoista mitään, vaan tekee päätöksensä sille annettujen lukuarvojen perusteella. tuo [ puolestaan on testioperaattori jonka avulla erilaisia ehtoja pystyy arvioimaan ja joka palauttaa arvioinnin tuloksen if:n ymmärtämässä muodossa.

if osaa arvioida esim. suoritettujen komentojen paluuarvoja, joten seuraava luonnollisesti onnistuu:
Koodia: [Valitse]
if `/bin/true`
then
    echo "totta"
fi

tuo epälooginen tekstipläjäys puolestaan on se mitä siinä arvioidaan. nuo $-merkillä alkavat ovat muuttujia ja tuo -ne siinä välissä vertailuoperaattori (http://tldp.org/LDP/abs/html/comparison-ops.html).
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: dhc7 - 13.06.08 - klo:19.22
Eikös kannattaisi sitten ajaa se koko skripti gksu:lla? ::)

Niin se varmaan toimisi, mutta kuinka teen sen sieltä valikosta käsin?

Näköjään onnistuu, jos muuttaa skriptin sisäiset sudoilut gksudoiksi. Silloin pyydetään salasanaa ja sudoilut onnistuvat. Mutta edelleenkään homma ei toimi kuten yritin, koska pääte ei ole auki suorituksen aikana. Tarkoituksena olisi näyttää joitakin suoritukseen liittyviä tietoja päätteeseen.

Mutta taitaa olla vähän hankalampi juttu, ainakin näin olen muita vastaavia ihmettelyjä lukiessa todennut.

Muoks: Korjaus.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: OH2FPG - 12.08.09 - klo:17.12
Moi,
Onko kenelläkään tietoa miten saisin avattua nettisivun Bash-skriptauksella?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: gdm - 12.08.09 - klo:17.16
Moi,
Onko kenelläkään tietoa miten saisin avattua nettisivun Bash-skriptauksella?

Koodia: [Valitse]
#!/bin/bash
x-www-browser http://haluttu.sivu
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: peran - 13.08.09 - klo:01.50
Mikäli riittää ladata html-koodit sivulta, muttei sivun kuvia ja muita, niin silloin riittää wget.

Koodia: [Valitse]
wget http://forum.ubuntu-fi.org
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: raimo - 13.08.09 - klo:17.08
Koodia: [Valitse]
#!/bin/bash
x-www-browser http://haluttu.sivu

Joo, mutta sh on huomattavasti kevyempi/nopeampi yms. kuin bash, eli
Koodia: [Valitse]
#!/bin/sh
x-www-browser http://haluttu.sivu

No ei mulla muuta asiaa sit ollukkaan, eikä tuokaan asia nyt ole maita kaatava, mutta kuitenkin...  ;D
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: mgronber - 13.08.09 - klo:17.20
Joo, mutta sh on huomattavasti kevyempi/nopeampi yms. kuin bash, eli

Suurimmassa osassa Linux-jakeluita /bin/sh on symbolisesti linkattu /bin/bash. Periaatteessa ajatus on kuitenkin oikea.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jkorhonen - 15.11.09 - klo:13.26
Englanninkielisiä ohjesivustoja on paljon, mutta haluaisin tuoda esiin yhden jota olen käyttänyt niin bash kuin myös windows skriptauksissa. Tämän sivuston huomasin itse käteväksi, kun olen joskus tehnyt monia skriptejä windowsin puolella ja halusin opetella vastaavien skriptien tekoa myös bashin puolella.

http://ss64.com/bash/
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 28.03.10 - klo:10.29
Olen aina halunnut käyttööni kunnollista funktiokirjastoa mielellään BASH-funktioista; esimerkkejähän on internetti täynnä, mutta niiden etsimisessä on aikamoinen homma ja useimmiten ne soveltuvat sinällään vain johonkin erikoistapaukseen. Ihmeekseeni BASHista löytyy funktio-kirjastojen liitoskäsky (ohjelman alkuun rivi (huomaa piste alussa): . funktiokirjaston_nimi_polkuineen ) joten löydettyäni sen aloin himoita valmiita kirjastoja (saavat jopa maksaa vähäsen). Tietääkö joku mistä sellaisia saisi ?   
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: cabaro - 06.11.10 - klo:21.25
Joo, mutta sh on huomattavasti kevyempi/nopeampi yms. kuin bash, eli

Suurimmassa osassa Linux-jakeluita /bin/sh on symbolisesti linkattu /bin/bash. Periaatteessa ajatus on kuitenkin oikea.


Ubuntussa tuo taitaapi kuitenkin olla /bin/sh -> /bin/dash

$> ls -la /bin/sh
lrwxrwxrwx 1 root root 4 2010-10-30 14:13 /bin/sh -> dash

/cab
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.12.10 - klo:17.07
Rullahiirellä ohjattava Valikko:

Koodia: [Valitse]
#!/bin/bash   
# Valikko_funktiot rullahiirelle   16.5.2012 petteriIII
# Valikko_funktion parametrit: Otsikko1=koko 2. rivi. Jos se on annettu sitä käytetään mutta jos ei ole niin Otsikko1 muodostetaan automaattisesti. Otsikko1:stä otetaan CRC-32 ja se yksilöikin kutsujan hyvin. Ulkopuolinen ohjelma saattaa käyttää sitä. Otsikko2=merkit 38-157 rivillä 1.
# muu "dokumentointi" on skriptissä nimeltä: Valikonesittely.

function AsetaPaate {  #kbdrate -r 40 80 # -> X environment: xset r rate 240 60 # paketti wmctrl ?
#reset # varmistetaan että aloitetaan puhtaalta pöydältä; toimii paremmin skriptiajurissa && sleep .1
RivienLuku=$(($(tput lines)-5)) # näytön riviluku. 5 on otsikon+yläpalkin viemä tila
RivinPituus=$(tput cols)        # näyttörivin merkkiluku.
echo 'Näyttö-funktio kirjastoa kutsuva ohjelma: ' $(basename $0)'  Riviluku:'$RivienLuku'   rivinpituus:'$RivinPituus
gconftool -s /apps/gnome-terminal/profiles/Default/title --type string $(basename $0)
apu=$(gconftool -g /apps/gnome-terminal/profiles/Default/default_size_columns)
gconftool -s /apps/gnome-terminal/profiles/Default/use_custom_default_size --type bool true
gconftool -s /apps/gnome-terminal/profiles/Default/default_size_columns --type integer 240 # Näyttörivin maximi pituus
gconftool -s /apps/gnome-terminal/profiles/Default/default_size_rows --type integer 60     # Näyttörivien maximi määrä
gconftool -s /apps/gnome-terminal/profiles/Default/use_theme_colors --type bool false
gconftool -s /apps/gnome-terminal/profiles/Default/scrollback_unlimited --type bool true
gconftool -s /apps/gnome-terminal/profiles/Default/default_show_menubar --type bool true   # tulee voimaan vasta toisella ajokerralla
gconftool -s /apps/gedit-2/preferences/editor/line_numbers/display_line_numbers --type bool true
raja=112\101\116\116\101\114\105\73\73\73
[[ ! "$apu" = 240 ]] && killall gnome-terminal
}

function tilastoa {
tput cup 0 0; tput setf 3
apu=$kanta/"$KansionNimi"/'ARKISTO/skriptit/'"${Valittavat[ValitunNumero-1]}"
[[ -d "$apu" ]] && [[ $(ls "$apu") ]] && echo -n "Editointikopioita:"$(ls "$apu" | sort -g | tail -n 1) || echo -n '    editointikopioita ei ole'
apu=$kanta/"$KansionNimi"/'ARKISTO/dokumentit/'"${Valittavat[ValitunNumero-1]}"
[[ -d "$apu" ]] && [[ $(ls "$apu") ]] && echo -n "  Dokumenttikopioita:"$(ls "$apu" | sort -g | tail -n 1) || echo -n '    dokumenttikopioita  ei ole'
apu=$kanta/"$KansionNimi"/'ARKISTO/historiat/'*
[[ -d "$apu" ]] && [[ $(ls "$apu") ]] && echo -n "  historiatietoja: "$(wc -l $apu | awk {'print $1'}) | tail -1 || echo -n '    historiatietoja ei ole'
echo -n "      paina enter jatkaakseesi "; read  #
tput cup 0 0; echo -en "\r\033[K$Otsikko2"
#key=''; echo 'näpäytä hiiren vasenta jatkaakseesi'; until [[ ${key:3:1} = "#" ]]; do echo -en "\e[?1000h"; read -n6 -s -t0.02 key ; echo -en "\e[?1001h"; done
}

 
function MuutaValittuPunaiseksi {
tput cup 2 $(($RivinPituus-45)) 
apu=$kanta/$KansionNimi/'ARKISTO/skriptit/'"${Valittavat[ValitunNumero-1]}"
tput setf 3;echo "näpäytä_tästä_käyttöohjeet    erikoistoimet" #Editointeja:"$(ls "$apu" | sort -g | tail -n 1)"                   "
#tput cup 0 0; cat $kanta/$KansionNimi/"${Valittavat[ValitunNumero-1]}"  | head -2 # esimerkiksi tutkittaessa historioita
Xkoordinaatti=$(((ValitunNumero - 1 - $(((ValitunNumero - 1) / RivienLuku))) / RivienLuku)); Ykoordinaatti=$((ValitunNumero - $((RivienLuku * Xkoordinaatti -1))+3));tput cup $((Ykoordinaatti - Xkoordinaatti - 2)) $((Xkoordinaatti * SarakkeenPituus));  tput setf 4; echo "${Valittavat[ValitunNumero-1])}"
}

function MuutaValittuVihreaksi {
  Xkoordinaatti=$(((ValitunNumero - 1 - $(((ValitunNumero - 1) / RivienLuku))) / RivienLuku)); Ykoordinaatti=$((ValitunNumero - $((RivienLuku * Xkoordinaatti - 1))+3));tput cup $((Ykoordinaatti - Xkoordinaatti - 2)) $((Xkoordinaatti * SarakkeenPituus));  tput setf 3; echo "${Valittavat[ValitunNumero-1])}"
}

function TulostaValittavat {
clear
tput setf 3
echo -e $Otsikko2'\n'$Otsikko1'\n'                     
ValittavienLuku=${#Valittavat[@]}
SarakkeidenLuku=$(($((ValittavienLuku-2))/RivienLuku+1))
SarakkeenPituus=$((RivinPituus/SarakkeidenLuku))
for (( Rivi=0; Rivi<=RivienLuku; Rivi++ )) 
do
  nayttoRivi='' # koe, ettei nayttoRivi olisi koskaan määrittelemätön
  for (( Sarake=0; Sarake<=SarakkeidenLuku-1; Sarake++ ))   
  do   
    naytettava=${Valittavat[Sarake*RivienLuku+Sarake+Rivi]}"                                                                                           "
    nayttoRivi="$nayttoRivi"${naytettava:0:$SarakkeenPituus}  # mitä sarakkeita näyttörivillä ennestään onkin ja uusi sarake lisää
  done
  echo "${nayttoRivi:0:$RivinPituus}"; nayttoRivi=''
done 
}

function MuutaValittuNuolinappaimilla {
MuutaValittuPunaiseksi
 echo -en "\e[?1000h"; read -n6 -s -t0.02 key ; echo -en "\e[?1001h"
stty -echo
if [[ ${key:3:1} = "a" ]]; then MuutaValittua -1; fi  # rullaus ylös
if [[ ${key:3:1} = "\`" ]]; then MuutaValittua 1; fi  # rullaus alas
if [[ ${key:3:1} = "!" ]]; then tilastoa; fi          # hiiren rullan näpäytys
if [[ ${key:3:1} = "#" ]]; then valintatehty=1; x=$(LC_CTYPE=C printf '%d' "'${key:4:1}"); y=$(LC_CTYPE=C printf '%d' "'${key:5:1}")
  [[ $y = 35 ]] && [[ $x > $(($RivinPituus+15)) ]] && Tapahtuma=erikoistoimet && valintatehty=1 && return
  [[ $y = 35 ]] && [[ $x > $(($RivinPituus-12)) ]] && Tapahtuma=käyttöohjeet && valintatehty=1 && return
  [[ $y > 35 ]] && MuutaValittuVihreaksi && ValitunNumero=$((y-35+((x-33)*(SarakkeidenLuku))/RivinPituus*(RivienLuku+1))) && MuutaValittuPunaiseksi && return
valintatehty=0
fi                                                         
}

function MuutaValittua () {
MuutaValittuVihreaksi
ValitunNumero=$(($ValitunNumero+$1))
[[ $ValitunNumero -lt 1 ]] && TulostaValittavat && ValitunNumero=1 # TulostaValittavat lisätty
[[ $ValitunNumero -gt $ValittavienLuku ]] && ValitunNumero=$ValittavienLuku # apu->ValittavienLuku; -2 lisätty poistettu
}
 
function ValitseValittavistaYksi () {
[[ ${#Valittavat} == 0 ]] && echo 'ei ole valittavaa' && sleep 2 && valintatehty=1 && break
AsetaPaate
local Otsikko1 Otsikko2 key valintatehty=0 Xkoordinaatti Ykoordinaatti   
[[ ! $ValitunNumero || $ValitunNumero -lt 1 || $ValitunNumero -gt ${#Valittavat[@]} ]] && ValitunNumero=1
setterm -cursor off 
Otsikko1="$1"; [[ ! "$Otsikko1" ]] && Otsikko1='Ohjelma: '$0' pyytää valitsemaan näistä'
Otsikko2="$2"
crc=$(echo $Otsikko1 | cksum | awk {'print $1'}) # cksum=32bittinen crc, sha1sum ->160bittinen ...
TulostaValittavat
valintatehty=0
Tapahtuma=""
while [ $valintatehty -lt 1 ] ; do MuutaValittuNuolinappaimilla;  done
tput setf 0; setterm -reset; stty echo
ValitunTeksti=${Valittavat[ValitunNumero-1]}
[[ ! ${ValittavanArvo[ValitunNumero-1]} ]] && ValitunArvo='ei määritelty' || ValitunArvo=${ValittavanArvo[ValitunNumero-1]}
}

function lue_näppäimistöltä () {
# Tämä rutiini tuo sen että vanhoja rivejä voi selata ja riviä voi editoida. Historiatiedosto on kansio- ja kysyttävän muutujan nimi-kohtainen.
# kutsu: lue_näppäimistöltä <kysyttävän muuttujan nimi> <kysymysteksti jonka merkkivalikoimaa ei ole rajoitettu>
historia_tiedoston_nimi="$2" # "$2" oli $1
historiatiedosto=$kanta/"$KansionNimi"/ARKISTO/historiat/"$historia_tiedoston_nimi"
[[ ! -d $kanta/"$KansionNimi"/ARKISTO/historiat ]] && mkdir -p $kanta/"$KansionNimi"/ARKISTO/historiat 
[[ ! -f "$historiatiedosto" ]] && echo 'historian alku' > "$historiatiedosto"   # kysymykselle luodaan kysymyksen tekstin mukainen historia-tiedosto
apu=$(eval echo \$$1)
[[ "$apu" != $(tail -1  "$historiatiedosto") ]] &&  echo "$apu" >>  "$historiatiedosto" # kysytty arvo viedään heti historiaan josta sitä voi siis kutsua jo ensimmäisellä kerralla # koe lisäys
history -r  "$historiatiedosto"
echo -e "\nEdellisiä vastauksia voi selailla nuolinäppäimillä. Näytöä voi vierittää hiirellä. Myös editointi toimii. Kirjoita vaikkei kysymysmerkkiä näykään\n\n"$2
read -e $1 # apu oli 1 myös seuraavassa lauseessa
eval echo \$$1 >>  "$historiatiedosto"
}
 
function muuta_uusi_nimi_valituksi {
täytä_valittavat $kanta/"$KansionNimi"
for (( n=1; n<=${#Valittavat[@]}; n++ )); do [[ $SkriptinNimi = ${Valittavat[n-1]} ]] && break; done
echo $n > $kanta/"$KansionNimi"/ARKISTO/ViimeksiValittuSkriptiTässäKansiossa # kokeiltava toimiiko
}

function täytä_valittavat () {
kansio=$@ # kun listattavaksi tulee kansio jonka nimessä on välilyöntejä siirtyy jokainen sana omassa parametrissaan ja ne täytyy liittää yhteen
find "$kansio" -size 0b -delete; find "$kansio" -type f -name \*~ -delete; find "$kansio" -type f -name .~lock* -delete; find "$kansio" -type f -name delme -delete
eval Valittavat=(".edellinen_kansio." " "     $(ls "$kansio" -Q --group-directories-first))
}

function command_not_found_handle () {
# tämännimistä funktiota kutsutaan käyttöjärjestelmän toimesta automaattisesti mikäli skriptissä viitataan käskyyn jollaista ei löydy.
komennon_nimi=$1
shift
echo "ohjelma:"${BASH_SOURCE}" Rivino:"${BASH_LINENO}
echo "seuraavaa käskyä ei löydy: \""$komennon_nimi\"""
echo "käskyn argumentit ovat: ""$@"
echo
kaskysta_on_puhuttu_paketeissa=$(apt-cache search $komennon_nimi)
[[ $(echo $kaskysta_on_puhuttu_paketeissa | grep ^$1) ]] && paketin_nimi=$(echo $kaskysta_on_puhuttu_paketeissa | grep ^$1) ||
paketin_nimi= $(echo $kaskysta_on_puhuttu_paketeissa | awk {'print $1'})
echo " paketin määrittelyä: "$paketin_nimi
sudo apt-get install ${komennon_nimi} || echo "siitä on puhuttu paketeissa: "$kaskysta_on_puhuttu_paketeissa
exit
}
 

 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 06.01.11 - klo:17.23
Lukaisin pikaisesti tuota läpi ja näytti etenevän ok mutta yksi kohta ei auennut:
Lainaus
....Sitten paina näytön toiseksi ylimmältä riviltä merkkiä x...
kun ei osunut silmään missään mikä tuo "x" voisi olla. 

Toki sen asian löytää kun klikkaa hiiren oikealla tuota harjoitustiedostoa, valitsee "Ominaisuudet" ja sieltä "Oikeudet" välilehti, ruksia kohtaan "Salli tiedoston suoritus ohjelmana".
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.01.11 - klo:09.55
Lukaisin pikaisesti tuota läpi ja näytti etenevän ok mutta yksi kohta ei auennut:
Lainaus
....Sitten paina näytön toiseksi ylimmältä riviltä merkkiä x...
kun ei osunut silmään missään mikä tuo "x" voisi olla. 


Taisi siis selvitä jo: kyseessä on se ohjelman lopetusnappi ylärivillä - se josta kiistellään pitäisikö se sijoittaa oikeaan vai vasempaan reunaan. Enemmän Ubuntua käyttäneet ymmärtäisivät ilmanmuuta paremmin jos sanoisi vain: sulje ohjelma.

Yritin mahdotonta, ei tuo tosiaan ollut selvästi esitetty. Syy yritykseen oli se, että Linux on mielestäni turvonnut sotku joka kenties lakkaisi turpoamasta jos suosittaisiin alkeellisia menetelmiä niinkuin esimerkiksi BASH ohjelmointia.

Siis tarkemmin: BASH on alkeellinen normaalissa ohjelmoinnissa. Vaan eipä sitä ole normaaliin ohjelmointiin tarkoitettukaan.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 07.01.11 - klo:12.51
Lainaus
jos suosittaisiin alkeellisia menetelmiä niinkuin esimerkiksi BASH ohjelmointia.
Oikeastaan noita BASH-ohjelmia on Linux jakeluissa aika paljon jo perusjärjestelmässä. 
Jo muutamilla riveillä saa joskus ihmeen paljon aikaan mutta kirjoittaminen isommassa määrin pelkän BASH: in avulla voi olla tuskallista verrattuna johonkin ohjelmointikieleen.  Tuntuisi että pelkkä peräkkäin suoritettavien tehtävien automatisointi sujuu tuolla helposti tai myöskin vaikeasti muistettavien komentosarjojen piilottaminen helpompaan muotoon yhden komennon taakse.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 07.01.11 - klo:13.50
Taisi siis selvitä jo: kyseessä on se ohjelman lopetusnappi ylärivillä - se josta kiistellään pitäisikö se sijoittaa oikeaan vai vasempaan reunaan. Enemmän Ubuntua käyttäneet ymmärtäisivät ilmanmuuta paremmin jos sanoisi vain: sulje ohjelma.
Eiköhän ole ihan kohtuullista olettaa että scriptien kirjoittamisesta kiinnostunut, aloittelijakin, tietää mistä ikkunat suljetaan? ::) Rautalankaohjeeseen sopisi paremmin liittää kuva joka vaiheesta, kuin yrittää kuvailla mitä milloinkin näkyy.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 17.01.11 - klo:19.20
Linuxissa jokainen näppäimistö on ohjelmoitava. Esimerkiksi nappulat f2-f12-ins-del ja monet muutkin erikoiskoodin näppäimet voit ohjelmoida seuraavasti:
- f1 on ongelmallinen sillä pääte omii sen ohjeiden näyttämiseen - saattaa siis toimia tai sitten ei.

1. anna päättessä käsky: cat ja paina enter.
2. paina haluamaasi erikoisnappia ja katso mitä näytölle tulee. Esimerkiksi nappula f12 aiheuttaa näytölle koodin: ^[[24~
3. käännä mielessäsi: ^[ on \e
4. avaa toinen pääte ja anna käsky:
bind '"\e[24~":"kirjoita tähän minkä tekstin haluat kirjoittuvan"' . Lopuksi paina enter. Huomaa nuo kummalliset lainausmerkki-kokoelmat. Leikkaa-liimaa on helpointa.

- nyt painaessasi nappulaa f12 tulostuu tuo teksi
- tämä ei toimi boottauksen yli noinvain vaan tuo käsky täytyy kirjoittaa esimerkiksi tiedostoon .bashrc  - vaikkapa sen loppuun.
- voit ohjelmoida niin monta nappulaa kuin haluat.
- toimiipa sekin että esimerkiksi kirjoitettuasi viestin jollekulle joka on kanssasi samassa koneessa haluatkin ennen viestin lähettämistä varmistua siitä että hän on paikalla. Mikäli ~/bashrc:ssä on käsky: bind -x '"\eU":"users"' niin painettuasi alt-shift-u tulostuvat käyttäjät ja kirjoittamasi viesti perään.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 17.01.11 - klo:21.42
Pieni "a" se pitäisi olla ja vähän muutakin perässä mutta ei tuo ole pakollinen, täällä on jokin esimerkki:

http://tldp.org/LDP/abs/html/arrays.html (http://tldp.org/LDP/abs/html/arrays.html)

Tuo kirjain tulee sanasta "array", olisikohan tuo lähinnä yksiulotteinen taulukko tai numeroiden sarja suomeksi.  Tuo koodisi idea on itselleni hämärä monestakin kohdasta jos tarkoituksena on vain laskea kahden luvun tulo ja palauttaa sen arvo.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 18.01.11 - klo:19.23
#!/bin/bash
declare -A Kertotaulu # Miksi tämä ilmoitus vaaditaan ja miksi 'parametrin' pitää olla iso A ? Onko skripti muutenkaan OK ?            
for (( apu1=10; apu1>=1; apu1-- )); do
  for (( apu2=1; apu2<=10; apu2++ )); do
    Kertotaulu[$apu1,$apu2]=$(echo $apu1*$apu2 | bc )

Hyödynnät tuossa Bash 4:n assosiatiivista taulukkoa, joka syntyy juuri declare -A:lla. Asetat siis taulukkoon kertolaskun x*y tulon avainmerkkijonolla "x,y". Tämä toimii periaatteessa, mutta tehokkaampaa lienee simuloida kaksiulotteista taulukkoa yksiulotteisella. Siis kertotauluesimerkin tapauksessa:

Koodia: [Valitse]
#!/bin/bash

koko=100
declare -a kertotaulu

for (( i=1; i<=$koko; i++ )); do
    for (( j=1; j<=$koko; j++ )); do
        let "n = $i * $koko + $j"
        let "kertotaulu[$n] = $i * $j"
    done
done
let "n = 7 * koko + 8"
echo ${kertotaulu[$n]}

Kokeilin molempia 300x300 -taululla ja normaalia taulukkorakennetta käyttävä versio tarvitsi puolet vähemmän aikaa ja tilaa. Lisäksi sen aikavaativuus suhteessa taulukon kokoon on lineaarinen, kun assosiatiivisella taulukolla aikavaativuus näyttää olevan O(n^2). Se on kaukana optimaalisesta, ja isot assosiatiiviset taulukot ovatkin bashilla käyttökelvottoman hitaita.

Vertailun vuoksi miljoonan kokonaislukualkion (1000x1000) kertotaulun luonti bash-skriptillä kesti yli minuutin ja käännetyllä C-ohjelmalla 10 millisekuntia. Tai noin 1 ms ilman ohjelman käynnistymisestä aiheutuvaa viivettä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.01.11 - klo:20.48
Nämä on esitetty koodina jotta tekstin muotoilu säilyisi
Koodia: [Valitse]
Linuxin haku on monipuolinen ja senvuoksi vaikea hallita. Sen käyttö helpottuu kun määrää ettei se tulkitse erikoismerkkejä. Hakea voi vaikka järjestelmätiedostoista
mutta etsintää kyllä helpottaa jos etsit omasta kotikansiostasi. Sen käyttö on seuraavanlaista kun etsitään jotakin tiedostoissa olevasta tekstistä:

grep -hrF etsittävä mistä_etsitään   Antaa tuloksenaan niiden tiedostojen nimien joukon joista etsitty löytyy. Esimerkiksi kun haetaan merkkiryhmää: [[ $1 ]] niin
                                     tämä etsintä määrätään yksinkertaisesti :  grep -hrF '[[ $1 ]]' /home/käyttäjänimi  . Rajoittamattomana hakuna sama on:  
                                     grep -hrE '\[\[\ \$1\ \]\]' /home/käyttäjänimi .
grep -lrF etsittävä mistä_etsitään   Antaa tuloksenaan niiden lauseiden joukon joista etsitty löytyy
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Rajoittamatonta hakua on pakko käyttää kun tarvitaan joustavuutta; silloin hakusanoissa olevat erikoismerkkit tulkitaan kuten Linux ne tulkitsisi ellei niiden edessä ole merkkiä \ . Esimerkiksi $ on muuttujan edessä, * ja . tulkitaan jokerimerkeiksi jne. Kytkimestä poistetaan kytkin F, muuten toiminta on sama.
          
Hakusanan Määrittelyjä:
'viisi numeroa'    kun etsittävässä on välilyöntejä laitetaan se heittomerkkien väliin
'. tr .'           kun haluat painottaa että ennen etsittävää on välilyönti ja perässä myös; tässä etsitään käskyä tr
grep.*lr           kun etsittävän osia erottaa tuntematon määrä merkkejä ( .* paikalla ei täydy välttämättä olla mitään. Etsittävien tulee olla samalla rivillä)
^grep              kun halutaamn painottaa että etsittävä on rivin alussa
kolmio.$           kun halutaan painottaa että etsittävä on rivin lopussa  ( siis ^$ on tyhjä rivi )
[abc]-ajokortti    kun halutaan löytää kaikki paikat joissa puhutaan a-,b- tai c-ajokorteista, mutta e-ajokortit saa jäädä löytymättä
[a-e]-ajokortti    kun halutaan löytää ne kaikki
\$dollari          kun etsittävässä on erikois-merkityksen omaava merkki jolla kuitenkin on normaali aakkos-merkitys niin kun sen eteen kirjoitetaan \
'[0-9]\{5\}'       kun etsitään sanaa jossa on viisi numeroa (numerossa on merkit 0-9; siis etumerkistä ei välitetä)
[\ e]grep          kun halutaan löytää grep ja egrep, mutta muita grepejä ei
 
haettavien erikoismäärityksiä (merkkiluokkasyntaksi ?):
[:alnum:]->[A-Za-z0-9]. Yleensä nämä täytyy laittaa hakusanassa kaksinkertaisiin sulkuihin: grep [[:digit:]] test.file
[:alpha:]->[A-Za-z]     [:blank:]->välilyönti tai TAB    [:cntrl:]->jokin kontrollimerkki     [:digit:]->[0-9]     [:graph:]->ASCII 33 - 126.
[:lower:]->[a-z]    [:print:]->ASCII 32 - 126 ja lisäksi välilyönti    [:space:]->välilyönti ja 'pystysuora TAB'   [:upper:]->[A-Z]
[:xdigit:]->[0-9A-Fa-f].

pilkku menettää erikoismerkityksensä hakusanan viimeisenä merkkinä.
^ menettää erikoismerkityksensä ellei se ole ensimmäinen merkki
useimmat erikoismerkit menettävät erikoismerkityksensä merkkien [] välissä.

grepin kytkimet:
-r  etsitään rekursiivisesti                          -s  ei anneta varoitusviestejä                      -c  vain löytöjen lukumäärä tulostetaan
-h  tulostaa sen tekstirivin jolta etsitty löytyy     -w  oleta etsittävän alkuun ja loppuun \< ja \>     -v  poista hakutuloksista ne joissa etsitty on osana  
-l  tulostaa sen tiedoston nimen josta etsitty löytyy -n  tulostaa myös miltä riviltä etsitty löytyi      -i etsittäessä ei huomioida kirjainkokoa    
-F  etsitään kirjaimiltaan juuri sellaista mikä etsittäväksi määrättiin
-E  ota käyttöön laajennoksia, mm. \ ei tarvita; siis esimerkiksi kun etsit "placeholdereita" niin etsittävä on: grep -lrE {} mistä_etsittään
- jostain syystä kun etsii ilmaisua: -exec kirjoitetaan hakusana: \\-exec

jälkihuomautuksia:
- usein tuloksia tulee niin paljon että niistä olisi kiva poistaa turhia. Esimerkiksi ensimmäisen haun perään voi lisätä käskyn poistaa tuloksista binääri- ja piilotiedostot: | grep -vE '(binääri|.*)'
- haut ovat yleensä tosinopeita, mutta jotkut haut saattaa kestää pitkään kuten esimerkiksi haku:  grep -hr '[0-9]\{24\}' hakupaikka    
- kun etsit löytyykö joku ilmaisu skriptien nimistä niin käsky onkin: find mistä |  grep "mikä sana löydetyissä pitäisi olla"
                                                    siis esimerkiksi: find /home |  grep "haku"                
- en näitä hakuja oikein hallitse ja keskenkin tämä on - ilmanmuuta ohjeessa on paranneltavaa monessakin asiassa
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 19.01.11 - klo:12.16
Opinpa minäkin uuden asian, tämän takia täällä foorumilla on hauskaa käydä ! 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 25.01.11 - klo:09.15
Lainaus
Kohdat 1&2 on esitetty vastauksessa #36. Ilman niitä ei tämä skripti toimi.
Asian vierestä, jos haluat helpottaa lukijan vaivaa niin kannattaa laittaa linkki joka hyppää suoraan tuohon vastaukseen #36 jolloin lukija ei joudu pyörittelemään tekstiä tyyliin "missä ihmeessä tuo #36 nyt olikaan".  Viittauksista, tyhmempi lukija (=minä) ei välttämättä heti oivalla minne viittaa "kohdat 1&2" ja "niitä".  No, se on tietty lukijan ongelma mutta selkokielinen / helposti seurattava viesti menee paremmin perille.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.01.11 - klo:11.49
... jos haluat helpottaa lukijan vaivaa niin kannattaa laittaa linkki joka hyppää suoraan tuohon vastaukseen #36 jolloin lukija ei joudu pyörittelemään tekstiä tyyliin "missä ihmeessä tuo #36 nyt olikaan"....

Kiitos huomautuksesta, linkki on nyt tehty - ei kai se sääntöjen mukaan tullut tehtyä, mutta tuntuu toimivan.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 02.02.11 - klo:13.35
Silmäsin hieman tuota scriptiäsi, en ole varsinainen "skriptaaja" vaikka olenkin satunnaisesti tuota tehnyt.   Itse olen mieltynyt eräänlaiseen rakenteellisuuteen eli erottamaan näytölle tulostamisen ja funktiot toisistaan niin että funktiot tekevät jotain operaatioita ja palauttavat arvoja mutta pääohjelma tekee kutsujen jälkeisen käsittelyn ja näytölle tulostamisen.  Hyödyllistä tai ei, joku koodaaja voisi ehkä kommentoida paremmin.

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.02.11 - klo:16.42
Täytyy myöntää että ali-dokumentoituja ne ovat, ja raskaasti. Varsinkin jos ottaa huomioon missä mielessä koodi on esitetty. Täytyy tapella itseni kanssa, eiköhän dokumentointia tule lisää.
- Selvitys: olin kauan sitten ohjelmoija ja opin inhoamaan yli-dokumentointia koska sellaisen koodin hyödyntäminen käy surkean hitaaksi - myös netistä kopioidun.

BASH-ohjelmien dokumentointi on kyllä sikäli turhauttavaa että todellinen virtuoosi supistaa pitkänkin funktion muutamaan pääohjelmassa olevaan käskyyn. Enkä minä ole enää minkäänlainen virtuoosi.

Samoin ne näytölle-tulostukset ovat kyllä liiaksi painottuneet funktioihin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ppiirto - 27.03.11 - klo:20.13
Tietääkö onko jotain tapaa tehostaa .sh-skriptien toimintaa tai jopa konvertoida tai kääntää niitä johonkin tehokkaampaan muotoon?

Olen käyttänyt animaatioiden tekoon paljon ImageMagick-kuvanmuokkausohjelmaa käyttäviä skriptejä ja nämä skriptit ovat aika tolkuttoman hitaita. En tiedä kyllä johtuuko hitaus lähinnä IM:n hitaudesta. Hitaudella tarkoitan nyt sellaista, että skriptin ajaminen kestää viikon, en mistään millisekuntieroista.

Tuossa esimerkkiskriptin pätkä, varmaan noita voisi muutenkin kyllä optimoida:

Koodia: [Valitse]
stutterJpgEND ()
{
len=${#images[*]}
l=0
        k=0
sourceImage=${images[$l]]}
prev=0
let stutterNum=0
while [ $l -lt $len ]; do
if [ $k -lt 10 ] ; then
num="0${k}"
else
num="${k}"
fi
if [ $k -lt 100 ] ; then
num="0${num}"
else
num="${num}"
fi
if [ $k -lt 1000 ] ; then
num="0${num}"
else
num="${num}"
fi
sourceImage=${images[$l]}

resultImage="videoframes/stutter/stutter_ani${num}.jpg"
tempImage="videoframes/stutter/stutter_temp${num}.jpg"

if [ ${stutterUseBase} -eq 1 ] ; then
resultImage="stutter/stutter_ani${num}.jpg"
tempImage="stutter/stutter_temp${num}.jpg"
fi

convert ${sourceImage} -contrast -contrast -resize 1620x1080 ${tempImage}

if [ ${stutterLoop} -eq 1 ] ; then
let stutterNum+=stutterStep
if [ ${stutterNum} -gt ${stutterSize} ] ; then
let stutterNum=0
fi
fi
let stutterLine=stutterSize+stutterNum

if [ ${stutterUseBase} -eq 1 ] ; then
resultImage="stutter/stutter_ani${num}.jpg"
tempImage="stutter/stutter_temp${num}.jpg"
fi

${scriptDirectory}stutter.sh -s ${stutterLine} -d ${stutterDir} ${tempImage} ${resultImage}
rm ${tempImage}

echo "stuttered ${num} of ${len}"

let k++
let l++
done

}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 27.03.11 - klo:21.56
Joskus 1995 joku Espanjalainen teki sille jonkunsortin C-kielelle kääntävän kääntäjän joka oli ainakin aikoinaan sangen suosittu; nimeäkään en muista enkä googlestakaan nyt löytänyt. Mutta joskus 2005 sen ylläpito loppui, en tiedä miksi. Kai sen vieläkin saisi käsiinsä.
- olen nyt vajaan vuoden opiskellut BASHia aikani kuluksi. Tuntuu että sillä on kykyjä vaikka mihin; mutta riippuen siitä kuinka skriptin kasaa sen suoritusajat, muistivaatimukset jne. vaihtelevat aivan kammottavasti.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ppiirto - 27.03.11 - klo:23.22
Kiitos vastauksesta. Olen itse käyttänyt muiden tekemiä skriptejä vuosia, mutta vasta vajaan vuoden itse näitä olen enemmän tehnyt. Näillä tosiaan voi tehdä nopeasti vaikka mitä, mutta juurikin tämmöisissä toistuvissa matemaattisissa operaatioissa yms ei taida olla kauhean tehokasta. En tiedä kyllä olisiko ero suuri jos tekisin vastaavan esim Javalla. C:tä tai C++:aa en itse osaakaan kunnolla.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 28.03.11 - klo:15.58
Olen käyttänyt animaatioiden tekoon paljon ImageMagick-kuvanmuokkausohjelmaa käyttäviä skriptejä ja nämä skriptit ovat aika tolkuttoman hitaita. En tiedä kyllä johtuuko hitaus lähinnä IM:n hitaudesta. Hitaudella tarkoitan nyt sellaista, että skriptin ajaminen kestää viikon, en mistään millisekuntieroista.

Ainakin tuossa esimerkkiskriptissäsi ainoastaan convert ja stutter.sh saattavat olla hitaita, eli ongelma ei ole skriptissä itsessään.

Mikä tässä on lopullisena päämääränä? Esimerkiksi ffmpegillä voi tehdä monia kuvankäsittelyoperaatioita suoraan komentoriviltäkin, ja FFmpegin jpeg-kodekki ja skaalaimet ovat ainakin kertaluokkaa nopeampia kuin ImageMagickin.

Näillä tosiaan voi tehdä nopeasti vaikka mitä, mutta juurikin tämmöisissä toistuvissa matemaattisissa operaatioissa yms ei taida olla kauhean tehokasta. En tiedä kyllä olisiko ero suuri jos tekisin vastaavan esim Javalla. C:tä tai C++:aa en itse osaakaan kunnolla.

Ensin kannattaa selvittää, mikä skriptistä tekee hitaan. time-komento (tai vaihtoehtoisesti /usr/bin/time, jossa on enemmän toimintoja kuin bashin timessa) auttaa suoritusaikojen mittaamisessa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 28.03.11 - klo:22.54
Kannattaa myös miettiä tarkkaan miten ImageMagick komentoja käyttää. Esim.
Koodia: [Valitse]
convert ${sourceImage} -resize 1620x1080 -contrast -contrast ${tempImage}lienee nopeampi kuin
Koodia: [Valitse]
convert ${sourceImage} -contrast -contrast -resize 1620x1080 ${tempImage}...ellei tuossa sitten skaalata kuvaa isommaksi.

Eihän nuo nyt tietenkään tee täsmälleen samaa lopputulosta, mutta käytännössä ero ei välttämättä ole huomattava.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Antifilatelisti - 03.05.11 - klo:02.07
Laitanpas tänne bashaamiani sekasikiöitä:

Koodia: [Valitse]
#!/bin/bash
# Vaihdetaan kansion sisältämät tiedostonimet vastaamaan tekstitiedoston uudet.nimet listaa:
#
echo"
#Esim:
#---------------------------------------------
# ls Kansio/    t1 t2 t3 t4 t5            |
#                                         |
# cat uudet.nimet                         |
# n1                                      |
# n2                                      |
# n3                                      |
# n4                                      |
# n5                                      |
#                                         |
# Skriptin nimenmuunto.sh suoritus        |
# -> ls Kansio/    n1 n2 n3 n4 n5         |
#---------------------------------------------"

echo "Kerätään tietoja"

ls *.[^nimet]* >vanhat.nimet
riveja=`wc -l uudet.nimet|awk '{print $1}'`;

cat vanhat.nimet



echo -n "Muunnetaanko $riveja:n tiedoston nimet enter(joo): "
read syotto




if [ -z "$syotto" ];
  then
                echo "========================="
     for luku in $(seq $riveja)

  do

apused=''$luku"q;d"''

  v=`sed  ''$apused'' vanhat.nimet`
  u=`sed  ''$apused'' uudet.nimet`

echo "Vanha: $v"
echo "Uusi: $u"

  mv "$v" "$u"

done
  echo "========================="

 else
echo "Mitään ei muunnettu...poistutaan";
 fi


exit


Ja sitten tää mun winepelejen käynnistäjä:

Koodia: [Valitse]
#!/bin/bash
echo "------------------------------------"
echo  "Mitä pelataan"
echo;
echo "-enter :poistu"
echo "-1 :Il2"
echo "-2 :civ4"

echo -n "Valitse peli:"
read  valittu


   case $valittu in
#1
1) echo "Vaihtoehto 1 valittu"
wine "/home/kayttaja/pelit/il2410m/il2fb.exe"
exit;;
      
#2
2) echo "Vaihtoehto 2 valittu"

echo  "Mitä pelataan"
echo;
echo "-enter :poistu"
echo "-1 :civ4"
echo "-2 :wlords"
echo "-3 :beyond"
echo -n "Valitse peli:"
read  versio;

  case $versio in
#1
1) echo "Vaihtoehto 1 valittu"
WINEDLLOVERRIDES="msxml3=n" wine "/home/kayttaja/pelit/civ/Civilization4.exe"
exit;;

#2
2) echo "Vaihtoehto 2 valittu"
WINEDLLOVERRIDES="msxml3=n" wine "/home/kayttaja/pelit/civ/Warlords/wlords.exe"
exit;;

#3
3) echo "Vaihtoehto 3 valittu"
WINEDLLOVERRIDES="msxml3=n" wine "/home/kayttaja/pelit/civ/Beyond the Sword/Civ4BeyondSword.exe"

exit;;
esac

exit;;
esac
exit



Tästä voimme päätellä jotta bashata voi paksusti ja rusinoita(;) saa mättää mielin määrin minne vaan, mutta silti toimii. ugh :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Antifilatelisti - 05.05.11 - klo:02.38
Verkossa on varmaankin jossain kerrottu kuinka Nattylle tehdään sivupalkkiin käynnistin omille skripteille mutta en kylläkään ole nähnyt. Mutta tässä se nyt on:

Nattyn sivupalkkiin voi tehdä itsetekemilleen skripteille käynnistimen: heti boottauksen jälkeen napauta hiiren vasemmalla työpöytää jostainkohtaa ja avautuvasta ikkunasta: luo käynnistin. Avautuvassa ikkunassa kirjoita skriptin nimi kohtaan Nimi:. Senjälkeen paina Selaa ja etsi skrpti ja näpäytä sitä hiiren oikealla . Sitten siirrä hiiriosoitin sanan Komento: viereisen ikkunan alukuun ja näpäytä hiiren vasemmalla. Kirjoita siihen: gnome-terminal -e ja kirjoittamasi perään välilyönti ennen sanaa /home..... Kun näpäytät valmis niin työpöydälle ilmestyy kuvake: tässävälissä kannattaa muuten kokeilla että kuvake toimii. Sammuta skriptiajuri.
Töypöydällä siirrä hiiriosoitin tehdyn kuvakkeen päälle ja paina hiiren vasen pohjaan ja pidä se pohjaanpainettuna. Kuvake on nyt hiiriosoittimenasi - siirrä se vasemmalla olevan palkin päälle (tässävaiheessa älä välitä millainen kuvake sillä on mutta pistä mieleesi millainen se on) ja päästä hiiren nappi ylös jolloin kuvake jää palkkiin. Paina näppäimistössä "Windows"-nappia ja katso mikä numero tekemäsi kuvakkeen päälle tulee. Kun painat yhtaikaa nappeja: windows-nappi ja tuo numero niin skriptiajuri käynnistyy.
- muuten työpöydällä olevan kuvakkeen heittäminen roskikseen vie kuvakkeen pois sivupalkistakin.
- muuten tuo loppu (Työpoydällä ...)  kertoo kuinka Ubuntun omat sovellukset saa sivupalkkiin.
- Nattyn sivupalkki häiriköi pahasti: heti kun viet hiiriosoittimen vasempaan laitaan niin sivupalkki todennäköisesti ponnahtaa esiin ja taas puhutaan perunoista. Ilmiötä lieventää aikapaljon asetus: ALT-f2 - kirjoita: gconf-editor - apps / compiz-1 / plugins / unityshell / screen0 /options / Launcher_reserved_edge - muuta arvo Left:istä Right:iksi. Palkki tulee edelleen esiin kun hiiriosoitin menee vasempaan laitaan ruudun 'ylärivillä' mutta ei enää kun ollaan näytössä 'ylärivin' alapuolella.


LOL :) oiskohan tässäkohden helpompaa käynnistää ubuntu kirjautumisruudusta perusubuntu desktoppiin ja raahata kuvake yläpaneliin ja käynnistää uudelleen nattynyn :)

Ja kun natyn paneelissa on niin rajallinen tila niin ehkäpä voisi kirjoittaa pienen skriptin:
Koodia: [Valitse]
#!/bin/bash
# Käynnistin omille skripteille, jotka ovat sijoitettuna yhteenkansioon
(sijoita tämä skripti samaan kansioon)
#


echo "------------------------------------"
ls|cat -b
echo -n  "Valitse suoritettavan skriptin numero: "

read skriptiNo



apused=''$skriptiNo"q;d"''

vs=`ls|cat -b|sed  ''$apused''|colrm 1 8`
sh $vs
exit;
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 05.05.11 - klo:23.01
  oiskohan tässäkohden helpompaa käynnistää ubuntu kirjautumisruudusta perusubuntu desktoppiin ja raahata kuvake yläpaneliin ja käynnistää uudelleen nattynyn :)

Ei onnistu, kun itsetehdyillä skripteillä ei ole kuvakkeita? Tunnustan: on sekundaa tehdä pelkkä käynnistin. Mutta kuvakkeen teossa on todella iso homma.



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 06.05.11 - klo:00.21
 ??? Käynnistin alias kuvake alias .desktop -tiedosto?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Antifilatelisti - 06.05.11 - klo:00.23
 oiskohan tässäkohden helpompaa käynnistää ubuntu kirjautumisruudusta perusubuntu desktoppiin ja raahata kuvake yläpaneliin ja käynnistää uudelleen nattynyn :)

Ei onnistu, kun itsetehdyillä skripteillä ei ole kuvakkeita? Tunnustan: on sekundaa tehdä pelkkä käynnistin. Mutta kuvakkeen teossa on todella iso homma.

Niin tarkoitin kyllä, että lisää siellä perusdesktopissa myös sen kuvakkeen(hoikea ja selaa omakuvake). Itellä ainaski näin on omien skriptien kuvakkeet näkyneet nätisti myös natynpaneelissa. Myönnetäköön, että käynnisteleminen edes takaisin destoppien välillä voi olla rassaavaa :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Antifilatelisti - 06.05.11 - klo:00.45
Olinpas hölmö.

Hommahan menee melkein samalla tavalla kuten ennenkin. Hieman muunnettu petterillin alkuperäisestä versiosta:

1.1 Tyyppiin valitaan sovellus päätteessä
1.2 Nimeen laitetaan vaikkapa omakomento
1.3 Komentoon selaat oman komennon

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: BackTrack - 28.05.11 - klo:00.11
Hei,

ei ole näitä scriptejä tullut tehtyä, niin neuvot olisivat paikallaan. Jonkun yksinkertaisen saisin varmasti itsekkin tehtyä, mutta tämä nyt ei ainakaan omassa pääkopassa kovin yksinkertaiselta tuntunut, eikä noilla suomenkielisillä ohjeistuksilla mitä webistä löysin oikein saanut kiinni asiasta. Kysyn siis suosiolla kokeneemmilta :)

Eli tarvitsisi saada scripti seuravaanlaiseen toimintaan.

Kansiossa X on useita tekstitiedostoja. Tiedostot on nimetty jotenkuten loogisesti, mutta ei aivan "tiedosto1.txt", "tiedosto2.txt" jne. tyylisellä kurinalaisuudella.

Tekstitiedostoista on suurin osa turhia, mutta noiden sisällön kahlaaminen käsin on aikaa vievää ja turhauttavaa touhua. Ajatus olisi siis saada scripti joka kävisi tiedostot läpi, suorittaisi jokaisessa tiedostossa haun määritellyllä sanalla/sanoilla/numerolla/virkkeellä ja mikäli vastaavuus tiedostosta löytyy, siirtäisi sen toiseen kansioon/nimeäisi eri tavalla tai jättäisi ne tiedostot rauhaan joista vastaavuus löytyy, mutta poistaisi suoraan tiedostot joista vastaavuuksia ei löydy.

Onko tälläinen edes mahdollinen?  ::)

Eli jos ajateltaisiin asiaa siltä kantilta, miten itse tuon käsin tekisin, menisi se jotenkuten näin: Kansio auki, tekstitiedosto auki, ctrl+f  ; hae sanaa/virkettä/numeroa. If vastaavuus löytyy = tiedosto talteen, else = tiedosto roskiin. Seuraava tiedosto auki, haku, roskiin/talteen, seuraava tiedosto auki, haku, roskiin/talteen jne.jne.jne.. Ja tämä pitäisi nyt saada kirjoitettua scriptiksi, sillä noita tiedostoja on tosiaan vähän liikaa näin käsin kahlattavaksi..

Erittäin suuret kiitokset auttajille, jo näin etukäteen :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 28.05.11 - klo:14.13
Lainaus
Onko tälläinen edes mahdollinen?

No varmasti.   Sen sijaan että antaisin valmiin ratkaisun heti niin annan pieniä ideoita joista pääsee alkuun kokeilemaan.  Pikkuisen "rautalankaa" teemasta aluksi.


ls  näyttää tiedostojen nimet

cat  teksti.txt  tulostaa tiedoston sisällön

grep jotain_tekstiä  valitsee vain ne rivit joilla esiintyy jotain_tekstiä


Komentoja voi "putkittaa" peräkkäin käyttämällä "putkimerkkiä":   |      Tuo putkittaminen tarkoittaa että sen sijaan että komennon tulokset menisi näytölle ne meneekin toiselle komennolle.  Esimerkki:

ls | grep txt

näyttää kaikki joissa esiintyy "txt".  Komennon "ls" tulokset menevät ohjelmalle "grep" joka seuloo halutut rivit.  Vastaavasti  

ls | grep -v txt

näyttää kaikki joissa ei esiinny "txt"   (eli tuo -v  kääntää merkityksen).  Tuolle "grep" seulalle pystyy antamaan tiedoston joka sisältää ne kaikki halutut osumat omilla riveillään.  Tällöin yllä oleva komento saa muodon:

ls | grep -f lista.txt

Tuonne "lista.txt" kirjoitellaan editorilla (gedit lista.txt)  ne sanat tai lauseet jotka halutaan löytää, kukin omalle rivillensä.  Tyhjiä rivejä ei saa jäädä minnekään yhtä ainutta, ei edes tiedoston loppuun tai tuo seula ei toimi.

Näitä on helppoa kokeilla päätteessä.  Jos ei selviä putkittamalla kaikki komennot peräkkäin yhdelle riville niin aina voi tehdä komentotiedoston (scripti) mihin laittaa komentoja peräkkäin kunnes koko asia onnistuu.    Tiedoston nimellä ei ole merkitystä mutta tietysti auttaa myöhemmin muistamaan mikä tämä on jos antaa kuvaavan nimen.  Jos tiedoston tyypiksi antaa "bash" niin se jo kuvaa käyttötarkoitusta.  Tiedoston ensimmäisellä rivillä pitää lukea risuaita huutomerkki /bin/bash ja komennot peräkkäin sen jälkeen.  

Koodia: [Valitse]
#!/bin/bash
komentorivi_1
komentorivi_2
jne.
Tuolle tiedostolle pitää erikseen sallia oikeus ajaa sitä kometotiedostona:

chmod +x komentotiedostoni.bash

Tuon jälkeen sen voi ajaa:
Koodia: [Valitse]
./komentotiedostoni.bashÄlä unohda tuota "piste-ja-kauttaviiva" -yhdistelmää edestä, tarkoittaa nykyistä hakemistoa.

Tässä on lyhyt listaus komennoista joita voi tuonne viritellä:

http://ss64.com/bash/ (http://ss64.com/bash/)

Sitten vaan kokeilemaan. Ja korjaamaan..  :)


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 29.05.11 - klo:20.44
Menihän tuo yhdelle riville.  Tässä lyhyt ohje:

1)  Tee kotihakemistoosi editorilla tiedosto jonka nimi on "lista".  Laita tuohon tiedostoon kaikki ne sanat tai sanonnat joita etsit omista tekstitiedostoistasi, kukin omalle rivillensä.   Älä jätä tyhjiä rivejä minnekään, ei alkuun eikä loppuun.  Viimeisen rivin pitää loppua viimeiseen kirjaimeen, siellä ei saa olla "Enter": iä perässä !  

Koodia: [Valitse]
gedit ~/lista
2) Siirry hakemiston alle jossa tekstisi sijaitsevat.

3)  Testaa tekemäsi tiedoston toiminta tällä, se näyttää vain niiden tiedostojen nimet joista tuli osuma (eli jotka halutaan siirtää talteen):

Koodia: [Valitse]
grep -Hs -f lista *.txt | cut -d ":" -f1  

Jos tuntuu että jotain tärkeää puuttuu niin käy uudestaan kohdassa 1 ja testaa uudestaan.

4) Kaikki ok ?  Sitten menoksi:

Koodia: [Valitse]
mkdir backup && grep -Hs -f ~/lista *.txt | cut -d ":" -f1 | xargs -r -n 1 -I{} mv {} ./backup
Tuo tekee ensin "backup" -hakemiston samaan paikkaan minne olit siirtynyt "cd": llä ja siirtää valitut tiedostot sinne.  Ylläolevassa on oletettu että tiedostot ovat *.txt.  Samassa hakemistossa ei saa olla "/backup" -hakemistoa valmiina tai ylläoleva rivi ei tee mitään (varotoimi).

Tässä on vielä ylläolevasta "tekee eikä huomauttele" -versio, yrittää luoda "/backup" -hakemiston mutta ei keskeytä jos se jo oli olemassa vaan jatkaa tiedostojen siirtoon:

Koodia: [Valitse]
mkdir backup 2>/dev/null ; grep -Hs -f ~/lista *.txt | cut -d ":" -f1 | xargs -r -n 1 -I{} mv {} ./backup
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: VilhoPamppu - 01.06.11 - klo:18.45
Onko olemassa tai olisiko mahdollista luoda scriptiä joka:

käyttäisi web-kameraa ja ottaisi kuvan jos kirjautuessa käyttäjätilille tulisi väärä salasana, tallentaisi sen paikkaan x tahi lähettäisi verkon yli palvelimelle x.

En ole oikeastaan vielä juurikaan tutustunut scriptaukseen notta mietin olisiko tässä projekti jolla aloittaa.

Eli jos aloittais sillä että tallentaisi kuvan koneelle (jättäisi tuon verkon yli sitten myöhemmäksi  :D) niin mistähänä kannattaisi aloittaa (jos saisi vinkkiä edellisten viestien tapaan)  :)

1) koneen käynnistyessä aja scripti
2) if salasana väärin jatka scriptiä
  3) käynnistä muikku ja ota kuva
  4) tallenna kuva
  5) lopeta looppi ja palaa kohtaan 2)
6) else lopeta scripti

vai onko jo ajatusmalli päin honkia?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 03.06.11 - klo:02.03
Ei tuokaan mikään mahdoton ole. Vaikein kohta saattaa olla tuo epäonnistunut login.  Koko idea pitäisi saada viritettyä niin päin että tuo scripti ajetaan epäonnistuneen loginin avulla.   Joku ehkä tietää miten epäonnistuneeseen loginiin voisi lenkittää oman scriptin kiinni.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.06.11 - klo:15.26
Törmäsin verkossa käskyyn: cheese --snapshot --delay 1 --outfile $HOME/Pictures/failedlogin.jpg
joten joku on ilmeisesti saanut homman toimimaan.

Tulee montakin hommaa mieleen: mikä $HOME kun ei vielä olla kirjauduttukaan?

Onkohan ratkaisu sopiva vain kokoajan toiminnassa olevaan isoon serveriin ? Mutta toisaalta BASH kyllä kykenee mihin hyvänsä.
   
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 04.06.11 - klo:18.48
Lainaus
Onko olemassa tai olisiko mahdollista luoda scriptiä joka:

käyttäisi web-kameraa ja ottaisi kuvan jos kirjautuessa käyttäjätilille tulisi väärä salasana, tallentaisi sen paikkaan x tahi lähettäisi verkon yli palvelimelle x.

Selasin hieman asiaa netistä. Scripti joka käynnistyy heti kun käyttäjä antaa väärän salasanan tuntuu aika vaikealta toteuttaa.  Paikka jossa tiedetään tuliko oikea vai väärä salasana on "PAM" mutta tuota ei taida saada asetettua (?) käynnistämään mitään scriptiä.  PAM: iin pitäisi tehdä oma moduli. 
Asiaa voi kiertää (erittäin tehottomalla tavalla) käymällä läpi lokitiedostoa usein ja reagoimalla "FAIL" -viestiin. Lisäksi on olemassa "pam-hooks" -ohjelma joka voi käynnistää scriptejä aina kun login aktivoituu (en tutkinut tuota tarkemmin).

Jos joku tietää miten PAM: in saa viritettyä käynnistämään scriptin epäonnistuneen loginin tilanteessa niin kiinnostaa toki kuulla.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 20.06.11 - klo:19.32
Kummitteleva "CD 84

Olen pariin otteeseen törmännyt outoon ongelmaan Gedit: in kanssa:  Jostain ilmestyy ylimääräiset heksakoodit "CD 84" jotka eivät näy Gedit: ssä mitenkään mutta sotkevat kometotiedoston toiminnan kokonaan.   
Samoin kävi nyt tuon "petteril": in ohjelmanpätkän kanssa kun siirsin sen Gedit: iin, tallensin nimellä "aaa", annoin ajo-oikeuden ja yritin käynnistää:

  chmod +x aaa

  ./aaa
  bash: ./aaa: /bin/basḧ́: bad interpreter: No such file or directory

  ls aaa
  aaa

 Onneksi oli heksaeditori käsillä (Bless) joka näytti ongelman laadun.   Sitä ennen ehdin jo ihmetellä tovin enkö osaa bash: iä enää pätkääkään  :)


[ylläpito on poistanut liitteen]
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.06.11 - klo:18.00
 Skripti toimii naurettavan hitaasti jopa BASH skriptiksi johtuen siitä että mitään skriptiä ei edes ole vaan pääte itse toimii laskimena. Lisäksi BASH:in on pakko käyttää laskentaan jotain laskin-ohjelmaa. Ja laskinohjelmat ovat matemaatikoille tarkoitettuja ja niiden merkintätavat senmukaisia. Tämä skripti käyttääkin ajastaan valtaosan muuttaakseen merkintätavat inhimillisiksi. Jos haluaa skriptiinsä BASH:in normaalia nopeutta kannattaa tästä skriptistä kopioida omaan skriptiin tarvittava osa - siis tämä skripti vain osoittaa mikä on mahdollista.

Käyttääkseesi päätettä laskimena kopioi seuraaava koodi johonkin tiedostoon koneessasi. Kun avattuasi päätteen annat käskyn:  . polku_tiedoston_nimeen (siis: piste välilyönti polku) niin kehote tulee takaisin semmoisenaan ja tuntuu siltä ettei mitään tapahtunut. Mutta vaikka pääte toimii niinkuin ennenkin niin tämä pääte osaa nyt normaalin pääte-toiminnan lisäksi toimia myös laskimena - jos esimerkiksi kirjoitat päätteeseen: sin30-cos60 niin enteriä painettuasi saat vastauksen: -.000000000000000000000000000000000000000000000000000000000000000003  . Listan tunnetuista laskuista saat käskyllä: laske. Skripti ei tee koneeseen mitään pysyviä muutoksia ja kun suljet päätteen niin matematiikka-taidotkin unohtuvat.

Laskimella on hitautensa korvauksena rajoittamaton tarkkuus:
- jos haluat laskustasi tuhansia oikeita desimaaleja niin se onnistuu.
- kokonaisosan numeromäärää ei rajoiteta koskaan. Esimerkiksi 10000!:n kaikki 36000 numeroa lasketaan 3:ssa sekunnissa.
- myös samassa päätteessä toimivat skriptit perivät matemaattiset taidot.
- toiminta alkaa siitä että kirjoitetaan päätteeseen "laskukaava" ja painetaan enter. Mikäli funktiot eivät osaa laskukaavaa toteuttaa siirretään "laskukaava" päätteen komento-tulkille joten normaali päätetoiminta on mahdollista.

Koodia: [Valitse]
#!/bin/bash
# petteriIII 2.7.2014 ~/OMATSKRIPTIT/FUNKTIOKIRJASTO/matemaattiset_funktiot
set +H # jos tämä puuttuisi niin kertoman merkki: ! aiheuttaisi virheen
unset command_not_found_handle # poistaa virheviestit mallia: tarkoititko käskyä xxx paketissa yyy


trap 'laske "$BASH_COMMAND"' 1 ERR

function laske () { # Tuloparametrina on laskukaava. Tulostaa näytölle ja asettaa kutsuttujen funktioiden nimiset muuttujat ja tuloksen muuttujaan: tulos.
[[ ! $1 ]] && echo "- laskuissa on 66 desimaalia jollei toisin määrätä. Desimaalipisteen jälkeisiä peränollia ei tulosteta.
- kokonaisosan numeromäärä on aina rajoittamaton ja desimaalien määrän voi määrätä ihan miksi haluaa.
- sallitut operaattorit: +, -, *, / ja ^ Näissä laskuissa ^ on potenssiinkorotus, esimerkiksi 3^0.5  tai: 10000000000^-0.1.  
- funktiot: sin, asin, cos, tan, atan, sqrt, log, ln, exp, abs, int, e, pi, ! (=kertoma), kahteen, kymmeneen (kantaluvun muutokset), des (määrää desimaalien
  luvun),  rad (=määrää että geometriset laskut suoritetaan radiaaneissa), annuiteettilaina lainanmäärä korkoprosentti lainaaika_vuosissa -> vuosilyhennys
- merkitätavat noudattavat normaaleja matematiikan sääntöjä. Sulkuina on kuitenkin helpointa käyttää hakasulkuja.  
- syötettävien esitysmuoto voi olla tieteellinenkin, esimerkiksi: 1.23e6 . Vaikka se olisi samassa lauseessa kuin joku luonnollisen e:n funktio.
- desimaalien ja radiaanien määräämis-esimerkki: des256radsin0.511 . Asetukset säilyvät kunnes muutetaan tai vaihdetaan päätettä.
- testilaskuja: -1-[2*6!+1]/1441+[[sin210-cos60]*tan60*2]/[3^.5]*ln[e^-pi]/pi -> 0 ; ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^ln[e^1.25]]]]]]]] -> 1.25 ;
  2+1.1e2+e-2.7 -> 110.018....; sin[asin.5]-asin[sin.5] -> 0
- edellisen laskun tulokseen voi viitata sanalla: tulos (esimerkiksi laskun: 3^-.5  tuloksen -oikeellisuustesti- on: 1/tulos joka on neliöjuuri 3)"

trap "" 1 ERR
tulos=$1

# testi yritetäänkö jotain tuntematonta:
[[ $(echo $tulos | sed 's/asin//g;s/sin//g;s/cos//g;s/atan//g;s/tan//g;s/log//g;s/ln//g;s/exp//g;s/sqrt//g;s/int//g;s/abs//g;s/kymppiin//g;s/kahteen//g;s/des//g;s/pi//g;s/annuiteetti//g;s/e//g;s/rad//g;s/ast//g;s/\!//g;s/tulos//g' | grep [a-zäöåA-ZÄÖÅ] ) ]] && exec $tulos
 

lasketaan_radiaaneissa=${lasketaan_radiaaneissa:-0} # toimintamuotona on asteet ellei toimintamuodoksi peritä radiaaneja.
desimaaleja=${desimaaleja:-66} # laskujen desimaaliluku on 66 ellei peritä toista arvoa.

tulos=${tulos#+}   # poistetaan + edestä
tulos=${tulos%%#*} # poistetaan kommentit
tulos=${tulos//tulos/$tulos} # muutetaan annetussa laskussa sana:tulos edellisen laskun muuttujaan tulos
tulos=$(echo $tulos | tr -d \' | tr -d \" | tr "(" '[' | tr ")" ']' ) # poistetaan heittomerkit ja muunnetaan kaarisulut hakasuluiksi.
tulos=$(echo "$tulos" | sed 's/--/+/g;s/++/+/g;s/-+/-/g;s/\^+/\^/g;s/\/+/\//g;s/\*+/\*/g') # -- muuntuu +:ksi ja /+ muuntuu /:ksi ja *+ muuntuu *:ksi
[[ $(echo $tulos | grep des) ]] && maaraa_laskujen_desimaaliluku
[[ $(echo $tulos | grep rad) ]] && lasketaan_radiaaneissa=1

# muutetaan luonnollisen järjestelmän kantaluku e lukuarvoonsa kaikkialla. Siis luvut kuten tuo e kaavassa: 1+e^2
e_numeroina=$(exp 1)
tulos=$(echo $tulos | sed "s/e$/$(exp 1)/g;s/^e/$(exp 1)/g" ) # muuttaa e:n rivin alusta ja lopusta
apu=$(echo $tulos | grep -ob [^0-9a-z]e[^a-z0-9] | tr "\n" " " | awk '{print $1}' )
while [[  $apu != "" ]] ; do e_paikka=${apu%%\:*}; tulos=${tulos:0:e_paikka+1}$e_numeroina${tulos:e_paikka+2}; apu=$(echo $tulos | grep -ob [^0-9a-z]e[^a-z0-9] | tr "\n" " " | awk '{print $1}' ); done

# muutetaan pi (ympyrän kehä/halkaisija) lukuarvoonsa kaikkialla
pi_numeroina=$(echo "scale=$desimaaleja; 4*a(1)" | bc -l | tr -d '\\' | tr -d "\n")
tulos=$(echo "$tulos"  | sed "s/pi$/$pi_numeroina/g" ) # muuttaa pi:n kun se on rivin lopussa #[[ $(echo "$tulos" | grep -o pi[^a-z0-9] ) ]] &&
tulos=$(echo $tulos | sed "s/pi\^/$pi_numeroina\^/g;s/pi\*/$pi_numeroina\*/g;s/pi\-/$pi_numeroina\-/g;s/pi\//$pi_numeroina\//g;s/pi[+]/$pi_numeroina\+/g" )

# ratkaistaan kaikki tieteelliset esitysmuodot (niinkuin: 1.23e17)
apu=$(echo $tulos | grep -o [0-9]e.[0-9]* )
while [[  $apu != "" ]] ; do muuta_e_esitys_numeroiksi; apu=$(echo $tulos | grep -o [0-9]e.[0-9]* ); done

tput cuu1; tput el # kursori ylös rivinverran ja rivin tyhjennys
echo #"$tulos"

#korkeintaso=0; [[ ${tulos#*\[} ]] &&
etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti

[[ $korkeintaso -gt 0 ]] && laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla #&& return

for funktiolaji in asin sin cos atan tan log ln exp sqrt int abs kymppiin kahteen annuiteetti ! ; do
korvauksia_tekematta=1
while [ $korvauksia_tekematta -eq 1 ];                                                                                    do
korvauksia_tekematta=0
[[ $(echo $tulos | grep $funktiolaji ) ]] && korvauksia_tekematta=1 && erota_parametrit_ja_kutsu_funktiota $funktiolaji ; done
                                                                                                  done
# ratkaise x^y kaikkialla, sillä bc ei yksinään ymmärrä desimaalisia eksponentteja
apu=$(echo $tulos | grep -o [0-9.][0-9.]*^[0-9.+-][0-9.]* | tr "\n" " " | awk '{print $1}')

while [[  $apu != "" ]] ; do ratkaise_eksponentti $apu; apu=$(echo $tulos | grep -o [0-9.][0-9.]*^[0-9.+-][0-9.]* | tr "\n" " " | awk '{print $1}'); done
tulos=$(echo $tulos | sed 's/--/+/g') && tulos=$(echo "obase=10;ibase=10;scale=$desimaaleja ; $tulos" | bc -l | tr -d '\\' | tr -d "\n" ) && tulosta $tulos
}

function erota_parametrit_ja_kutsu_funktiota () {
[[ $funktiolaji = ! ]] && apu=$(echo ${tulos%%\!*}  | sed 's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/\-/ /g') && numero=${apu##* }
[[ $funktiolaji != ! ]] && apu=$(echo ${tulos#*$funktiolaji} | sed  's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/\-/ /g') && numero=${apu%% *}
[[ $numero = "" ]] && apuu=$(echo ${tulos#*$funktiolaji}) && apu=${apuu:0:1}${apu:1} && numero=${apu%% *} # palautetaan operaattoreita poistettaessa hävinnyt merkki
kutsu="$funktiolaji $numero"; $kutsu; arvo=$($kutsu); korvattava=$funktiolaji$numero;[[ $funktiolaji = ! ]] && korvattava=$numero$funktiolaji;tulos=$(echo $tulos | sed "s/$korvattava/$arvo/g")
}

function tulosta () { #  mikäli luvussa on desimaalipiste niin poistetaan peränollat ja pistekin jos se jää loppuun yksikseen
[[ ${1%%\.*} != $1 ]] && echo $1 | sed 's/0*$//g' | sed 's/\.$//g' || echo $1
}
 
function ratkaise_eksponentti () { # ratkaistaan: x^y sillä ilman apua bc ei ymmärrä desimaalisia eksponentteja
[[ $(echo $tulos | grep '\^' ) = "" ]] && return
lukuedessa=${tulos%%^*}
etukaannettyna=$(echo $lukuedessa | rev)  
kantaluku=$(echo ${etukaannettyna%%[!\.0-9]*} | rev)  
exponentti=$(echo ${tulos##*\^} | sed 's/\^/ /g;s/\*/ /g;s/\// /g;s/\+/ /g;s/[0-9]\-/ /g') # 420
valitulos=$(echo "scale=$desimaaleja; e($exponentti*l($kantaluku))" | bc -l | tr -d '\\' | tr -d "\n")
korvattava=$kantaluku"\^"$exponentti; tulos=$(echo $tulos | sed "s/$korvattava/$valitulos/g")
echo $kantaluku'^'$exponentti' '$valitulos
}
 
function etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti  () { # echo "etsitään tasoa"
raja=112\101\116\116\101\114\105\73\73\73
korkeintaso=0; olintaso=0
for (( n=0; n<=${#tulos}; n++ )); do
  [[ ${tulos:$n:1} = "[" ]] && let olintaso++  
  [[ $olintaso -gt $korkeintaso ]] && korkeintaso=$olintaso && tekstinalku=$n
  [[ ${tulos:$n:1} = "]" ]] && let olintaso--
done
[[ $korkeintaso -eq 0 ]] && return
for (( m=tekstinalku; m<=${#tulos}; m++ )); do
  [[ ${tulos:$m:1} = "]" ]]  &&  tekstinloppu=$m && break
done
}

function laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla () {
tulos_alussa="$tulos"
suluissa_oleva="${tulos:$tekstinalku:$(($tekstinloppu-$tekstinalku))}" # oli: -$tekstinalku+1))}"
ilman_sulkuja=$(echo $suluissa_oleva | sed 's/^\[*//g;s/\]*$//g');  
laske $ilman_sulkuja
uusi_tulos=${tulos_alussa:0:$tekstinalku}$tulos${tulos_alussa:$tekstinloppu+1}; tulos=$uusi_tulos; echo $tulos
etsi_laskukaavan_korkeimman_tason_vasemmanpuoleisimman_sulun_teksti $tulos  
[[ $korkeintaso -gt 0 ]] && laita_se_laskentaan_ja_korvaa_sulun_teksti_lasketulla_arvolla  # koe [[ $korkeintaso -gt 0 ]] && laske $tulos
}

function muuta_e_esitys_numeroiksi  () { # ratkaistaan tieteellinen esitystapa; luvut mallia 1.23e18                                              
korvattava=$(echo $tulos | grep -o [.0-9]*e[-+0-9][0-9]* )
[[ $korvattava = "" ]] && return
arvo=$(echo $korvattava | sed "s/e/\*10\^/g")
tulos=$(echo $tulos | sed "s/$korvattava/"\["$arvo"\]"/g")
}

function maaraa_laskujen_desimaaliluku () {
desimaaleja=$(echo ${tulos##*des} | sed 's/[[:alpha:]]//g')
korvattava="des"$desimaaleja; tulos=$(echo $tulos | sed "s/$korvattava//g")
}

function tan () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin  poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && tan=$(echo "scale=$desimaaleja; s($asteluku)/c($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || tan=$(echo "scale=$desimaaleja; s($asteluku*$pi_numeroina/180)/c($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n")  ; tulosta $tan
}

function kahteen () {
kahteen=$(echo "obase=2;ibase=10;$1" | bc -l | tr -d '\\' | tr -d "\n" ) ; tulosta $kahteen
}

function kymppiin () {
kymppiin=$(echo "obase=10;ibase=2;$1" | bc -l | tr -d '\\' | tr -d "\n" ) ; tulosta $kymppiin
}

function cos () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin  poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && cos=$(echo "scale=$desimaaleja; c($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || cos=$(echo "scale=$desimaaleja; c($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $cos
}

function log () { # briggsin logaritmi
log=$(echo "scale=$desimaaleja; l("$(echo $1 | sed "s/e/$e_numeroina/g")")/l(10)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $log
}

function ln () { # e-kantainen logaritmi
ln=$(echo "scale=$desimaaleja; l($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $ln  
}

function exp ()  { # e^x
exp=$(echo "scale=$desimaaleja; e($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $exp
}

function sqrt () { # neliöjuuri
sqrt=$(echo "scale=$desimaaleja; sqrt ($1)" | bc -l | tr -d '\\' | tr -d "\n"); tulosta $sqrt
}

function ! () {
[[ $1 -eq 0 ]] && kertoma=1 || kertoma=$( eval echo {1..$1} | tr " " \* | bc | tr -d '\\' | tr -d "\n"); tulosta $kertoma
}

function atan () { # arctangentti
[[ $lasketaan_radiaaneissa -eq 1 ]] && atan=$(echo "scale=$desimaaleja; a($1)" | bc -l | tr -d '\\' | tr -d "\n") || atan=$(echo "scale=$desimaaleja; a($1*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $atan
}

function sin () {
asteluku=$(echo ${1#+}) # jos asteluvun edessä on + niin  poistetaan se
[[ $lasketaan_radiaaneissa -eq 1 ]] && sin=$(echo "scale=$desimaaleja; s($asteluku)" | bc -l | tr -d '\\' | tr -d "\n") || sin=$(echo "scale=$desimaaleja; s($asteluku*$pi_numeroina/180)" | bc -l | tr -d '\\' | tr -d "\n") ; tulosta $sin
}

function asin () { # arcsini
[[ $lasketaan_radiaaneissa -eq 1 ]] && asin=$(echo "scale=${desimaaleja:-66}; a($1/sqrt(1-($1*$1)))" | bc -l | tr -d '\\' | tr -d "\n") || asin=$(echo "scale=${desimaaleja:-66}; a($1/sqrt(1-($1*$1)))*180/$pi_numeroina" | bc -l | tr -d '\\' | tr -d "\n")  
tulos=$(echo $tulos | sed "s/asin$1/$asin/g");tulosta $asin
}

function abs () { # tämä poistaa etumerkin, myös plussan
abs=$1; abs=$(echo ${abs#[-+]}); tulosta $abs
}

function int () { # muuttujan kokonaisluku-osa
int=$(echo 0$1 | cut -d. -f1 ); tulosta $int
}

function annuiteetti () {
laske [[1+$korkoprosentti/100]^$lainaaika*$korkoprosentti/100]/[[1+$korkoprosentti/100]^$lainaaika-1]*$lainamaara
annuiteetti=$tulos
}

function ratkaise_annuiteetti () { # lainanmäärä korkoprosentti lainaaika(vuosissa)
set -- $(echo $tulos | sed 's/annuiteetti//g')
lainamaara=$1
korkoprosentti=$2
lainaaika=$3
echo 'annuiteetti '$lainamaara' '$korkoprosentti' '$lainaaika
annuiteetti #kutsu="annuiteetti";arvo=$("$kutsu"); korvattava="annuiteetti $lainamaara $korkoprosentti $lainaaika"; tulos=$(echo $tulos | sed "s/$korvattava/$arvo/g"); echo $annuiteetti
}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.07.11 - klo:06.14
BASH-skripteissä ei pitäisi olla tällaista 'normaaliohjelmointia' ja jos sitä on pakko käyttää on se yleensä merkki siitä ettei skriptiä ole tehty parhaalla mahdollisella tavalla. Silti tällaiset rakenteet täytyy löytyä sillä eihän parasta tapaa kannata edes etsiä jos toiminta on muutenkin kelvollista.

Pinon käsittely:
- pinoon lisätään käskyllä:           pinoa pinottavan_muuttujan_nimi          
- pinon päältä luetaan käskyllä:   ensiksi_pinottu pinottavan_muuttujan_nimi
- pinon alta luetaan käskyllä:     viimeksi_pinottu pinottavan_muuttujan_nimi

- luettaessa pinosta silloinkin kun se on tyhjä ei aiheuta virhettä, luettu jää vain tyhjäksi ja sen perusteella toimiminen jää kutsujan tehtäväksi
- toimii sekä numeroille että tekstille (vain sanoille, ei lauseille).
- ainoastaaan pinon päälle voi lisätä mutta lukea voidaan kummastakin päästä vaikka samassa skriptissä vuorotellen.
- koodia lisäämällä saa muuttujat local-tyyppisiksi. Mutta koodista tulisi suurempi ja toimintanopeuskin laskisi.
- jos valittavien joukossa on välilyönti se jätetään valittavien joukosta pois. Virhe se kuitenkaan ei ole.
- Linuxissa on bc:n lisäksi toinenkin kalkulaattori. Nimeltään se on dc. Se on vaikeakäyttöinen ja pino-perusteinen. Sen taitajat vannovat sen nimeen. Pitääpä palauttaa mieleen kuinka sitä käytetään.

Koodia: [Valitse]
#!/bin/bash

function pinoa () {
[[ ! $pinoosoitin ]] && pinoosoitin=0 && pinonpohja=0
for n in $1; do pino[$pinoosoitin]=$n; let pinoosoitin++; done
}

function viimeksi_pinottu () {
[[ $pinoosoitin>0 ]] && let pinoosoitin--
eval $1=${pino[$pinoosoitin]}
unset pino[$pinoosoitin]
}

function ensiksi_pinottu () {
eval $1=${pino[$pinonpohja]}
unset pino[$pinonpohja]
let pinonpohja++
}

# pääohjelma: pelkkää kokeilua
pinoa 0
pinoa "1 2 3 4 5 6 7 8 9 10 11 12"
pinoa "yksi 2 kolme ja vieläkin yksi"
while true; do
  ensiksi_pinottu apu  #  kutsuttaessa: viimeksi_pinottu tulostus-järjestys muuttuu  
  [[ !  $apu ]] && break
  echo $apu
  read
done
[[ ${pino[*]} ]] && echo 'tässä ei pitäisi tulostaa mitään sillä pinon pitäisi olla tässävaiheessa tyhjä'
read -p 'paina enter jatkaakseesi'
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 19.07.11 - klo:20.49
Taas kertaalleen tuntuu siltä ettei rajaa skriptien nopeuttamiseksi olekaan. Samaan hölmöyteen kompastuu joka kerta: ainakin minä yritän ratkaista ongelmat itse enkä tajua käyttää käyttöjärjestelmän rutiineja.
- muuten jo tehdyn skriptin muuttaminen käyttöjärjestelmää paremmin käyttäväksi on helpompaa kun tekee koko skriptin uusiksi.

Tein skriptin jonka tarkoitus oli laskea skriptikansion skriptien lukumäärä: skriptikansiossa on monia alikansioita joissa on skriptejä dokumentteineen. Skripti toimi kyllä, mutta vähän hidas se oli. Sitten muunsin skriptin käyttämään enemmän käyttöjärjestelmää. Nyt skripti on niin nopea ettei kättään kerkiä näppäimistöltä nostaa kun on jo valmis.

- Skriptin suoritusajan mittaamiseen tarkoitettu time-käsky esittää kolme arvoa tarkoituksenaan kertoa muunmuassa se kuinka nopea skripti on ja millaiset ovat mahdollisuudet nopeuttaa sitä. Arvot ovat:  Real, User ja Sys. Suhde Real/Sys kertoo kuinka paljon on nopeuttamisen varaa: mitä suurempi arvo on sitä enemmän voi nopeuttaa.

Hitaana skriptin arvot olivat:
real   0m2.096s
user   0m1.804s
sys   0m0.224
- suhde 9.4

Nopeutettuna skriptin arvot ovat:
real   0m0.176s
user   0m0.092s
sys   0m0.044s
- suhde 4. Nopeuttaminen olisi varmaan vieläkin mahdollista, mutta nopeus riittää tämänhetkisessä tilanteessa kun skriptejä on vajaa tuhat ja muita tiedostoja muutamia satoja.

Koodia: [Valitse]
#!/bin/bash
skriptienluku=0
for tiedostonimi in $(grep -lr / ~/omatskriptit | grep -v /ARKISTO/)
do
  read -N 3 tiedoston_alku < $tiedostonimi # luetaan tiedostosta 3 ensimmäistä merkkiä
  [[ $tiedoston_alku = "#!/" ]] && let skriptienluku++
done
echo "skriptienluku="$skriptienluku
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 20.07.11 - klo:09.15
Asian vierestä / idea: 
Joskus kun "on kiire" kannattaa tehdä asioita rinnakkain eikä peräkkäin jos se on mahdollista.  Testasin hieman tuota ideaa aiemmin kun joku halusi "nopean pingauksen".  Kirjoitin silmukan joka käynnistää rinnakkain 254 ping -komentoa, odottelee kun kaikki nuo ovat valmistuneet ja näyttää tulokset jos jotain löytyi.  Mukavaa katsella kun koneessa käynnistyy prosesseja :)  Mitenköhän monta noita voisi laittaa samaan aikaan käyntiin ?  Tuo rinnakkaisping tekee myös tehtävänsä rivakasti.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.07.11 - klo:17.42
Asian vierestä / idea: 
Joskus kun "on kiire" kannattaa tehdä asioita rinnakkain eikä peräkkäin jos se on mahdollista. 
...

Minulle ainakin on tarve tällaiselle ja toivon että näin saisin skriptissäni toimimaan siedettävän nopeasti kun luen editoriin monta tiedostoa yhdellä kertaa tyyliin:
gedit tiedosto1 tiedosto2 tiedosto3 ... tiedosto12
- toimii kyllä myös kun määrää: (gedit tiedosto1 tiedosto2 tiedosto3 ... tiedosto12&) mutta tiedostot latautuvat yhtä hitaasti kuin ennen
- tiedostot ovat suurinpiirtein samakokoisia.
- ei se kestä kuin 4 sekuntia mutta parempi olisi kun kestäisi vajaan sekunnin.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 23.07.11 - klo:22.37
Lainaus
- toimii kyllä myös kun määrää: (gedit tiedosto1 tiedosto2 tiedosto3 ... tiedosto12&) mutta tiedostot latautuvat yhtä hitaasti kuin ennen

Entäs näin:

gedit tiedosto1 &
gedit tiedosto2 &
gedit tiedosto3 &
.
.
.
jne.



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.07.11 - klo:08.12
Entäs näin:

gedit tiedosto1 &
gedit tiedosto2 &
gedit tiedosto3 &
.
.
jne.

Tattista, auttoihan se. Gedit itse on vielä hidas mutta parempi gedit on kuulemma tulossa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.07.11 - klo:11.53
Tietääkö onko jotain tapaa tehostaa .sh-skriptien toimintaa tai jopa konvertoida tai kääntää niitä johonkin tehokkaampaan muotoon?

Nyt vasta törmäsin yhteen opastukseen, joten taitaa olla myöhässä tähän asiaan. Lisäksi luulen että koodin nopeus ei juurikaan nouse (sattan olla väärässä joten kokeilla kannattaa) vaan tarkoitus on lähinnä estää koodin muokkaaminen, tai salata koodi.

http://alnazmin.blogspot.com/2011/07/how-to-compile-bash-script-using-shc.html
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 06.08.11 - klo:06.00
BASH:in käsky: eval on osoittautunut tärkeäksi. Se kertoo BASH:ille että yksi kierros lisää tulkkausta olisi tarpeen. Toinen 'sinnepäin peukalosääntö': se muuttaa tekstijonon käskyksi. Sitä tarvitaan esimerkiksi:
- funktiossa nimiparametri jää ilman eval-käskyä siksi miksi sitä nimitetäänkin: nimeksi. Eval muuttaa sen arvoksi.
- joidenkin rakenteiden sisällä olevaa ei laajenneta ellei käskyllä eval kerrota BASH:ille että nyt laajennetaan.
- yksi mielenkiintoinen käyttö esitetään sivulla: http://www.linuxjournal.com/content/bash-preserving-whitespace-using-set-and-eval
- joskus samassa ilmauksessa on osia jotka täytyy laajentaa ja osia joita ei saa laajentaa. Ratkaisu on että eval määrätään ihan normaalisti, mutta ne osat joita ei saa laajentaa suojellaan laittamalla se kovien lainausmerkkien väliin. Yleensä kovat lainausmerkit lisätään yhdistelmällä: \'     . Siis keno, ei /


Eval-käsky on kuulemma vaarallinen. Käyttöjärjestelmän omissa skripteissä sitä kuitenkin esiintyy paljonkin niinkuin muuten myös goto-käskyä. Mutta aivan kuten aikoinaan goto-käskyn kanssa kunniallista syytä vaarallisuuteen ei esitetä, puhutaan vain epämääräisesti tietoturvasta. On ihan mahdollista että kunnollinen syy on olemassa ja ettei sitä voi käsittää muut kuin teoreettisen ohjelmoinnin tuntijat. Mutta ihan mahdollista on sekin, että niin väitetään jottei oma tietämättömyys paljastuisi. Tottakai siinä on vähän tottakin, sillä ainoa turvallinen PC on se joka on sammuksissa, rikki ja merenpohjalla.

- naurasko vaiko itkis: jos joku ei toimi mitenkään muuten niin laita sen eteen eval.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 06.08.11 - klo:10.59
Lainaus
Mutta aivan kuten aikoinaan goto-käskyn kanssa kunniallista syytä vaarallisuuteen ei esitetä, puhutaan vain epämääräisesti tietoturvasta.
Olettaisin tai ainakaan minulla ei ole parempaa tietoa tuosta "goto" -käskystä kuin että runsaasti viljeltynä se tekee koodista "spagettia", rikkoo koodin lohkorakennetta, toiminnan/rakenteen seuraaminen isommassa ohjelmassa vaatii pyörittelyä ja etsimistä sieltä täältä.  En keksi syytä miksi siitä olisi jotain teknistä "vaaraa", sitä vaan pidetään huonona rakenteellisena toteutuksena.   Ensimmäisellä kerralla voi olla helppoa kutoa tuo spagetti mutta kun vuoden päästä siihen tarvitaan jokin pieni muutos voi olla jo hankalampaa. 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 06.08.11 - klo:12.33
...
 Ensimmäisellä kerralla voi olla helppoa kutoa tuo spagetti mutta kun vuoden päästä siihen tarvitaan jokin pieni muutos voi olla jo hankalampaa.  

En kiistä; kyllähän goto houkuttelee tekemään spagettia ja BASH+goto houkuttelisi vielä enemmän. Mutta sen kiistän että niin goto kuin mikään muukaan käsky olisi aina paha.  

Goto+label toimii varmaan BASHissa vieläkin koska grep löytää niitä binäärisistä skripteistä joista ei saa ohjelmalistausta että näkisi varmasti, ja kokeilla en halua sillä en kuvittelisikaan käyttäväni niitä sillä niistä oli niin hirveä huuto aikanaan oli että sieluuni jäi vamma.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 06.08.11 - klo:18.48
Goto+label toimii varmaan BASHissa vieläkin koska grep löytää niitä binäärisistä skripteistä joista ei saa ohjelmalistausta että näkisi varmasti

Bashissa ei ole goto-komentoa, eikä mahdollisuutta esikääntää skriptejä. Ne "binääriset skriptit" ovat siis todennäköisesti jollain muulla kielellä kirjoitettuja ja käännettyjä ohjelmia. Jotkut shell-skriptit voivat myös sisältää pätkiä muilla kielillä kirjoitettua ohjelmakoodia. Ainakin perl-skripteissä näkyy usein goto-käskyjä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.08.11 - klo:17.02
BASH:issa ei tosiaan ole goto-komentoa - sillä BASH:issa koko sana goto jätetään pois vaikka toiminta säilyykin - itseasiassa koska myös palataan niin kyseessä on "gosub x of ..." tyyppinen sirpalekäsikranaatti.

Ei siis voi pompata rivinumeroon eikä label:iin vaan ainoastaan funktioon. Funktioon mennään kun sen nimi mainitaan joko selväkielisenä nimenä tai muuttujan arvona. Varsinkin kun mennään muuttujan arvon mukaiseen paikkaan on syytä kirjoittaa esimerkiksi näytölle tai tiedostoon mitä tehdään:

- sinne mihin pompataan: echo "tänne: ${FUNCNAME[0]} tultiin paikasta:" ${FUNCNAME[1]}     
- sinne mistä lähdetään: echo "${FUNCNAME[0]} riviltä: $LINENO  hypätään funktioon:"$funktion_nimi
- echo "skriptin nimi on: ${@: -0}"
- echo kutsupino: ${FUNCNAME
- kutsupinon merkitys: funktiolla tulee olla tieto mihin palata - tämä pino on kylläkin käyttäjän kannalta vain nähtäväksi. Kun johonkin funktioon mennään laitetaan paluuosoite pinoon ja palattua se poistetaan. Koska funktiot voivat kutsua toisiaan niin pinosta voi tulla huomattava - myös rekursiossa jokaisella kutsulla pino kasvaa.

- jos ei tuommoisia lisää niin vuosien kuluttua on tekijälle itsellekin täysi mysteeri kuinka johonkin on jouduttu.
- jos toteutus on sellainen että näyttö menisi sekaisin täytyy tulostaa tiedostoon. Silloin vain lisätään perään: > tiedostonimi.

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

Parametritkin palautuvat automaattisesti - ellei hölmöile niinkuin usein neuvotaan. Käytännössä tuota parametrien siirtymistä kuvaava skripti voi olla seuraavantyyppinen:

Koodia: [Valitse]
#!/bin/bash
function lue () {
 echo -n "kirjoita teksti "$1":"
 read -e $1  # kirjoitettavaa voi editoida ja ääkkösetkin toimii
             # sanoissa saa olla useampiakin epämääräisen pituisia välilyönti-jaksoja
             # myös päätteen historiasta saa tekstiä nuolinäppäimillä.
}

lue a        # huomaa että parametri on muuttujan nimi eikä arvo.
echo "a sai arvon:$a"
lue b
echo "a on vieläkin:$a    ja b sai arvon:$b"


Koska funktiot ovat BASH:in "goto-käskyn" oleellinen osa on niitä syytä kuvata:

Funktio voidaan muodostaa kahdella tavalla:
1. funktion_nimi () { normaali BASH-skripti ; } # jolloin funktio jakaa muistialueen pääohjelman kanssa. Muodostuva funktio avautuu samaan prosessiin kutsujansa kanssa joten se on nopea - kutsu kestää noin 15 mikrosekuntia. Tätä tapaa käytetään lähes aina.
2. funktion_nimi () ( normaali BASH-skripti ; ) # siis sulut ovat muuttuneet kaarisuluiksi. Tällöin funktio muodostetaan omaan prosessiinsa joten sillä on muunmuassa ihan oma muistiavaruus. Sen kutsu kestää noin 350 mikrosekuntia, ja parametrien palauttaminen vaikeutuu. Mutta toiminnallisesti se on melkein samanlainen.
 
BASH:in funktioiden ominaisuuksista:
1. Funktio voi kutsua itseään ihan samallatavalla kuin muutkin. Rekursio on siis helppo toteuttaa. Ja rekursiosta voidaan palata joko lopetusehdon täytyttyä tai määräkertojen jälkeen tai...
2. jos mitään muuta ei määrätä palataan funktiosta kun se on kokonaan suoritettu ja palataan siihen lauseeseen mikä on heti kutsunjälkeen. Funktiossa voi kylläkin olla return-käsky kesken koodin mutta sekin palaa kusun jälkeiseen lauseeseen. Mutta on leegio menetelmiä joilla voi palata ihan jonnekin muualle - esimerkiksi toiseen funktioon ja kutsupinoa käsittelemällä paluu sieltä tapahtuu pääohjelmaan - tai esimerkiksi ctrl-c:llä funktioon joka selvitää miksi äsken hyydyttiin tai sekoiltiin.

- funktiolla voi olla melkeinpä millainen nimi hyvänsä, esimerkiksi ! tai ^ kelpaa hyvin tai mikä hyvänsä lause jossa ei ole välilyöntiä - välilyönnin voi korvata merkillä _ .

- pääohjelmassa voi kutsua vain funktioita jotka on määritelty jo. Mutta funktioissa voi kutsua sellaista joka määritellään vasta myöhemmin. Liitetyistä kirjastoista voidaan aina kutsua.
- normaalisti funktioiden ja pääohjeman muuttujat ovat yhteisiä joten parametrien palauttamiseen ei ole aina syytäkään. Mikäli funktiokutsussa on annettu sen muuttujan nimi jota halutaan muuttaa niin koska funktiossa voidaan määrätä esimerkiksi: read $1 ei parametria edes tarvitse palauttaa.
- pahat pojat väittävät ettei funktio kykene palauttamaan parametreja. Sillä on kuitenkin merkitystä vain silloin kun käytetään funktiokutsua joka toimii omassa prosessissaan - siis tuo aikaisemmin kuvattu tapa 2 jota ei juuri koskaan käytetä. Mutta keinoja palauttaa sittenkin parametreja on monia:
1. funktio voi kirjoittaa levylle ja pääohjelma lukee sen sieltä. Ja RAM-levyllä homma on nopeaakin.
2. Kuvaannollisesti käsky: a=$(joku_funktio jolla voi olla parametrejakin) lukee näytöltä mitä joku_funktio on tulostanut. Keino toimii olipa funktio globaalialueella tai omassa funktiossaan. Voidaan soveltaa useammallekin muuttujalle.
3. funktiolle annetaan parametriksi muuttujan nimi ja sitten koodissa jossakin määrätään esimerkiksi: read $1  - silloin syötetyn muuttujan arvo muuttuu. Ei siinä mitään eval-käskyä tarvita. Vaikka tämä menetelmä toimii vain globaalialueen funktioissa niin sillä on merkitystä koska silloin funktiota voidaan kutsua käsittelemään erinimisiä muuttujia niin että syötetyn muuttujan arvo muuttuu. Näin voidaan käsitellä vaikka kuinkamontaa muuttujaa.

Koodia: [Valitse]
- funktioille voi syöttää kahdentyyppisiä parametreja:
  1. Arvo (by value). Tällöin vastaanottavassa päässä parametreihin viitataan: $1, $2 ....$N. Tai entisvanhaan: ${@:N} jolloin N:llä ei ole rajaa. Lopustapäin laskettuna toiseksiviimeinen: ${@:(-2):1}
  2. Nimi. (by reference). Tällöin vastaanottavassa päässä parametriin viitataan:  =${!N}    
- seuraavilla parametreilla on erikoismerkitys:
$* parametrien joukko $0 mukaanlukien ( $0 on funktion nimi )
$@ parametrien joukko, sama kun kirjoittaisi "$1" "$2" "$3" .... Parametri $0 siis jää pois
$# parametrien lukumäärä, parametria $0 ei lasketa mukaan. Siis: [ $# -lt 1]" -> mikäli parametreja ei ole
$$ ytimen tunnus
$! viimeksi suoritetun prosessin tausta-prosessin tunnus
$? viimeksi suoritetun  käskyn paluuarvo
$- oletusarvo?
$_ heti kutsun jälkeen suoritettavan skriptin täydellinen tiedostonimi ( siis polkuineen). Muulloin viimeiseksi suoritetun käskyn viimeinen argumentti.   

Ohjelmille voidaan antaa myös optioita, miinus-merkin edeltämiä kirjainsarjoja tyyliin: ls -lpr

parametrien joukko          ${@}   
\$1 --------------------->  $1       
\$2 --------------------->  $2       
skriptin prosessinimi       ${0}     # joka on BASH_SOURCE pääohjelmassa   
skriptiä polku ja nimi      ${0%/*} 
oma nimeni                   ${0##*/}



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 14.08.11 - klo:04.43
Linuxin (ja myös Ubuntun) käsky ls ei tulosta tiedostonimiä oikein jos niissä on välilyönti. Teinpähän korvaavan funktion joka huomioi oikein myös sellaiset tiedostonimet joissa on välilyönti - tosin se on vielä ihan kapula-asteella mutta pääasiahan on että se toimii ja on riittävän nopea - ja ideanhan siitä saa revittyä mihin skriptiin tahansa.

Koodia: [Valitse]
#!/bin/bash
function tiedostolistaus () {
kansio=$@ # kun listattavaksi tulee kansio jonka nimessä on välilyönti siirtyy jokainen sana omassa parametrissaan ja ne täytyy liittää yhteen
echo '( ' > delme
find "$kansio" -maxdepth 1 -name "*" -type d | sort | while read Rivi  
do
  [[ $Rivi = $kansio ]] && echo '".edellinen kansio."' >> delme || echo -n \'${Rivi##*/}\'' ' >> delme
done
find "$kansio" -maxdepth 1 -name "*" -type f | sort | while read Rivi  
do
  echo -n \'${Rivi##*/}\'' ' >> delme
done
echo ' )' >> delme
eval Valittavat=$(cat delme )
rm delme
}

read -p 'minkä kansion listauksen haluat: ' kansio
tiedostolistaus "$kansio"  
echo -e ' '"${Valittavat[@]/%/\n}"
read -p 'paina enter jatkaakseesi'

- tiedostonimissä olevat välilyönnit ovat ongelmallisia kaikissa tehtävissä, mutta suurin osa selviää noudattamalla muutenkin järkevää periaatetta että tekstijonot täytyy laittaa heittomerkkien väliin. Esimerkiksi välilyönnin sisältävät tiedoston kotihakemistoosi saat käskyllä: touch /home/$(whoami)/"katin kontit" ja poistettua käskyllä: rm /home/$(whoami)/"katin kontit"

- myös kansioiden nimissä sallitaan välilyönnit

 Mutta esimerkiksi editoitaessa täytyy joissakin tapauksissa käytää semmoistakin rakennetta kuin: eval gedit "tiedostonimi".

- tarkoituksena on kiertää ongelmia joita esitetään sivulla: http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29 tavalla joka ei vaadi minkäänlaisia muutoksia tiedostonimeen. Vika on käskyn ls toteutuksessa; tiedostojärjestelmä sinällään sallii melkein mitävain; esimerkiksi kansion jonka nimi on: * tai joka sisältää mitä merkkejä vaan (paitsi kova lainausmerkki: ' )

muoks: lisätty yksinkertainen ajuri
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 15.08.11 - klo:16.19
Linuxin (ja myös Ubuntun) käsky ls ei tulosta tiedostonimiä oikein jos niissä on välilyönti.

ls -b
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 16.08.11 - klo:10.50
Lainaus
Vaan itseasiassa kyse oli siitä että tuollaiset nimet täytyy laittaa lainausmerkkien väliin...

Lisäksi käytetään "pehmeää" ja "kovaa" lainausta:

"Tämä on pehmeä lainaus"
'Tämä on kova lainaus'

Sangen usein joudutaan käyttämään lainauksia "suojaamaan" eli osoittamaan että nuo sanat joiden välissä on välilyöntejä tarkoittaakin yhtä asiaa.  Tiedostonimien kanssa törmää tuohon usein.

Kannattaa myös kokeilla tulostaa lainausmerkki echo: n avulla  :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 16.08.11 - klo:11.49
Vaan itseasiassa kyse oli siitä että tuollaiset nimet täytyy laittaa lainausmerkkien väliin että niitä voisi käsitellä. Esimerkki: luo tiedosto: touch "nimi jossa on välilyönti". Käske: ls -> tulostuu ihan oikein. Käsky: gedit nimi jossa on välilyönti  
ei onnistu. Sensijaan onnistuu kun käskee:  gedit "nimi jossa on välilyönti"
Tai sitten:
Koodia: [Valitse]
touch nimi\ jossa\ on\ välilyönti
gedit nimi\ jossa\ on\ välilyönti
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 17.08.11 - klo:16.44
Kätevää. En heti huomannutkaan. Oletan että olette vanhoja ohjelmoijia niinkuin minäkin ja minun ja niin varmaan teidänkin mielestänne tuommoinen on oikeastaan ihan hyväksyttävää sillä ketä tuo haittaa jos lauseessa on muutama keno ylimääräistä? Mutta nuoret käyttäjät eivät hyväksy mitään mikä viittaakaan meidän vanhuksien meininkeihin ja BASHin tulevaisuus riippuu heistä. Todella kätevä silti tuo: ls -b .

Kun tiedostonimi alkaa miinuksella niin <ls -b> tulostusta täytyy vähän mukaella: gedit -- -alkuinen\ nimi.
Mutta entäpä jos alussa onkin --  ? En löytänyt konstia mutta eiköhän semmoinen jostain löydy. (optioiden eteen tulee joskus -- )
Mutta entäpä jos alussa onkin $-merkki ? Eteen keno: gedit \$alkuinen\ nimi . Näitähän riittää, ja usein ne vaativat pieniä muutoksia. Mutta tuo esittämäni selviää sinällään lähes kaikista. Mutta niinkuin sanoin se on vielä kapula-asteella. Esimerkiksi vasta kun Ajaaskel puhui kovasta ja pehmeästä lainauksesta tajusin muuttaa lainat  koviksi.

Tai kansio jonka nimessä on merkki * . Silloin ei käsky ls toimi.

Editoitaessa skriptiä tai sen dokumenttia olisi kiva jos editoriin avautuisi eri lehdille myös muutamia aikaisempia editointeja koska silloin on helppo kokeilla mikä toimii sillä vanhaan on helppo palata. Tämä tuo esiin nuo käskyt: eval gedit .... Ja silloin ei kenoja tai muita virityksiä hyväksytä; siis mitään ylimääräistä ei hyväksytä mutta erikoismerkit ja ääkköset kyllä hyväksytään.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 17.08.11 - klo:19.59
Kun tiedostonimi alkaa miinuksella niin <ls -b> tulostusta täytyy vähän mukaella: gedit -- -alkuinen\ nimi.
Mutta entäpä jos alussa onkin --  ? En löytänyt konstia mutta eiköhän semmoinen jostain löydy. (optioiden eteen tulee joskus -- )

Hoituu samalla tavalla: gedit -- --abc

Jos haluat saada ls:llä aikaan listauksen, josta voi suoraan poimia mitä tahansa tiedostonimiä komentoriville, se onnistuu parhaiten parametrilla --quoting-style=shell tai --quoting-style=shell-always. (- ja -- -alkuiset nimet ovat tosin tällöinkin hankalia.)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.08.11 - klo:07.17
Ihan tosissani kysyn sillä en ole saanut virallista koulutusta Linuxiin: luuloja on ja netistähän löytää vaikka mitä ja kirjojakin on mutta eivät ne kunnon koulutusta vastaa.

Käsittääkseeni tiedostonimeen ei normaalisti kannata laittaa erikoismerkkejä niinkuin esimerkiksi merkkiä " . Kuitenkin skriptini salli sen niinkuin ainakin merkit - ja $; nautiluskin näyttää ne ihan kiltisti, ja niitä voi käsitellä normaalisti. Kieltämättä vaikeuksia tulee kokoajan vastaan vaan tuntuvatpa selviävän.

- merkkiä ' se ei käsittele oikein ja taitaa jäädä ikuisesti toimimattomaksi. Merkki / on vielä pahempi.
- niin muuten myös erikoismerkkejä sisältävät tiedostonimet näkyvät skriptiajurissa semmoisenaan, esimerkiksi: "vaikea tiedostonimi" (lainausmerkkien kanssa) tai $dollari poikineen  ja käyttö tapahtuu samoin kuin tavallistenkin nimien.
- nyt olen kokeillut ääkkösillä runsaasti, mutta vain paikallisella levyllä. Toimii teoriassa aivan kivasti. Mutta vaikeuksiakin on: esimerkiksi käsky basename toimii joskus kummallisesti varsinkin kun sekä kansionnimessä että tiedostonnimessä on erikoismerkkejä.

Hölmöyshän tässä vaivaa: minunpuoleltani koko juttu alkoi siitä, että kun nautiluksella tekee tiedostosta backupin samaan kansioon tiedoston nimeltä: <tiedostonimi> (kopio), siis sekä välilyönti että sulkumerkki. Osittain homma on siis virallisesti hyväksytty.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 18.08.11 - klo:09.40
Dashin l. tavuviivan käsittelyn optiona l. vipuna voi kiertää myös lisäämällä nimeen polun.
Koodia: [Valitse]
gedit ./--abc
Dollariin puree myös yksinkertainen (kova) hipsu, mutta keno on monesti kätevämpi. Cygwin käyttäjille vinkki:
Koodia: [Valitse]
rm -r \$Nt*windows hakemistossa tekee kivasti lisää  levytilaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.08.11 - klo:20.15
Aikoinaanhan kovasti varoiteltiin ettei pidä käyttää ääkkösiä skriptien nimissä ja jopa tehtiin skriptejä jotka muuttaa ne tavallisiksi aakkosiksi kaikkialla koneessa.

Jo parivuotta olen käyttänyt skriptien nimissä ääkkösiä eikä koskaan ole tullut mitään harmia. Funktioiden nimissä käytin myös ääkkösiä pari-vuotta sitten ja toimi kivasti, mutta jotenkin tuntui siltä että on viisainta hylätä ääkköset funktionimissä.
Mutta mikähän on noiden ääkkösten tilanne Ubuntussa virallisesti ottaen ?

Käsittääkseeni paikallisten levyjen kanssa ei tule ongelmia muiden kuin / kanssa? Mutta vekkokovalevyt ja muut joita käytetään oman käyttöjärjestelmänsä kautta ovat epävarmoja, ja vain UTF-8:a käyttävät toimivat ongelmitta?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.08.11 - klo:14.38
Skripti on teoriassa lyhyt sillä skriptintekemisen vaikeudet kasvavat skriptin pituuden myötä exponentiaalisesti.

Jos kuitenkin tekee pitkän skriptin niin tekemisen vaikeuksista yksi pahimpia on se että järjestelmään on usein lisätty käskyjä paketeilla ja kun Ubuntun kehittäjät päättävät ettei jotain pakettia enää laitetakaan peruskokoonpanoon lakkaavat jotkut skriptit toimimasta ja ilmoittavat: "käskyä ei löydy" mutta kukaan ei kerro mikä käsky sen on aiheuttanut ja millä rivillä se on.

Nämä ilmoittava funktio on tekeillä, mutta on vasta ihan alkutekijöissään eikä funktio vielä etsi sitä onko käsky kenties jossain tunnetussa paketissa mutta sen pitäisi olla pieni ongelma ja eiköhän se kohtapuoleen tule.

Lisäät vain skriptiisi yhden funktion eikä mitään muuta edes voi tehdä, ei mitään viittauksia siihen eikä mitään muutakaan:      

function command_not_found_handle () {
  echo "ohjelma:"${BASH_SOURCE}" Rivino:"${BASH_LINENO}
  echo "seuraavaa käskyä ei löydy: \""$1\"""
  echo "käskyn argumentit ovat: \""$2\"" \""$3\"""   # $4, $5 ...
  read -p '- jokakerran kun komentoa ei löydy niin pysähdytään ja jatketaan kun painetaan enter'
}

- kyseessä on bash4:n ominaisuus = toimii vain uudehkoilla Ubuntuilla.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 22.08.11 - klo:15.47
Lainaus
"command not found" eikä missään kerrota mikä käsky sen on aiheuttanut

Käytännössä tuo kai menee niin päin että jotkut "ulkoiset" komennot ovat vakiona kaikissa jakeluissa ja sellaiset jotka liittyvät esim. desktop -ympäristöön (Gnome, KDE, ...) joutuu skriptissä itse testaamaan ensin eli joko mikä desktop tai sitten itse komentoa löytyykö se koneesta ennen kuin käyttää sitä asioiden tekemiseen ohjelmassa.
Itse koen hankalimmaksi skriptien kirjoituksessa pikkutarkan syntaksin "tyhjän tilan" suhteen, esimerkiksi jossain pitää olla välilyönti ja jossain toisessa paikassa sitä ei saa missään tapauksessa olla.
Hauskinta bash: in kanssa on että lyhyelläkin koodin pätkällä voi saada ihmeen paljon aikaan. Netistä löytyy usein käyttökelpoisia ideoita jonkin asian toteuttamiseen.  Niitä kannattaa hieman keräillä aina kun osuu silmään jokin kätevä tapa tai idea.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.08.11 - klo:16.13
Itse koen hankalimmaksi skriptien kirjoituksessa pikkutarkan syntaksin "tyhjän tilan" suhteen, esimerkiksi jossain pitää olla välilyönti ja jossain toisessa paikassa sitä ei saa missään tapauksessa olla.

Puhut asiaa; ainakaan minun päässäni ei säännöt pysy ja jokakerta menee kokeiluksi.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 23.08.11 - klo:15.09
Lainaus
"command not found" eikä missään kerrota mikä käsky sen on aiheuttanut
Riippuu mistä roikkuu eli noinhan siinä käy, jos komennat jotain odottamatonta
Koodia: [Valitse]
$ ärkele
ärkele: command not found
mutta viisaampia neuvoja tulee, jos ohjelma on pakettivarastossa, vaikkei asennettu
Koodia: [Valitse]
$ ncftp
The program 'ncftp' is currently not installed.  You can install it by typing:
sudo apt-get install ncftp
tai jos jotain samankaltaista olisi tarjolla
Koodia: [Valitse]
$  zedit
No command 'zedit' found, did you mean:
 Command 'gedit' from package 'gedit' (main)
 Command 'dzedit' from package 'dzedit' (universe)
 Command 'xedit' from package 'x11-apps' (main)
 Command 'edit' from package 'mime-support' (main)
 Command 'nedit' from package 'nedit' (universe)
 Command 'iedit' from package 'xnc' (universe)
 Command 'jedit' from package 'jedit' (universe)
 Command 'medit' from package 'medit' (universe)
 Command 'ledit' from package 'ledit' (main)
zedit: command not found
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 23.08.11 - klo:18.16
mutta viisaampia neuvoja tulee, jos ohjelma on pakettivarastossa, vaikkei asennettu
Koodia: [Valitse]
$ ncftp
The program 'ncftp' is currently not installed.  You can install it by typing:
sudo apt-get install ncftp

Tuollaisia neuvoja saa kuitenkin vain vuorovaikutteisessa päätteessä, eli skriptiä pitäisi ajaa bashin -i -vivulla. Tällä puolestaan on useita muita sivuvaikutuksia. Kuten ajaaskel jo totesi, skripteissä normaali käytäntö on tarkistaa kaikkien niiden kutsuttavien ohjelmien olemassaolo, joiden ei voida olettaa kuuluvan kohdejakelun peruskokoonpanoon.

Paketoiduissa skripteissä tilannetta helpottaa paketin pakollisten riippuvuuksien määrittely, mutta silloinkin skriptissä on syytä olla samat tarkistukset ja selkeät virheilmoitukset.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 29.08.11 - klo:02.55
Vipu -i ei tuntunut toimivan kun käskyn nimi on aivan eri kuin paketin nimi kuten lähes poikkeuksetta *tools paketeissa määriteltyjen käskyjen kanssa on.
Saattaa hyvinkin olla että kyseiset käskyt kuuluvat luokkaan 'obsolete, vanhentunut' tai että vain kokeilin väärin. Kiinnostaisihan tuo tietää, mutta tähän asiaan se ei vaikuta: kyseinen paketti pitäisi saada automaattisesti ladattua. Ainakin omaan koodiin sen saa helposti.

Koska grep-aptavail on määritelty paketissa dctrl-tools tekee skripti rekursion jonka jälkeen se vasta tietää ladata oikean paketin. Tottakai rekursio on paha juttu ja siitä kannattanee pyrkiä eroon. Mutta tulipa kokeiltua ja toimii tällähetkellä niinkuin toimii 'normaaleillakin' käskyillä.

Tässä koodi uudistettuna ja mukana tälläkertaa pieni yhdenrivin pääohjelma mukana testaustarkoituksessa. Pääohjelman tilalla voi olla mikätahansa skripti jolloin jokaisella ajokerralla ladataan yhden tuntemattoman käskyn paketti niin kauan että kaikki käskyt on määritelty. Hidasta ja huonoa vielä, mutta kun toiminta on puoli-automaattista niin tietää virheitä korjatessaan tarkemmin missä mennään.

- mikäli käskyn edessä on sudo toiminta taitaa siirtyä root:in command_not_found_handleen; kai sen joskus kerkiää kokeilla.

Koodia: [Valitse]
#!/bin/bash

function command_not_found_handle () {
  komennon_nimi=$1
  shift
  echo "ohjelma:"${BASH_SOURCE}" Rivino:"${BASH_LINENO}
  echo "seuraavaa käskyä ei löydy: \""$komennon_nimi\"""
  echo "käskyn argumentit ovat: ""$@"
  echo
  kaskysta_on_puhuttu_paketeissa=$(apt-cache search $komennon_nimi)
  [[ $(echo $kaskysta_on_puhuttu_paketeissa | grep ^$1) ]] && paketin_nimi=$(echo $kaskysta_on_puhuttu_paketeissa | grep ^$1) ||
paketin_nimi= $(echo $kaskysta_on_puhuttu_paketeissa | awk {'print $1'}) # onko tämä or-osa tarpeen ?
  echo " paketin määrittelyä: "$paketin_nimi #; read
  sudo apt-get install ${komennon_nimi} || echo "siitä on puhuttu paketeissa: "$kaskysta_on_puhuttu_paketeissa
  exit
}

grep-aptavail bar # halutaan tutkia tarkemmin niitä paketteja joissa komennosta bar puhutaan. Ladataan käskyn grep-aptavail määrittelevä paketti mikäli sitä ei jo ole.
# atop
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 08.10.11 - klo:05.25
...
Googlella löytää kyllä ideoita. Ja 'yhdenrivin skriptejä' - joista useat jopa toimii.
...

Törmäsin kiusalliseen kuvioon ylen liveradion kanssa.
Ajoittain otan talteen jotain komennolla:
mplayer -dumpstream -cache 1024  -dumpfile $outfile &
ja kutsuvassa skiptissä on sleep joka ampuu homman alas annetun ajan jälkeen.

Mutta, liian usein eka yritys epäonnistuu ja outfileen tallentuu vain:
[Reference]
Ref1=http://mediak.yle.fi/liveradiosuomi?MSWMExt=.asf
Ref2=http://10.1.6.5:80/liveradiosuomi?MSWMExt=.asf

Ja komento 'file' paljastaa myös, että kyse ASCII -tekstistä eikä  M$:n ASF:sta

Saako tuon kutsun loopattua jotenkin, siis niin kauan kun outfile on tekstiä niin yrittää uudelleen ja uudelleen. (Toki annetun rajan verran yrityksiä)

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 08.10.11 - klo:13.28
Ehkä näin: Katso heksaeditorilla mitä siellä on tiedoston alussa kun kaikki toimii ok.  Jos uuden tiedoston alku ei sisällä tuota niin edetään sen mukaisesti.  Siellä saattaa lukea "ID3" jos teet mp-kolmosia.  Tai saahan sen tuon "file": n avulla myös --- vai oliko itse looppaaminen se ongelma ?
Testi ensin tiedoston sisällölle ja "while" looppi joka yrittää uusiksi ja testaa tiedoston statuksen sitten ?  Vai oliko tuossa jokin muu juttu ?   Auttaisiko muuten "fuser" niin että ei tarvitse odottaa jotain kiinteätä aikaa ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 08.10.11 - klo:17.45
Ehkä näin: Katso heksaeditorilla mitä siellä on tiedoston alussa kun kaikki toimii ok.  Jos uuden tiedoston alku ei sisällä tuota niin edetään sen mukaisesti.  Siellä saattaa lukea "ID3" jos teet mp-kolmosia.  Tai saahan sen tuon "file": n avulla myös --- vai oliko itse looppaaminen se ongelma ?
Testi ensin tiedoston sisällölle ja "while" looppi joka yrittää uusiksi ja testaa tiedoston statuksen sitten ?  Vai oliko tuossa jokin muu juttu ?   Auttaisiko muuten "fuser" niin että ei tarvitse odottaa jotain kiinteätä aikaa ?

Joo, siis looppaaminen on hukassa, "file" kyllä paljastaa sen sisällön.
(Pitääpä yrittää kurkata mitä mplayer palauttaa silloin kun heittää pyyhkeen kehään kun ei löydä streamia - josko mitään.)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 09.10.11 - klo:13.32
Törmäsin kiusalliseen kuvioon ylen liveradion kanssa.
Ajoittain otan talteen jotain komennolla:
mplayer -dumpstream -cache 1024  -dumpfile $outfile &
ja kutsuvassa skiptissä on sleep joka ampuu homman alas annetun ajan jälkeen.

Mutta, liian usein eka yritys epäonnistuu ja outfileen tallentuu vain:
[Reference]
Ref1=http://mediak.yle.fi/liveradiosuomi?MSWMExt=.asf
Ref2=http://10.1.6.5:80/liveradiosuomi?MSWMExt=.asf

YLE on siitynyt nettilähetyksissään Flash/RTMP-tekniikkaan ja nuo vanhat ASF-streamit saattavat hävitä kokonaan jossain vaiheessa. Ylen nykyiset RTMP-nettiradiot saa talteen RTMPDump-YLEllä (http://users.tkk.fi/~aajanki/rtmpdump-yle/index.html). Näiden lähetysten etuna on myös MP3-muotoinen ääni WMA:n sijaan, joskin ääniraita on FLV-säiliön sisällä (http://linux.fi/wiki/RTMPDump-YLE#Radio-ohjelmat_mp3-muodossa).

Radio Suomi tallentuu komennolla:

Koodia: [Valitse]
yle-dl "http://areena.yle.fi/player/index.php?channel=3&language=fi"
Ajaaskel on julkaissut nämä asennuspaketit:

64-bittinen: http://www.homelinuxpc.com/download/rtmpdump-yle_20110714-0_amd64.deb
32-bittinen: http://www.homelinuxpc.com/download/rtmpdump-yle_20110714-0_i386.deb
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 09.10.11 - klo:20.32
Törmäsin kiusalliseen kuvioon ylen liveradion kanssa.
Ajoittain otan talteen jotain komennolla:
mplayer -dumpstream -cache 1024  -dumpfile $outfile &
ja kutsuvassa skiptissä on sleep joka ampuu homman alas annetun ajan jälkeen.

Mutta, liian usein eka yritys epäonnistuu ja outfileen tallentuu vain:
[Reference]
Ref1=http://mediak.yle.fi/liveradiosuomi?MSWMExt=.asf
Ref2=http://10.1.6.5:80/liveradiosuomi?MSWMExt=.asf

YLE on siitynyt nettilähetyksissään Flash/RTMP-tekniikkaan ja nuo vanhat ASF-streamit saattavat hävitä kokonaan jossain vaiheessa. Ylen nykyiset RTMP-nettiradiot saa talteen RTMPDump-YLEllä (http://users.tkk.fi/~aajanki/rtmpdump-yle/index.html). Näiden lähetysten etuna on myös MP3-muotoinen ääni WMA:n sijaan, joskin ääniraita on FLV-säiliön sisällä (http://linux.fi/wiki/RTMPDump-YLE#Radio-ohjelmat_mp3-muodossa).

Radio Suomi tallentuu komennolla:

Koodia: [Valitse]
yle-dl "http://areena.yle.fi/player/index.php?channel=3&language=fi"
Ajaaskel on julkaissut nämä asennuspaketit:

64-bittinen: http://www.homelinuxpc.com/download/rtmpdump-yle_20110714-0_amd64.deb
32-bittinen: http://www.homelinuxpc.com/download/rtmpdump-yle_20110714-0_i386.deb

Nojoo, tuo selittääkin jo tarpeeksi.
Ja jos oikein muistan niin olen jollain kuorinut tuon kääreen pois ja lopputulema kelpaa audacitylle :)

Joten muutan nyt ensi alkuun laturini käyttämään tuota yle-dl:ää.

Edit: ei ollutkaan ihan läpihuutojuttu ;(
Se aiempi laturini kutsui suoraan mplayeria ja sen sain pyydettyä lopettamaan kun oli aika täysi.
Tuo yle-dl onkin erillinen kuori ja sen pysäyttäminen ei näköjään lopeta tallennusta....
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 10.10.11 - klo:10.45
Auttaisiko "pkill yle" tuohon ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 10.10.11 - klo:12.42
Auttaisiko "pkill yle" tuohon ?

En usko, koska ajossa oleva prosessi on eri nimellä.
Mutta tuollai sen noukin:
PID=`ps -ef |grep rtmpdump|head -n1|awk '{print $2}'`
ja sitten
kill $PID

Kokeilematta vielä jos on parikin laturia samaan aikaan ajossa, niin kumpi tapetaan ;(

Ryystän nuo radiolähetykset cronitettuna ja mahdolliset areenan videopätkät satunnaisesti - eli ihan mahdollista että useampi ajossa samanaikaisesti.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 10.10.11 - klo:13.48
Edit: ei ollutkaan ihan läpihuutojuttu ;(
Se aiempi laturini kutsui suoraan mplayeria ja sen sain pyydettyä lopettamaan kun oli aika täysi.
Tuo yle-dl onkin erillinen kuori ja sen pysäyttäminen ei näköjään lopeta tallennusta....

Pysäytä lataus, kun N sekuntia lähetettä on tallessa:

yle-dl --stop N "http://areena.yle.fi/player/index.php?channel=3&language=fi"

Huomaa myös parametri -o, jolla tallenteen voi nimetä haluamallaan tavalla, eli siihen voi liittää vaikkapa oikean päivämäärän ja ajan.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 10.10.11 - klo:21.15
Edit: ei ollutkaan ihan läpihuutojuttu ;(
Se aiempi laturini kutsui suoraan mplayeria ja sen sain pyydettyä lopettamaan kun oli aika täysi.
Tuo yle-dl onkin erillinen kuori ja sen pysäyttäminen ei näköjään lopeta tallennusta....

Pysäytä lataus, kun N sekuntia lähetettä on tallessa:

yle-dl --stop N "http://areena.yle.fi/player/index.php?channel=3&language=fi"

Huomaa myös parametri -o, jolla tallenteen voi nimetä haluamallaan tavalla, eli siihen voi liittää vaikkapa oikean päivämäärän ja ajan.

Doddih!
Siinähän se - kuinkahan usein on tullut keksittyä pyörä uudelleen ;)

Näin jälkeenpäin ajatellen on ihan luontevaa että myös lopetuslaskuri moisessa palikassa.
Mutta kun tähän asti olen kiskonut vain areenasta niin siellähän tiedosto alkaa ja loppuu jossain kohtaa eikä tuota ole tarvinnut miettiä...

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Rähmäkäpälä - 20.10.11 - klo:09.50
Tarkoituksena on tekaista pieni skripti, jolla voi sammuttaa tai käynnistää kannettavan kosketushiiren. Käsittääkseni alla olevan koodin pitäisi toimia, mutta ei. Ohjelma suorittaa aina vain tuon käynnistämisfunktion, oli kosketushiiri sitten valmiiksi päällä tai ei.

Koodia: [Valitse]
#!/bin/bash

function start_function
{
xinput set-prop 12 "Device Enabled" 1
}
function stop_function
{
xinput set-prop 12 "Device Enabled" 0
}

if xinput set-prop 12 "Device Enabled" 0
then
echo "startataan touchpad"
start_function

elif xinput set-prop 12 "Device Enabled" 1
then
echo "stopataan touchpad"
stop_function
fi
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 20.10.11 - klo:20.11
xinput palauttaa käyttämälläsi komennolla arvon 0 riippumatta hiiren tilasta ja siitä kytketäänkö se päälle vai pois päältä.

Joudut siis lukemaan "Device Enabled"-muuttujan arvon list-props-tulosteesta. Tämän artikkelin lopussa on valmis skripti: https://help.ubuntu.com/community/SynapticsTouchpad/ShortcutKey
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Rähmäkäpälä - 20.10.11 - klo:22.57
xinput palauttaa käyttämälläsi komennolla arvon 0 riippumatta hiiren tilasta ja siitä kytketäänkö se päälle vai pois päältä.

Joudut siis lukemaan "Device Enabled"-muuttujan arvon list-props-tulosteesta. Tämän artikkelin lopussa on valmis skripti: https://help.ubuntu.com/community/SynapticsTouchpad/ShortcutKey
Kiitti!  :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 07.03.12 - klo:20.22
"sed",  tiedoksi jotta et painisi saman asian kanssa:  

Ihmettelin tovin miksi "sed": ssä ei rivin lopun tunnistus taalan "$" avulla toiminut sitten millään.  Syyn arvasin vasta kun kurkistin hexaeditorilla mitä siellä kulkee datana: "od oa".  "sed" ei pidä ollenkaan tuosta CR+LF lopusta.  Korvasin nuo pelkällä LF: llä ja alkoi "sed" toimia dokumenttien mukaisesti.  

Jos ihmettelet mistä tuo CR+LF oli alkuaan tullut niin kirjoittelin bash-koodinpätkän joka muuntaa RSS-syötteestä napatun .xml tiedoston taulukkomuotoon. Tässäpä pieni näyte muunnettuna YLE Beta Areenan lasten ohjelmien rss-syötteestä:

|
title=Pikku Kakkosen posti
link=http://areena-beta.yle.fi/ng/areena/tv/1429041
guid=d9a50764b44c48528c8572ec31b01f2a
description=Lasten tarinoita Peikkovuoresta. Johanna esittelee lasten lähettämiä piirustuksia.
pubDate=Fri, 2 Mar 2012 07:48:00 +0200
|
title=Taikakaruselli
link=http://areena-beta.yle.fi/ng/areena/tv/1218
guid=c8f509e91e7b474899dda5f922a3d714
description=Olli vainukoirana. Uusinta.
pubDate=Fri, 2 Mar 2012 07:22:00 +0200
|

Putkimerkki erottaa tuossa tietueita jotka muodostettu "item" -kentistä xml-tiedostossa.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.03.12 - klo:15.50
Tuntuu olevan kätevää tuo hexeditointi, sillä löytää todella vaikeita vikoja. Kerkiäisitkö neuvomaan mitä hexeditoria kannattaa käyttää ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 08.03.12 - klo:19.10
Koodia: [Valitse]
cat tiedosto.txt|hexdump

- edit

Saattaa selvyyden vuoksi käyttää vipuja esim...
Koodia: [Valitse]
cat tiedosto.txt|hexdump -C
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.03.12 - klo:21.23
Tattista, enpä tullut ajatelleeksikaan. Kenties vielä pieni vahvistus: cat tiedosto.txt|hexdump -C|gedit

- noita Linuxin varus-ohjelmia minäkin pyrin yleensä käyttämään sillä ne pakottavat käyttämään omaakin päätä jolloin löytää vikoja ihan toisella tavalla.

Googlasin vähäsen ja löytyihän noita ohjelmallisiakin hexeditoreita: tässä on ihan sama riesa kuin Linuxeissa aina - uusia parempia löytää aina vaikka etsisi loppuelämänsä. Ilmeisesti mukavin käyttää on ghex mutta kyllä hexedit:kin käy. Sitten niitä vaikeakäyttöisiä: etupää xxd ja peräpää mc , emacs, vim, maksullinen UltraEdit ...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 09.03.12 - klo:00.47
Noita on paljon mutta pakettivarastoista löytyi "bless" joka oli jäänyt käyttöön jo kauan sitten.

Koodia: [Valitse]
sudo apt-get install bless
Hyvää tuossa on ainakin se että on helpompi silmällä seurata kun klikkaa hiirellä jonnekin, näyttää punaisella molemmissa kentissä missä kohdassa mennään.

Ohjelmien editointiin on "geany" kätevänoloinen, osoittaa kirjoitusvirheen melkein sormella (tai ainakin värillä).


PS. Muunsin lopulta .xml tiedoston vielä hieman erilaiseeen .tsv muotoon, hyvin aukeaa nyt myös Libre Officeen tuo taulukko ja helppoa tehdä mielivaltaisia hakuja tietueisiin myös skriptissä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 10.03.12 - klo:22.33
awk,  sarjassa pikku havaintoja ja omia virheitä

Awk: ssa on yksi "kierous" jota en ihan heti keksinyt:   Komentorivillä ajettuna kaikki toimi hyvin mutta kun siirsin saman koodin erilliseen funktioon skriptitiedostossa niin awk ei suostunut palauttamaan mitään.  Haarukointi ositti parametrien välityksen suuntaan, jostain syystä välitys ei taalan kautta toiminut mutta "kovakoodattuna" suoraan awk: lle alkoi awk toimia.    Selvisi että tuo johtui kovan lainauksen (yksinäiset ylä pilkut 'kissa') käytöstä, kun vaihdoin pehmeän lainauksen ("kissa") niin awk alkoi toimia odotetulla tavalla.   Tuo sattuu hyvin helposti jos on pitkään kirjoitellut sed: in kanssa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 11.03.12 - klo:18.26
Noita on paljon mutta pakettivarastoista löytyi "bless" joka oli jäänyt käyttöön jo kauan sitten.

Bless vaikuttaa hyvältä hexeditorilta.

Linux on voittamaton. Tuli tarve saada lista niistä joilla on koneessa käyttöoikeus, enkä heti löytänyt sopivaa käskyä joten tein itse:

for n in {1000..1200}; do apu=$(cat /etc/passwd | grep ':'$n':' ); [[ ! $apu ]] && continue || echo ${apu%%\:*}; done
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 12.03.12 - klo:09.46
Lainaus
for n in {1000..1200}; do ....

Tuossa kokeillaan onko 1000, 1001, 1002,....  Löytyyhän se noinkin mutta tehdään aina 200 yritystä eli tämä on  "raa'an voiman ratkaisu".   
Usein tehokkain ratkaisu on keksiä seula joka mahdollisimman nopeasti poistaa ei-toivotun datan ilman että tarvitsee pyöriä silmukassa. Putkessa edettäessä työ nopeutuu putken loppua kohti datan vähetessä. Esitän tämän esimerkin teknisestä uteliaisuudesta ajatuksella oppia itse ja näyttää mullekin vaikka tuo ratkaisusi on riittävän tehokas mille hyvänsä koneelle ja tekee työnsä.

Koodia: [Valitse]
time for n in {1000..1200}; do apu=$(cat /etc/passwd | grep ':'$n':' ); [[ ! $apu ]] && continue || echo ${apu%%\:*}; done.
.
real   0m3.808s
user   0m1.132s
sys   0m0.680s

Koodia: [Valitse]
time cat /etc/passwd | grep "/home/" | awk -F':' '{ if ( $3 >= 500 ) print $1 }'.
.
real   0m0.042s
user   0m0.012s
sys   0m0.004s

Jälkimmäinen tapa on noin 91 kertaa tehokkaampi.  Selitän hieman mitä tuo tekee. Ensin listataan tuo "passwd" -tiedosto, poimitaan mukaan vain ne rivit joilla esiintyy "home". Tuossa vaiheessa datan määrä jo pieneni paljon.  Poimitaan niistä mukaan vain ne joissa kolmannessa kentässä lukee yli 500 ja tulostetaan (eli välitetään eteenpäin) näistä pelkästään kenttä numero yksi eli käyttäjätunnus.  "awk" on tuossa kätevä kun sille voi kertoa että kaksoispiste on välimerkki joka erottaa kentät. Tuohon voi tietysti valita minkä merkin kulloinkin tarvitsee kentän välimerkiksi, tässä tapauksessa tarvittiin kaksoispistettä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.03.12 - klo:10.48
Tattista, toimii todella nopeasti. Ja ilmeisesti ylärajaa käyttäjien määrälle ei ole?

- nopein taitaa olla:
Koodia: [Valitse]
awk -F: '/home/&&/bash/ {print $1}' /etc/passwd
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 12.03.12 - klo:11.01
Tattista, toimii todella nopeasti. Ja ilmeisesti ylärajaa käyttäjien määrälle ei ole?

Käytännössä ei rajaa tai en ainakaan pysty kuvittelemaan tilannetta että kohtaisit sen.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kuutio - 12.03.12 - klo:11.16
Koodia: [Valitse]
time cat /etc/passwd | grep "/home/" | awk -F':' '{ if ( $3 >= 500 ) print $1 }'
Ei tuohon cat:tia tai grep:piäkään tarvita, homma onnistuu myös suoraan awk:illa, esim:
Koodia: [Valitse]
awk -F':' '$6 ~ /home/ && $3 >= 500 { print $1 }' /etc/passwd
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 12.03.12 - klo:11.29
Koodia: [Valitse]
time cat /etc/passwd | grep "/home/" | awk -F':' '{ if ( $3 >= 500 ) print $1 }'
Ei tuohon cat:tia tai grep:piäkään tarvita, homma onnistuu myös suoraan awk:illa, esim:
Koodia: [Valitse]
awk -F':' '$6 ~ /home/ && $3 >= 500 { print $1 }' /etc/passwd

No nyt se on 103 kertaa tehokkaampi kun se edellinen oli 91 kertaa tehokkaampi. :)
real   0m0.037s
user   0m0.008s
sys   0m0.004s

Joo, nämä "awk", "sed", "grep", ym. osaavat lukea suoraan itse ilman tuota "cat" -vaihetta.  Suodatusidean havainnollistamiseen tuo putkitus on hyvä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.03.12 - klo:12.20
Pientä virhettä siinä alkuperäisessä käskyssäni oli sillä continue:n tilalle kuuluu break - mutta pää on paperia eikä muista mitään.
- break:ia voi käyttää sillä käyttäjä-ID:t  alkaa 1000:sta ja kasvavat aina yhdellä ja kun vastaan tulee ensimmäine ID jota ei vastaa nimi niin käyttäjiä ei ole senjälkeenkään.

Break:kia käytettäessä  ja pienin muutoksin päädyin seuraavaan:
time for n in {1000..1200}; do cut -d':' -f1 <<< $(grep $n /etc/passwd); [[ ! $apu ]] && break; done

Siinä nopeudet ovat jotakin semmoista kuin:
real   0m0.007s (per käyttäjä?)
user   0m0.000s
sys   0m0.000s
- ne vaihtelee vähän kerrasta toiseen - niinkuin aina ?
- aika on tilanteesta kun käyttäjiä on kaksi. Lisäkäyttäjät maksaa real-aikaan noin 7ms/nenä; muihin aikoihin ei ole näkyvää vaikutusta.
- käytän tämmöistä pääasiassa koska matriisin täyttö on helpompaa muutosten jälkeen
- time-käsky taitaa olla hieman epämääräinen näillä tuloksilla.


- taisinpa löytää tähänastisen ehdottoman voittajan, vaikkakin se on hieman epäilyttävä:
Koodia: [Valitse]
time grep home.*bash /etc/passwd | cut -d: -f1

 - siinä on tulokset luokkaa:
real   0m0.005s
user   0m0.000s
sys   0m0.000s
**
Aina paranee:
Koodia: [Valitse]
cat /etc/passwd | awk -F: '/home/&&/bash/ {print $1}'
- tämä on kaksikertaa nopeampi
**
Mutta oikein kirjoitettuna käsky on:
Koodia: [Valitse]
awk -F: '/home/&&/bash/ {print $1}' /etc/passwd
- ja taas noin kaksikertaa nopeampi. Virhetarkastelua: tuo edellinen käsky kestää noin 0,58ms 10000 mittauksen keskiarvona. Mikäli mittaukseen käytettäisiin time-käskyä niin edellisen käskyn suoritusajaksi sen tulisi näyttää 1ms, mutta "digitaalisen virheen takia" se näyttää joskus 2ms. Siis virhe on luokkaa 140% eikä sitä voi parantaa mittaamalla 10000-kertaa ja laskemalla siitä keskiarvon - mittaukseen tulee käyttää date-komentoa ja mittauksissa poistaa cachejen/buffereiden vaikutus.

 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 13.03.12 - klo:10.27
Lainaus
Bless vaikuttaa hyvältä hexeditorilta.

...paitsi että ei suostu itselläni tallentamaan muutoksia.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.03.12 - klo:11.24
Lainaus
Bless vaikuttaa hyvältä hexeditorilta.
...paitsi että ei suostu itselläni tallentamaan muutoksia.

Ohhoh. Juuri kun alkoi tottua siihen.

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 13.03.12 - klo:11.47
Löysin vian ja se on helppo korjata, sieltä puuttuu oletuksena yksi hakemistomääritys asetuksista.  

1) Käynnistä "bless".
2) Avaa "Edit" -menusta "Asetukset".
3) Kirjoita "Temporary files" -kenttään /tmp   (älä yritä mennä selaamalla hakemaan sitä !).
4) Valmis.
 
Toimii ok.  Kokeilin jo välillä "ghex" editoria, sekin toimii mutta tuo "bless" on jotenkin "käteen sopiva", kätevän oloinen käyttää.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 19.03.12 - klo:08.53
 grepillä voidaa etsiä joko tekstistä tai tiedostosta:      echo lrgblrblrbl89befgb | grep 89             tai: grep 89 tiedosto_jonka_tekstistä_etsitään 
- huomaa että kaikki tiedoston rivit käydään automaattisesti läpi, joten tulostetta saattaa tulla paljonkin

grep:illä voidaan etsiä määräämällä selväkielellä mitä etsitään, määräämällä se muotti missä etsittävä on tehty tai yhdistellen selväkieltä ja muotteja. Esimerkiksi:
echo lrbl89befg | grep 89          tai: echo lrbl89befg | grep [0-9]*

Jos muotissa on selväkielistä osia niin ne kirjoitetaan aivan niinkuin ne alunperin olivat ja lisätään seuraavia lisämääreitä:

Jos etsitään merkkijonoa jonka alku ja loppu tiedetään niin muotti on: tekstialussa.*tekstiloppussa . Tekstialussa ja tekstilopussa täytyy olla samalla rivillä
 
etsittäessä jotakin erikoismerkkiä sen erikoismerkin eteen laitetaan merkki \  . esimerkiksi kun etsitään sanaa hinta$ niin hakusana on: hinta\$

Jos hakusana on etsittävän rivin alussa kirjoitetaan muotin eteen ^ ja jos se on lopussa kirjoitetaan muotin perään $

[asdfg] etsii mitähyvansä merkeistä asdfg (ei siis etsi koko sanaa vaan mitähyvänsä noista merkeistä. Kun etsitään sanaa niin jätetään hakasulut pois).
[^asdfg] etsii mitähyvänsä muuta merkkiä kuin jokin luetelluista

Grep tuntee ennaltamäärättyjä merkkiryhmiä. Joskus hyväksytään kaksois-sulun paikalla vain yksi sulku:
[[:alnum:]]->[[A-Za-z0-9]]      [[:alpha:]]->[A-Za-z]         [[:blank:]]->välilyönti tai TAB   [[:cntrl:]]->jokin kontrollimerkki     
[[:digit:]]->[0-9]              [[:graph:]]->ASCII 33 - 126   [[:lower:]]->[a-z]                [[:print:]]->ASCII 32 - 126 ja välilyönti     
[[:space:]]->välilyönti ja TAB  [[:upper:]]->[A-Z]            [[:xdigit:]]->[0-9A-Fa-f]         [[:punct:]]->jokin erikoismerkki

useimmat erikoismerkit menettävät erikoismerkityksensä merkkien []   tai: '' välissä

.*  korvaa mielivaltaisen määrän merkkejä, ja sen paikalla ei välttämättä täydy olla merkkiä ollenkaan. Piste edessä on vaatimus.
+   korvaa mielivaltaisen määrän merkkejä, ja sen paikalla täytyy olla vähintään yksi merkki
.   korvaa täsmälleen yhden merkin, siis hakusana on: ' etsittävä ' kun halutaan painottaa että ennen ja jälkeen etsittävän on välilyönnit
 
grepin kytkimet:
-r  etsitään rekursiivisesti                          -s  ei anneta varoitusviestejä                      -c  vain löytöjen lukumäärä tulostetaan
-h  tulostaa sen tekstirivin jolta etsitty löytyy     -w  oleta etsittävän alkuun ja loppuun \< ja \>     -v  poista hakutuloksista ne joissa etsitty on osana   
-l  tulostaa sen tiedoston nimen josta etsitty löytyy -n  tulostaa myös miltä riviltä etsitty löytyi      -i  etsittäessä ei huomioida kirjainkokoa     
-F  etsitään kirjaimiltaan juuri sellaista mikä etsittäväksi määrättiin, esimerkiksi merkkiä: *           -m 1    vain ensimmäinen osuma tulostetaan
-e  optiot on nyt kirjoitettu ja haettava seuraa vaikka se alkaisi -                                      -o kirjoita yksinomaan löydetyt kukin omalle rivilleen
-P  regular expressionissa: '\w'->sana '\d\'->numeroryhmä
-E  ota käyttöön laajennoksia, mm. \ ei tarvita; siis esimerkiksi kun etsit find-käskyn "placeholdereita" niin etsittävä on: grep -lrE {} mistä_etsittään
-A <numero>  tai: -B <numero> tai: -C <numero>      tulosta <numero> riviä haetun rivin edestä, jäljestä tai molemminpuolin

Regex:ille on laajennos joka on käytettävissä kun käyttää egrepiä, tavallisella grepillä kytkintä -E tai sed:illä kytkintä: -r. Ne ovat:
+  – yksi tai useampi edellisestä merkistä
|  - grepin tai-funktio, joko: grep -e .doc -e .html -e .odt -e .sgml -e .xml      tai: grep -E *.doc\|*.html\|*.sgml\|*.xml
?  - nolla tai yksi edellistä
() - sitoo merkkiryhmät yhteen. Esimerkiksi: grep -E '*(do)*(1|2)' . Kaikki merkit juuri näin

{i\}      kuten *, mutta lukumäärä on täsmälleem i-kappaletta
{i,j\}    kuten *, mutta lukumäärä on välillä i-j
{i,\}     kuten *, mutta jotakin yli i. siis esimerkiksi: egrep ^.\{15\}A etsii sanaa jonka alussa on 15 mielivaltaista merkkiä ja heti niiden perässä A
{,1\}     edellistä kohtaa on nolla tai yksi
a{2,\}?   jokainen a-ryhmä, jossa on vähintään 2 a:ta  (2:n paikalla voi olla mikähyvänsä luku joka toimii miniminä)
\(regexp\) esim. \(abc\)* korvaa nollaa tai suurempaa joukkoa jonka jokainen jäsen on abc, kuntaas abc* etsisi joukkoa jonka edessä on ab ja perässä on nolla tai useampi c
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 23.03.12 - klo:12.42
Komennon lähetys toiseen prosessiin

Kokeilin hieman "trap" komentoa, tällä voit itse kokeilla:

Koodia: [Valitse]
#!/bin/bash
trap "echo Saatiin HUP.; exit" HUP
trap "echo Saatiin INT.; exit" INT
trap "echo Saatiin TERM.; exit" TERM
trap "echo Saatiin USR1.; exit" USR1
trap "echo Saatiin USR2.; exit" USR2
for ((i=0;i<10;i++)) do
  echo "*"
  sleep 5
done

Laitoin tuon "trap" nimiseen tiedostoon, ajo-oikeuden ja käyntiin sekä lähetin toisesta päätteestä vastaavia koekomentoja, esimerkiksi:

Koodia: [Valitse]
killall -sINT trap
Tuon pienen ässän jäkeen tulee se testattavakomento.

Näyttää siltä että tuo "sleep" ajetaan aina loppuun ja "trap" tekee työnsä sitten.  Päätteessä suoraan annettu Ctrl-C sen sijaan näytti tekevän heti.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 26.03.12 - klo:21.32
Siis ilmeisesti trap:peja voi yhtäaikaisesti olla monta ?

Toinen asia: kun lisää skriptin alkuun: set -x niin tuleehan siitä kivaa tulostusta - mutta ainakin minun mielestäni se on sotkua josta on vaikeaa saada selvää ja kyllä se skriptin korjaaminen alkaa normaalista tulosteesta. Mutta molempi parempi: vaikka skripti tekee ihan normaalin tulosteen saa siitä samanaikaisesti suunnattua tracen tiedostoonkin; eikä taida muuten hidastaakaan.
Teorian muuttaminen koodiksi oli hidasta kun oikein mistään ei löytynyt esimerkkiä ja man-sivujen soveltaminen on tässä tapauksessa vaikeaa. Tämmöisen koodin sain aikaiseksi, lieneeköhän ihan oikein vaikka toimiikin:

#/bin/bash
PS4=' ${LINENO} ${FUNCNAME[0]} '  # traceen tulee rivinumero ja mahdollisesti funktion-nimi
exec 5>>~/omatskriptit/trace  # liitä tiedostokahva numero 5 tiedostoon
BASH_XTRACEFD=5                    
set -x

KIRJOITA SKRIPTI TÄHÄN

exec 5>&-                                # sulje tiedostokahva
set +x

- tämmöinen trace vaatii BASH 4.1:tä tai uudempaa -> Ubuntu 10.04 tai uudempi ? Minulla on 12.04
- koodiin voi liittää viestejä malliin: echo -ne "  funktio nimeltä rantasalmelaisia alkaa "$n" kerran">>~/omatskriptit/trace .
- tällaista debug:gia voi käsitellä ohjelmallisesti. Vaikka tuon ohjelman joutuisi tekemään itse, niin viikossa saa aikaan debuggerin joka on parempi kuin mitenkään muuten olisi mahdollista - verrattuna siihen että mitä "tehdastekoista" debuggeria käyttääkin niin sen kaikkien sielunmutkien selvittäminen kestää kuukausia.
- eiköhän aliakset toimi, täytyy joskus tutkia. Silloin toimitaan ihan niinkuin normaalistikin.
- skriptiajurin tietokannat ovat vielä surkean pienet, mutta jo nyt ne vetävät vertoja suurillekin tietokannoille Linuxin erinomaisen hakumoottorin ansiosta; googlekin häviää jo nyt - google löytää asiaa paljon enemmän mutta asian mukana on kokoajan kasvava määriä roskaa joten googlen käyttökelpoisuus pienenee kokoajan.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 27.03.12 - klo:09.50
Lainaus
Siis ilmeisesti trap:peja voi yhtäaikaisesti olla monta ?
Eri signaaleille kuten tuossa testissä yllä, kokeile pyörittää tuota testiä.  Tuolla tavalla käynnissäolevaa skriptiä voi "kauko-ohjata" signaaleilla tekemään erilaisa asioita, esimerkiksi noihin USR1 ja USR2 signaaleihin voisi liittää jotain toimintoja eli tuonne "echo jotain; exit" tilalle voi laittaa jonkun oman funktion nimen ja kirjoitella haluamansa toiminnon siihen.

Itse käytän hyvin yksinkertaisia keinoja kun jokin ei toimi kuten kuvittelin:

1) Tulostaa muuttujan arvon epäilyttävistä kohdista "echo $muuttujan_nimi".

2) Pelkkä "read" johonkin kohtaan jolloin pelin voi pysäyttää tuohon kohtaan ja jatkaa painamalla "Enter".

3) "exit"  kohtaan josta haluaa poikki ja tilanteen tarkastus.

Usein vika on että muuttujasta löytyikin jotain odottamatonta kuten:
- Välilyönti (tai useampi)  rivin alussa tai lopussa  --> rivin alun tai lopun testaus ei toimi
- <CR> rivin lopussa,  ---> rivin lopun testaus ei toimi
Lisäksi tavanomaista:
- Pehmeä lainaus "jotain juttua tässä "  unohtunut jolloin vain ensimmäinen sana (eli "jotain" ) menee perille.
- Sijoituslauseessa ei saa olla tyhjää "=" -merkin kahden puolen, testilauseessa pitää olla.
- "$" unohtunut kun muuttujaa käytetään... :)
- Jotkut merkit pitää "eskeipata" eli laittaa "\" (backslash, kenoviiva) eteen ettei niitä kyseisessä paikassa luulla kentän erottimiksi.
- Täytyy muistaa laittaa "-E" -vipu "grep": lle jos kirjoittelee RE/ERE -lauseita, laittaa tuplahakasiin Posix merkkiluokat [[:alnum:]] jne kuten esimerkiksi täällä on kerrottu:

http://www.zytrax.com/tech/web/regex.htm (http://www.zytrax.com/tech/web/regex.htm)

Sääntöjä ja poikkeuksia on aika paljon mutta ne oppii parhaiten virheiden ja "ahaa !" -elämysten kautta. Omalla tavallaan hauskaa puuhaa vaikka nuo "risuaidat" voivat näyttää aika hurjilta hieman monimutkaisemmassa "Regular Expression" (vakioitu ilmaus ?) tapauksissa.  

"grep": llä on aika mukava kokeilla erilaisia RE-lauseita kun antaa vivut "-oE" jolloin se päästää läpi vain "osuman saaneet".

 
  
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 02.04.12 - klo:07.42
Ehkä näin: Katso heksaeditorilla mitä siellä on tiedoston alussa kun kaikki toimii ok.  Jos uuden tiedoston alku ei sisällä tuota niin edetään sen mukaisesti.  

Minä en tykkää toimia tiedostojen kanssa vaan tekstijonojen. Tulipa vastaan tilanne jossa tekstijono ei toiminut niinkuin olisi "pitänyt" ja heksaeditorin käyttäminen tuntui mahdottomalta urakalta.

Tulostelin tekstijonoa ja tein taikoja monenlaisia. Sitten tuli mieleeni että voihan tekstijonossakin olla kontrollimerkkejä. Sittenpä ratkaisu olikin melko yksinkertainen, väliin vain käsky:  ValitunTeksti=$(echo $ValitunTeksti | tr -d [:cntrl:] )
- tämänjälkeen toimi taas.
- loppujenlopuksi syylliseksi paljastui ohjelma sed, joka asetti tiedostossa /tmp/delme tekstirivien eteen kovan lainausmerkin. Ja istutti tajuamattani kontrollimerkin. Lause oli ennen korjausta: sed -i "s/^/' /g" /tmp/delme  ja korjauksen jälkeen: sed -i "s/^/'/g" /tmp/delme

muuten tietoa tekstijonoistaan saa käskyillä:
  echo teksti:
  echo "$ValitunTeksti" | tr -dc '[:print:]' | od -c
  echo kontrollimerkit:
  echo "$ValitunTeksti" | tr -dc '[:cntrl:]' | od -c
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 02.04.12 - klo:13.31
Lainaus
Minä en tykkää toimia tiedostojen kanssa vaan tekstijonojen. Tulipa vastaan tilanne jossa tekstijono ei toiminut niinkuin olisi "pitänyt" ja heksaeditorin käyttäminen tuntui mahdottomalta urakalta.

echo $jokin_testi_teksti_tms | tehdään_jotain_1 | tehdään_jotain_2 | tehdään_jotain_3 > tulos.txt

Sen tekstijonon tarkastelu on helppoa kun sen putken lopputuloksen ohjaa tiedostoon ensin väkäsellä ja avaa sitten hexaeditorilla tuon tuotoksen, tuossa esimerkissä "tulos.txt".

Tyypilliset kiusat ovat välilyönnit edessä tai perässä kun ne eivät "näy" sekä ajoittain <cr>  (\r  ^M hex 0D )
mutta noihin on hyvät lääkkeet.

sed  's/^ *//g'                 poistaa välilyönnit rivin alusta
sed  's/^ *//g;s/ *$//g'     poistaa välilyönnit rivin alusta ja lopusta
tr -d '\r'                           poistaa <cr>  merkit

Esimerkki:

a="  kissa koira  "         # Pari tyhjää edessä ja perässä
echo ${#a}
15                     # 15 merkkiä pitkä
b=$(echo "$a" | sed 's/^ *//g;s/ *$//g')    # Poistetaan välilyönnit edestä ja perästä
echo $b
kissa koira
echo ${#b}
11                    # Nyt 11 merkkiä pitkä

Tuossa sed: ssä oli yhdistetty kaksi sed-komentoa puolipisteen avulla, lyhentää kirjoitustyötä. Jos putki menee pitkäksi niin voi jatkaa toiselle riville kun laittaa kenoviivan "\" rivin loppuun mutta tuonne ei saa kirjoittaa mitään kommentteja risuaidoilla, ei jättää tyhjää riviä ja rivin on loputtava tuohon kenoviivaan.  Oma pisin putkeni on seitsemän (7) riviä, rivitystä olen käyttänyt myös luettavuuden/tulkittavuuden takia.

Oman mielenkiintoisen ongelmakenttänsä muodostaa bash-muuttujien taulukkotyyppisyys, tuosta pitäisi kirjoittaa pidempi tarina.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.04.12 - klo:11.50
Oman mielenkiintoisen ongelmakenttänsä muodostaa bash-muuttujien taulukkotyyppisyys, tuosta pitäisi kirjoittaa pidempi tarina.

Kerkiäisitkö kirjoittamaan jotakin, vaikka vain jotain pientä? Kun semmoinen vika tässä koko skriptauksessa on, että useimmat muut neuvojen kirjoittajat tuntuvat liikkuvan toisilla planeetoilla eikä niiden neuvoista saa selvää. Esimerkiksi nuo kullanarvoiset man-sivut; pakko niitä on  käyttää muttei ne kovin selviä ole. Miksei ole enemmän esimerkkejä ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Asmo Koskinen - 03.04.12 - klo:13.11
Esimerkiksi nuo kullanarvoiset man-sivut; pakko niitä on  käyttää muttei ne kovin selviä ole. Miksei ole enemmän esimerkkejä ?

Onhan näitä?

http://www.ibm.com/developerworks/linux/library/l-bash/index.html
http://www.ibm.com/developerworks/linux/library/l-bash2/index.html
http://www.ibm.com/developerworks/linux/library/l-bash3/index.html

http://tldp.org/LDP/Bash-Beginners-Guide/html/
http://tldp.org/LDP/abs/html/

Ystävällisin terveisin Asmo Koskinen.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 05.04.12 - klo:14.27
Esimerkiksi seuraava lause:
a=1; a[1]=2; echo ${a[0]}; echo ${a[1]}
Kuinka a[0]:lle voidaan esittää arvo kun sille ei ole koskaan annettukaan ?

http://www.gnu.org/software/bash/manual/html_node/Arrays.html:

"Any variable may be used as an indexed array"
"Referencing an array variable without a subscript is equivalent to referencing with a subscript of 0. "
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 06.04.12 - klo:00.26
Osa 1

Johdanto

Bash-muuttujat voivat näyttää merkillisiltä jäykkään/kiinteään muuttujan tyypitykseen tottuneelle kuten minulle.  Käytännön esimerkit valaisevat kuitenkin ominaisuuksia ehkä paremmin kuin paksu nippu raskaslukuista dokumentaatiota --- ja on sitä paitsi hauskempaa.  Otetaan siis pääteikkuna tuohon viereen, kokeillaan hieman ja keksitään selityksiä.  Kokonaisluvut ovat aika tylsiä mutta merkkijonojen (stringit) kanssa löytyy mielenkiintoisia asioita.  Siispä liikkeelle:
Koodia: [Valitse]
a="kissa koira hevonen"       # loimme muuttujan "a" ja annoimme sisällön
echo $a                                   # tuttu tapa tulostaa eikä mitään yllättävää ($a tarkoittaa a: n sisältöä)
kissa koira hevonen

Muuttuja "a" on kuitenkin samalla taulukko mutta siinä on vain yksi elementti, "kaikki on laitettu samaan laatikkoon".  Voimme nähdä tämän kokeilemalla.  Kokeillaanpa hieman:

echo $a[0]
kissa koira hevonen[0]         # yritimme väärin tulostaa elementtiä nolla, echo kirjoitteli vain "$a" osan
                                               # --- ja loput merkit eli [0]  sellaisenaan perään

Tuo vaatii siis lisäsulut että tapahtuisi se mitä halusimme sanoa, tuohon käytetään aaltosulkuja:

echo ${a[0]}
kissa koira hevonen             # nyt bash ymmärsi oikein mitä halusimme sanoa eli tulostimme muuttujasta
                                              # "a"  ensimmäisen elementin eli numerointi alkaa nollasta !

Kokeillaanpa seuraavaa elementtiä:
echo ${a[1]}
                                              # ei löytynyt mitään  

Mikähän on tuon stringin pituus, kysytään sitä näin laittamalla yksi risuaita muutujan eteen:
echo ${#a[0]}
19                                          # tiedämme tästäkin että nuo ovat kaikki samassa elementissä ("laatikossa")

Katsotaanpa mikä on toisen elementin (eli indeksi yksi) sisältämän stringin pituus:
echo ${#a[1]}
0                                            # no, ei mitään odottamatonta


Nyt alkaakin mielenkiintoinen vaihe jos teemmekin näin:

a=(kissa koira hevonen)       # mitä kerroimme bash: lle ?  Että haluamme kaikki *omaan* laatikkoonsa

Katsotaanpa mitä nyt löytyy jos alamme käydä taulukon indeksejä läpi:

echo ${a[0]}
kissa

echo ${a[1]}
koira

echo ${a[2]}
hevonen                            # mainiota, kaikki omissa elementeissään, taulukossa nyt 3 elementtiä !

Mutta voimmeko kysyä nyt stringin pituutta laittamalla risuaidan muuttujan eteen ?

echo ${#a[0]}
5

echo ${#a[1]}
5

echo ${#a[2]}
7

Hyvin toimii.   Mutta mitä jos haluamme nyt saada echo: lla koko rytäkän eikä vain yhtä kenttää ?  
Tiedostoista monille on tuttua tähden "*"  käyttö --- toimiiko se täälläkin ? Kokeillaan indeksinä:

echo ${a[*]}
kissa koira hevonen        # no siinähän se !

Entäs koko rytäkän pituus ?  Ahaa, työnnämme siis risuaidan taas tuon muuttujan eteen ?

echo ${#a[*]}
3                                      #  Hups, saimmekin elementtien lukumäärän !

Joskus on tarpeellista tietää mitä indeksinumeroita on on olemassa, pyydämme luettelemaan ne:
echo ${!a[*]}
0 1 2                               # oikealta näyttää

Entä mitä tapahtuu jos emme kerro indeksiä ?
echo ${a}
kissa                        # Eli jos indeksi jätetään pois niin se tarkoittaa aina indeksiä nolla.

Yhteenveto


Jatkuu osassa 2...

Edit:  Piti laittaa "koodin" sisälle melkein kaikki kun foorumisofta tulkitsee tekstini eri tavalla kuin oli tarkoitus eli lukijan kannalta: hukkasi muuten merkkejä...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 06.04.12 - klo:12.58
Osa2

Muuttujan tyyppi ei ole bash: ssä ennaltamäärätty vaan tyyppi "määräytyy" oikeastaan sen mukaan miten muuttujaa käytetään.  Tuo "määräytyy" oli lainausmerkeissä koska pessimisti voisi sanoa että "mikään ei ole pysyvää" bash: in tapauksessa.  Onneksi tässä on kuitenkin takana omaksuttavissa oleva logiikka ja muuttujan tyyppi (tai rakenne) kyllä säilyy tiettyyn rajaan asti.  Tutkaillaan siis hieman lisää:

Sijoittaminen
Taulukkona käytetyn muuttujan arvon sijoittaminen toiseen muuttujaan aiheuttaa seuraamuksia riippuen siitä miten kerromme sen bash: lle.   Jatketaan samalla testidatalla kuin aiemmin:

Koodia: [Valitse]
a=(kissa koira hevonen)     # Annamme muuttujalle "a" arvon tehden 3 elementtisen taulukon
echo ${#a[*]}          # Tarkastamme elementtien määrän
3
echo ${!a[*]}           # Listaamme olemassaolevat indeksinumerot
0 1 2                         # Kaikki ok

Sijoitamme nyt muuttujan "a" sisällön muuttujiin "b" ja "c" mutta eri tavalla:

  b=$a
  c=${a[*]}

Mitähän muuttujat "b" ja "c" sisältävät tämän jälkeen ?  Yleisin vastaus on "saman".  Avataanpa laatikot "b" sekä "c" ja katsotaan mitä oikeastaan tapahtui:

echo $b
kissa              # No missäs se loppu on (arvaatko) ?  
echo ${b[*]}
kissa              # Sanoin aiemmin:  
                       # * Taulukosta otetaan vain kenttä nolla ellei indeksiä kerrota *
                       # Näin meille kävikin sijoituksessa.  

echo $c          
kissa koira hevonen   # Eikös tuon sitten olisi pitänyt tulostaa vain "kissa"... ?              
                                    # Se taulukon ensimmäinen ??  
                                    # Hyvä havainto --- odota hieman...

echo ${c[*]}
kissa koira hevonen

Varmistetaan vielä kenttien määrä, tutkitaan "risuaidalla":

echo ${#c[*]}
1                        # Hups, elementtien lukumäärä onkin yksi...  
                           # Tuli yksipaikkainen taulukko
                            # Siksi echo tulosti aivan oikein tuolla edellä eli
                            # sen ensimmäisen elementin
                         # joka sisältää nyt "kissa koira hevonen"

Miten saamme sijoituksessa tyypin säilymään ?   Tehdäänpä näin, kerrotaan erikseen sijoituksessa että sijoitamme nimenomaan taulukon uuteen muuttujaan:

d=(${a[*]})     # Muistatko kun alussa käytimme tavallisia sulkuja
                         # (kissa koira hevonen)  kertomaan taulukosta ?  
                         # Niin nytkin.
                         # Jotain hyvin mielenkiintoista tapahtui:

echo $d
kissa
echo ${d[*]}
kissa koira hevonen  # Data talleessa, entäs elementit:

echo ${#d[*]}
3                         # Taulukko säilyi samanlaisena, kolme elementtisenä.  
echo ${d[0]}
kissa
echo ${d[1]}
koira
echo ${d[2]}
hevonen



Yhteenveto


Jatkuu osassa 3...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 19.04.12 - klo:20.43
Lainaus
...ajetaankin skripti tulkin niin että se tarkistaa skriptin koodin
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 20.04.12 - klo:01.02
Bash-skriptin voi ajaa myös käskyllä: bash <skriptin nimi>. Käsky: bash -n  <skriptin nimi> ajaa skriptin tulkista läpi jolloin mitään ei tapahdu jos skripti toimii, mutta jos skriptissä on virheitä niin virheviestit tulostuvat.

Tarkoituksena on automaattinen toiminta: kun skripti laitetaan suoritukseen tulostetaan ensin sen virheet ja jos niitä on ei yritetäkään ajaa skriptiä vaan viedään se editoriin. Editorissa skriptiä korjataan ja korjaamisen jälkeen yritetään suorittaa skripti uudelleen. Jos tulee vieläkin virheitä niin mennään taas editoriin ... ja vasta kun  koodi on virheetön se lasketaan suoritukseen.

 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 20.04.12 - klo:10.26
Ok.  Tuolla on aika montakin "debuggaus" vipua:

Koodia: [Valitse]
bash -c 'help set'
Ei ole tullut tuota hyödynnettyä vaikka idea on hyvä karkeimpien virheiden seulontaan.  Kiusallisempia virhetilanteita jotka eivät jää tuossa kiinni ovat virheet toimintatavassa jossain erityisessä tilanteessa. Otetaan nyt esimerkkinä vaikka että työhakemisto välitetään vaikkapa muuttujassa "polku". Työhakemisto tyhjätään kun skripti tulee lopuksi ulos. Kuinka ollakaan, virhetilanne aiheuttaa sen että "$polku" onkin tyhjä ja "rm"  yrittääkin poistaa "/".    Huh, olipa hyvä etten ollut "root"... :)
Kirjoittelen nykyään hieman arvaillen "mitä voisi pahimmillaan tapahtua" ja laitan järkevyystarkastuksia  ja tarvittaessa virhetilanteen käsittelyfunktion joihinkin kohtiin. 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 01.05.12 - klo:04.52
BASH:issa jokainen homma voidaan tehdä lukemattomilla tavoilla. Jokainen tapa soveltuu juuri tiettyyn tilanteeseen ja väärän tavan käyttäminen on suuri virhe. Tietoa tästä on hävinnyt niinkuin sitä ei koskaan olisi ollut.

Koodia: [Valitse]

Kursorinkäsittelyä ESC-koodeilla:
ESC[#;#H tai ESC[#;#f         Kursori riville # #
ESC[#A                        Liikuta kursoria ylöspäin # riviä
ESC[#B                        Liikuta kursoria alaspäin # riviä
ESC[#C                        Liikuta kursoria eteenpäin # saraketta
ESC[#D                        Liikuta kursoria taaksepäin # saraketta
ESC[#;#R                     Kursorin paikan lukeminen
ESC[s                           Kursoripaikka talteen
ESC[u                           Kursoripaikka tallesta
ESC[2J                          Tyhjennä ruutu, siirrä kursori ylävasepaan
ESC[K                           Tyhjennä rivi kursoripaikasta rivin loppuun

esimerkiksi:
echo -ne "\\e[2J"    # tyhjennä näyttö ja vie kursori paikalle 0 0 (=näytön ylävasen)
echo -ne "\\e[10;5H" # siirrä kursori riville 10 sarakkeeseen 5
echo -ne "MoikonKoikon"

Kursorin käsittelyä ANSI:n mukaan:
Laita kursori riville L ja sarakkeeseen C: \033[<L>;<C>H tai: \033[<L>;<C>f
Liikuta kursoria ylöspäin N riviä:         \033[<N>A
Liikuta kursoria alaspäin N riviä:         \033[<N>B
Liikuta kursoria eteenpäin N saraketta:    \033[<N>C
Liikuta kursoria taaksepäin N saraketta:   \033[<N>D
Tyhjennä ruutu, siirrä kursori ylävasepaan:\033[2J
Tyhjennä rivi kursorista rivin loppuun:    \033[K
Talleta kursorin paikka:                   \033[s
Palauta kursorin paikka:                   \033[u
Palauta koko näyttö tallesta:              \033[0m    
- nämäkin tulostetaan: echo -ne koodi


- tput sgr0       # Palauta päätteen asetukset normaaleiksi.   echo -ne \E[0m taitaa olla parempi
- tput smcup      # talleta näyttö. Tämä käsky muuten tuhoaa näytön joten täytyy harkita missä ja milloi sitä käyttää
- tput rmcup      # palauta näyttö
- tput-hommina on vaikka mitä, katso: man terminfo

esimerkiksi:
echo 'joopasten'
read -p 'paina enter niin tyhjennän näytön'
tput smcup      # talleta näyttö
clear
read -p 'paina enter niin palautan näytön'
tput rmcup      # palauta näyttö
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Tomin - 22.02.13 - klo:23.42
Siirsin skriptiajuriviestejä omaan ketjuunsa. Toivottavasti tästä ketjusta ei tullut vaikeaselkoista sen takia. Yritin valikoida oikeat viestit, mutta foorumisoftan rajoituksista johtuen en saanut kaikkia viestejä (ainakaan vielä) siirrettyä.

Uusi aihe tuolla: http://forum.ubuntu-fi.org/index.php?topic=44605.0
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 30.03.13 - klo:07.39
BASH-skripteissä voi olla tulosteissa ja editoreissa näkymättömiä kontrollimerkkejä jotka saattavat tehdä skriptistä toimimattoman. Joskus myös tekstijono voi kiukutella.

varsinaisten kontrollimerkit ovat ascii-koodeiltaan välillä 00-1f hexadesimaalisina ja niiden "BASH-merkintä" on välillä \x00-\x1f.
osan kontrollimerkeistä voi poistaa tiedostosta käskyllä: for i in {01..19} 0b 0c 0d 0e 0f 1a 1b 1c 1d 1f; do apu='\x'$i; sed -i "s/${apu}//g" tiedostonnimi; done
kokeile: echo -e '#\x1d!/bin/bash\x01 koe\x0atoinen\x0d rivi' > delme; cat -v delme;  for i in {01..19} 0b 0c 0d 0e 0f 1a 1b 1c 1d 1e 1f; do apu='\x'$i; sed -i "s/${apu}//g" delme; done; cat -v delme

- kontrollimerkki \x00 on kielletty, se on kuulemma sed-sääntöjen vastainen
- ääkköset muuten säilyy vaikka tuloksen perusteella voisikin nopeasti päätellä että hassusti käy.
- käsky saattaa poistaa jotain tärkeääkin, joten suhtaudu varoen.

Mutta mitä enemmän tuota kontrollimerkki-ongelmaa tutkin niin sitä enemmän alkaa tuntua siltä, että vian nimi on hiukan väärin sillä eivät ne invalidisoivat merkit ole varsinaisesti kontrollimerkkejä vaan editorien lisäämiä "kontrolli-merkkejä" jotka yleensä alkavat: M- tai: C- . Myöskin ääkköset tuovat niitä mukanaan, ja taitaa olla niin että joskus käy ääkkösten poistamisessa niin että ääkkönen lähtee ja "kontrollimerkit" jäävät.

Näiden kontrolli-merkkien metsästämiseen sopii parhaiten käsky: cat -v skriptitiedostonNimi
- tai jos etsii kontrollimerkkejä tekstijonosta löytää ne käskyllä: echo "tekstijono" | cat -v
- jos skriptin listaus olisi muuten kovin pitkä voi sitä rajoittaa: cat -v skriptitiedostonNimi | grep M-   . Se selventää muutenkin.

Nuo kontrollimerkit saa poistettua vaikka gedit:illä ja kai muillakin editoreilla:
1. katso missä ne kontrollimerkit ovat käskyllä: cat -v skriptitiedostonNimi | grep -e M- -e C-
2. lue tiedosto editoriin, poista kontrollimerkkien jälkeinen ja edellinen merkki - sitten kirjoita ne heti takaisin ja talleta tiedosto. Kontrollimerkit hävisivät.

Ääkköset sotkevat hommaa: niiden merkkinä on "kontrollimerkkejä".

Kontrollimerkit sha-bangissa ( #!/bin/bash on yksi sha-bang, mutta niitä on monta samantapaista):
Jos sha-bang puuttuu tai on vahingittunut niin käytetään oletusta, esimerkiksi #!/bin/sh
Yksi sha-bang:in vahingoittumismuoto on se, että merkkien #! välissä on jotakin.
Sha-bangin tulee myös alkaa rivin alusta.
**
Löysinpä netistä yhdenrivin skriptin, joka etsii osoitettavasta kansiosta ja sen alikansioista tiedostoja jotka ovat täysin samoja lukuunottamatta nimeä. Tiedostot voivat myöskin olla eri kansioissa.
- samuus lasketaan md5sum:masta. Jos md5sum:maan on tyytymätön niin voi käyttää mitähyvänsä muuta hyväksikatsomaansa funktiota tai vaikkapa tehdä oman . Mutta nyt skripti on:
find polku_mistä_kansiosta_lähtien_eroja_etsitään -type f -exec md5sum '{}' ';' | grep -v mistä_kansiosta_ei_tuloksia_näytetä | sort | uniq --all-repeated=separate -w 33 | cut -c 35-
- skriptin oikeellisuus on tarkistettu toistaiseksi pintapuolisesti enkä ihmettelisi jos siinä ilmenisi myöhemmin kummallisia piirteitä. Mutta eihän kyse olekaan siitä toimiiko skripti oikein sillä siitä voi olla varma että korjaus löytyy.
- täytyihän sitä vähän parsia.

Eihän tuo skripti huippunopea ole: 15.000 tiedoston (koko kaikkiaan 100M) kanssa se kesti 32 sekuntia.
Kun tarvitaan laskuria käytetään BASH:issa yleensä BC:tä eikä vanhempaa vaikeakäyttöistä DC:tä. DC on kuitenkin paljon parempi näytös-luonteisissa tehtävissä joita nykyään vaaditaan - esimerkiksi piin arvon laskemisessa 100 000 numeron tarkkuudella tai laskettaessa mikä on 2000 kertoman arvo.
**
Skriptaamisen taito on nopeasti häviämässä. Tämä johtaa väkisin tehtyihin huonoihin skripteihin. Esimerkiksi yksi awk-skripti: kaveri pyysi apua foorumilla ja saikin paljon vastauksia. Jo ensimmäinen vastaus esitti toimivan skriptin, mutta myöhemmissä vastauksissa oli aina jotakin uutta jättäen pois jonkun hölmöyden. Kunnes lopulta eräs foorumilainen esitti todella sujuvan skriptin sanoen: miksi et tekisi kaikkea awk:issa ? On turhaa poiketa vähänväliä BASH_issa, putkittaa, käyttää apu-ohjelmia ym. kun kaiken voi tehdä awk:issa.

Onhan "väkisin tehty" skripti paljon parempi kuin se ettei skriptiä ole ollenkaan. Teen niitä väkisintehtyjä itsekin kokoajan sillä kun taito ei riitä niin taito ei riitä eikä sille nopeasti mitän voi. Mutta jotenkin vääristynyt nykytilanne on.
**
Pääsin awk:in tutkimisessa vaiheeseen matriisioperaatiot. Awk-skriptit "juoksevat rinkuloita matikka-ohjelmien ympärillä" ja teoriassa niiden tekeminen on helppoa, teoriassa onnistuisi minultakin. Mutta käytännössä niiden tekeminen ei minulta onnistu, sillä en osaa edes nimityksiä vaan joudun etsimään määritelmiä kaikkeen eikä ole hajuakaan mitä mistäkin pitäisi tulokseksi tulla. 

Mutta ne vähäiset esimerkit joita löysin osoittavat että niillä tekee salaman-nopeasti mitävaan - niinkuin matriisi-operaatiolla käsinkin, mutta sitä puuduttavaa a3b7c8 esitystä ei joudu tekemään. Mikä työkalu awk skriptaavalle matemaatikolle onkaan !
**
Päätteessä voi muutamia näppäimiä (kaiketi f5-f9 mutta riippuu distrosta) muokata:
- avaa pääte ja paina nappulaa jonka painamisen haluat tulostavan jonkun pitkän litanian tekstiä tai käskyn. Katso mitkä merkit tulee näytölle.
- anna käsky:   bind '"<merkit>":"teksti"'   
- jos teksti on käsky niin tekstin perään kirjoitetaan \n   
- Siis esimerkiksi:  bind '"7~":"/home/petteri/OMATSKRIPTIT/Etsi_ohjeista\n"'   huomautus: 7~ tuli minulla kun painoi f6
- ilmeisesti näppäimen ohjelmointi ei toimi mikäli näppäin on käyttöjärjestelmässä määritelty tekemään jotakin.
- käskyn voi laittaa .bashrc tiedostoon. Silloin voi kuulemma jättää bind:in pois.
**
eihän ne man-sivut enää ole ihan kunnossa ja kannattaakin hakea man-sivut netistä jos kyseessä ei ole ihan pikkujuttu.
Mutta pikkuhommissa tulee käytettyä koneen omaa man-käskyä ja sen toiminta on ihottava. Kypsyin lopulta koko man-käskyyn ja tein mann-käskyn joka hakee man-sivun editoriin, jossa sitä voi selata ja ctrl-f toimii myös. Käskyn saa kun .bashrc-tiedostoon kirjoittaa: function mann { man $1 > delme; gedit delme;wait;rm delme ; }
- muutoksen saa heti toimimaan kun käskee: . ~/.bashrc
- info-sivut ovat hieman paremmat kuin man-sivut. Info-sivujen lukemiseen tarkoitettu sovellus nimeltään pinfo on tutustumisen arvoinen.
**
AWK:in matemaattiset kyvyt ovat mielettömän hyvät. Kaikien mielestä hyvän esimerkin etsiminen on kylläkin niin vaikeaa niin että esitetään "koulu-esimerkki Fibonacci-lukusarjan laskemisesta":  seq 50 | awk 'BEGIN {a=1; b=1} {print a; c=a+b; a=b; b=c}'
- lasku kestää 6 ms - ei ole mitään järkeä parantaa C:llä.

Tai lasketaanpa taulukon sarakkeen normaalipoikkeama:
awk '{sum+=$1; sumsq+=$1*$1} END {print sqrt(sumsq/NR - (sum/NR)^2)}' tiedosto
- kyllähän siinäkin saattaa kulua ihan monta millisekuntia

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.05.13 - klo:15.43
Niinkuin BASH:issa kaikki muukin niin seuraavat matriisi-toiminnot voidaan määritellä monilla tavoilla.

Koodia: [Valitse]
#!/bin/bash
function tulosta_matriisi () {
[[ ! $(eval echo \${$1[*]}) ]] && echo matriisi $1 on määrittelemätön && echo && return # esimerkiksi kirjoitusvirhe nimessä aiheuttaa tämän
(echo -n "Matriisin $1 jäsenten arvot    : "; eval echo -e \${$1[@]^^}          # ^^ muuttaa arvot suurille kirjaimille. Muuttaminen pienille kirjaimille olisi ,,
echo -n "Matriisin $1 jäsenten osoitteet : "; eval echo \${!$1[*]}) | column -t # voi ne poiskin jättää
echo -n "Matriisin $1 jäsenienluku           :  "; eval echo  \${#$1[@]}
echo
}

declare -A matriisi1 # Assosiatiivinen matriisi tarvitsee ilmoittaa, mutta tavallista matriisia ei tarvitse ilmoittaa, mutta jos sen haluaa ilmoittaa niin tuo parameri on -a
matriisi1[yksi]=ensimmäinen
matriisi1[kaksi]=toinen
matriisi1[kolme]=kolmas

matriisi2[7]=2  # matriisin jäsenien osoitteiden ei tarvitse alkaa nollasta ja osoitteet voivat pomppiakin eikä muistia kulu väliin jääneisiin osoitteisiin. Myöskään
matriisi2[4]=1  # matriisin jäseniä ei tarvitse antaa järjestyksessä ja silti ne tulevat tulosteeseen järjestyksessä.
matriisi2[123]=3
matriisi2[334455]=4

matriisi3=(minä, sinä, '"Hentun Liisa"', '"Puntun Paavo"', '"Kapakka Lassi"' ja '"Mylläri Matti"' montako meittiä oli?)
# mikäli matriisin jäsenessä on välilyöntejä on jäsenen molemmin ouolin laitettava lainausmerkki. Se kyllä hieman sekoittaa tulostetta.
# mikäli vielä halutaan ne tulosteeseen selvästi erotettuina täytyy käyttää kovan- ja pehmeän lainausmerkin yhdistelmää

matriisi4=({a..z})

tulosta_matriisi matriisi1
tulosta_matriisi matriisi2
tulosta_matriisi matriisi3
tulosta_matriisi matriisi4
tulosta_matriisi matriisi5

# tässä on kaksi tehtävää ja voit tarkastella niiden suorittamista poistamalla kommentit neljästä tämän lohkon viimeisestä lauseesta ja kommentoimalla viimeisen lohkon
# 1.matriisin tiettyjen jäsenien tekeminen määrittelemättömiksi ja tulostaminen - kiinnitä huomiosi osoitteisiin
# 2.määrittelemien jäsenien poistaminen ja tulostaminen taas - kiinnitä taas huomiosi taas osoitteisiin
#for ((i=0; i<=9; i++)); do unset "matriisi4[$i]"; done
#tulosta_matriisi matriisi4
#matriisi4=( "${matriisi4[@]}" ) # matriisin jäsenten uudelleen numerointi siten, että ensimmäisen jäsenen osoite on 0 ja seuraavien jäsenten osoitteet aina 1 suurempi
#tulosta_matriisi matriisi4

# tämä vastaa noita neljää edellistä kommentoitua lausetta; työ on pakko suorittaa näin muissa kielissä sillä sparse tyyppistä matriisia ei niissä ole. 
# BASH:_issa sensijaan voi tehdä kummin vain.
matriisi4=("${matriisi4[@]:9:25}") # siis vain arvot 9-25 otetaan ja osoitteet uudelleen-numeroidaan samalla
tulosta_matriisi matriisi4
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.05.13 - klo:14.38
Mitä useilla man sivuilla oleva "C-kieli" on, mikä käyttötarkoitus niillä on ? Onko kyse vain pilalle-dokumentoinnista ?

Esimerkiksi näppäile: man wait ja vertaa tulosta siihen minkä saat esimerkiksi nettisivulta: http://ss64.com/bash/wait.html

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 03.06.13 - klo:20.57
Mitä useilla man sivuilla oleva "C-kieli" on, mikä käyttötarkoitus niillä on ? Onko kyse vain pilalle-dokumentoinnista ?

Esimerkiksi näppäile: man wait ja vertaa tulosta siihen minkä saat esimerkiksi nettisivulta: http://ss64.com/bash/wait.html

Jos kirjoitat:
Koodia: [Valitse]
$ man man
huomaat, että man-käsky hakee dokumentteja useista eri osioista. Koska wait ei ole erillinen komento, vaan bashin sisäänrakennettu käsky, ei waitia löydy osiosta 1. Samalla nimellä löytyy kuitenkin järjestelmäkutsu, osiosta 2, jolloin man näyttää ohjeistuksen sen käyttöön.

Kyse on siis siitä, että man näyttää ensimmäisen relevantin osuman, riippumatta siitä mitä komentotulkkia sattuu käyttämään.

Otetaan vielä esimerkki. Järjestelmästä löytyy hyvin suurella todennäköisyydellä komento stat. Sattumoisin samalla nimellä löytyy myös systeemikutsu. Jos siis kirjoitetaan:

Koodia: [Valitse]
$ man stat
näyttää man ohjeistuksen ensimmäisestä osiosta löytyvälle suoritettavalle komennolle stat, kun taas järjestelmäkutua kaipaava joutuu ohjeistustaan etsiessään kirjoittamaan:

Koodia: [Valitse]
$ man 2 stat
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 04.06.13 - klo:06.29
Selväksi tuli, tattista selityksestä. "Onnen lähteistä kumpuaa kitkeryys" vai mitenkä se oli. Kyllä tämä torpedo yhden BASHin upottaa.  

Sivulla www.manpagez.com tämä on kivasti esitetty, sillä siellä kerrotaan minkätyyppinen man-sivu tullaan esittämään, minkätyyppinen löytyy.
**
Man sivuilta voi muuten etsiä: esimerkiksi kun haluat tietoa readline-käskystä voit etsiä uusia näkökulmia etsimällä sivulta: man bash
- etsiminen: kirjoita kauttaviiva ja perään readline. kun painat enter esiin tulee ensimmäinen viittaus. Viittaukset eteenpäin: paina n  ;ja viittaukset taaksepäin: paina N
**
Koodia: [Valitse]
XOR on BASH:in perusfunktio. Se on niin tärkeä että merkki ^ määrättiin merkitsemään XOR:ia ja potenssiin korotus joutui tyytymään merkiin:
** . Kuvaus XOR:in toiminnasta:

Otetaanpa esimerkiksi: 1 XOR 4 . "BASH-kielisenä" se esitetään: echo $((1^4))
ja se antaa tulokseksi 5. Selvitys: BASH: matematiikka toimii binääriluvuilla. BASH olettaa, että mitkä numerot syötätkin niin ne on esitetty
desimaalisina joten se muuttaa ne binäärimuotoon. Siis:         1=            001
                                                                4=            100
                                                                -----------------
                                                                XOR=          101  

- siis XOR suoritetaan bitti kerrallaan lukujen binääriesityksen vastinbiteille.
- lukuja voi olla useampiakin kuin kaksi.
- BASH olettaa että haluat tulosteidenkin olevan desimaaliarvoja. Ja binääriluku 101 on desimaalisena 5.
- muuten BASH ei edes osaa esittää tulosta binäärisenä vaan sitävarten täytyy tehdä 12-rivinen skripti tai käyttää käskyryhmää:
  echo "obase=2; $((1^4))" | bc
- saman logiikan mukaan toimivat myös & ja: |  (=and ja or)
- mutta tässä esitetty XOR on bitwise-XOR eikä 'tavallista' XOR:ia ole vaivauduttu tekemään vaan se täytyy koodata itse - koodaustapa on esitetty lauseessa
jossa etsitään sataa pienemmät luvut jotka ovat jaollisia kuudella tai seitsemällä mutta eivät molemmilla:
for n in {1..100}; do  [[ $(($n%6)) = 0 || $(($n%7)) = 0 ]] && ! [[ $(($n%6)) = 0 && $(($n%7)) = 0 ]] && echo $n kelpaa ; done
**
Grep on kymmeniä kertoja hitaampi utf-8:lla kuin localella C. Enpä ole vielä kokeillut kun utf-8:lla nopeus on ollut toistaiseksi riittävä. Asiasta kerrotaan sivulla: http://tdas.wordpress.com/2008/02/03/speed-up-grep/  . Saapa nähdä nopeutuuko.

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: janne - 11.06.13 - klo:22.50
Grep on kymmeniä kertoja hitaampi utf-8:lla kuin localella C. Enpä ole vielä kokeillut kun utf-8:lla nopeus on ollut toistaiseksi riittävä. Asiasta kerrotaan sivulla: http://tdas.wordpress.com/2008/02/03/speed-up-grep/  . Saapa nähdä nopeutuuko.

Asiasta kertova juttu on yli 5 vuotta vanha ja sillä sanotaan että "Future version of grep are planned to address this issue.". Grep on myös päivittynyt tuona viiden vuoden aikana useamman kerran, silloisesta versiosta 2.5.3, nykyisen Ubuntun mukana tulevaan versioon 2.14. Nopealla googletuksella selviää, että ongelma korjattiin versiossa 2.7.

Ei tuo tietenkään tarkoita, etteikö asiaa saisi huvikseen testailla.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.06.13 - klo:12.10
Tattis vastauksesta, tuskinpa tuota kerkiää testaamaan ja kunnossahan se varmaan jo onkin; käytän grepiä aika paljon ja olen aina ihmetellyt kuinka niin nopeasti voi etsiä.
**
Harmittaa kuinka  arvostetuillakin nettisivuilla morkataan BASH:ia. Tekeytyvät BASH:ille "ystävällisiksi" mutta käyttävät vanhuuttaan kelvottomia toteutuksia ja yleistävät tulokset koskemaan koko BASHia. Menettäneet kehittymiskykynsä ja toimivat joo-joo-miehinä.   

Esimerkiksi BASH:in matemaattiset ominaisuudet - ei niitä oikeastaan ole joten kuinka niitä voi morkata? Morkkaajat eivät kykene edes bc:stä tekemään kunnollista. Kyvyttömät, eivät ne osaa tehdä muuta kuin repiä.

Matematiikkaa komentorivillä:
----------------------------------------
Jos kopioit kaikki funktiot foorumilta kirjoituksesta: http://forum.ubuntu-fi.org/index.php?topic=303.msg306401#msg306401             johonkin tiedostoon koneessasi ja annat käskyn:
gedit ~/.bashrc ja kirjoitat avautuvan tiedoston viimeiseksi riviksi: . polku_lataamaasi_tiedostoon    niin saat talletettuasi tiedoston ja bootattuasi päätteessä käyttöösi seuraavaa:

Voit kirjoittaa komentoriville minkätahansa laskun esimerkiksi: 2.5+2.5      tai: round[log[.11e2-2*sin+30]+ln[e^[round[16*[sin30*cos60]]]]]  ja enterin painamisen jälkeen kone kertoo oikean tuloksen; tässätapauksessa 5 eikä turhia perä-nollia esitetä vaikka onkin laskettu 60 desimaalia.
- komentorivin muu toiminta ei muutu ja käskyjä voi käyttää skripteissäkin.
- sallitut operaattorit: +, -, *, /, ^, %. Mikäli laskussa on operaattori / täytyy laskun eteen kirjoittaa sana: laske tai BASH tulkitsee laskun kansioksi. 
- parametrittömät funktiot : pi, e, #, asteet, radiaanit (lyhenne rad), round
- yksiparametriset funktiot: sin, cos, tan, atn, sqrt, log, ln, exp, abs, int, kertoma, kymppiin, kahteen (kantalukumuunnokset), desimaalit (lyhenne des)
- kaksiparametriset funktiot: power
- uusia funktioita voi tehdä; myös moni-parametrisia voi tehdä mutta mallia ei ole joten mukavaa pähkäilyä.
- kun laskuja suoritututtaa skripteistä käsin saa laskun tuloksen muuttujasta $(tulo); esimerkiksi: 2*2; sqrt$(tulo) 
- myös summaus-vähennystapa: +arvo tunnetaan. Tulkinta: lisää/vähennä siitä mikä edellisen laskun tulos oli.
- suluilla on niiden normaali merkitys. Suluiksi kelpaavat kuitenkin vain hakasulut.
- # on kommenttimerkki. Esimerkiksi ohjelmalle voidaan antaa laskettavaksi tiedoston. Tiedoston jokaisen rivin perään voi kirjoittaa kommentin. Kommentti alkaa merkillä #.
- power on desimaalisten eksponenttien laskemiseksi, esimerkiksi: power3^0.5

- desimaaleja on 60 ja trigonometria suoritetaan asteissa ellei toisin määrätä, esimerkikssi: laske des256radsin.5235988 . Määrättävä desimaalimäärä kuitenkin ilmoittaa bc:lle vain millätarkkuudella laskenta tulee vähitään suorittaa: jos jossakussa laskuun menevässä on enemmän desimaaleja niin desimaaliluku otetaan siitä. Asetukset säilyvät kunnes ne muutetaan noilla käskyillä tai bootataan.

- lukujen kokonaisosa huomioidaan kokoajan kokonaan onpa siinä kuinka monta numeroa hyvänsä. Ja mikäli ei erikseen määrätä niin ohjelman laskenta suoritetaan 66 desimaalin tarkkuudella ja tulostus tapahtuu toistaiseksi samalla tarkkuudella. Desimaalien lukumäärällä ei ole rajaa.

- tämä ohjelma käyttää laskuissaan matematiikka-ohjelmaa nimeltään bc. Matriisilaskuihin soveltuu paremmin awk joka osaa tekstinkäsittelyäkin.

- laskutusta voi ylläpitää tekstinkäsittely-järjestelmässä ja tulostaa laskutus-tiedostot laskuina. Vaikka tämä toimikin kokeissa niin kannattaa alkuun suhtautua erittäin varoen.

- siirrettäessä skripti toiseen koneeseen tai uuteen Linuxiin kannattaa skriptin toimivuus testata päässä-laskettavalx=2; laske $(echo [e^x-e^-x]/2 | sed "s/x/$x/g" | awk '{print $1}' )la mutta koneelle vaikeasti laskettavalla laskulla, esimerkiksi: round[log[.11e2-2*sin+30]+ln[e^[round[16*[sin30*cos60]]]]]  - tuloksen pitää olla 5 eikä siinä ei saa olla desimaaleja eikä laskuista saa tulla huomautuksia (warning).

- sevitys round:in toimintaan: laskettaessa jotakin jonka arvo on päättymätön numerosarja sattaa tuloksen viimeinen numero olla yksinkertaisissakin tapauksissa virheellinen yhdellä. Kun useammat pikkuvirheet kertautuvat saattaakin lopputuloksen useampikin viimeinen numero olla väärin. Käytettäessä laskemiseen bc:tä lasketaankin esimerkiksi kymmenen desimaalia enemmän kuin tarpeen olisi, hylätään yhdeksän viimeistä ja pyöristetään lopputulos kymmenennellä - toiminta on vähän hitaampaa mutta tulos varmasti oikea ja näyttävä - mitä siitä että se on matemaattisessa mielessä hölmöä.

- mikäli matematiikka suoritetaan eksponenttiluvuilla ei matematiikka voi olla tarkkaa. Tämä ei tarkoita ettei ohjelma saa hyväksyä eksponentti-muodossa olevia eikä sitäkään että ohjelma ei saisi esittää tuloksia eksponenttimuodossa. Tuntuu muuten siltä, että exel käyttää bc:tä - ja koska hyökkäys on paras puolustus niin Microsoft syyttää Linuxia.

- toiminta on hidasta niinkuin exel-tyyppisissä yleensäkin. Mutta tulokset ovat oikeita silloinkin kun muuten saa pelkkää virhettä. 

- lähes kaikilla funktioilla on monia mahdollisia laskutapoja elikä korvaus-funktioita: esimerkiksi sinhx voidaan laskea kaavasta: [e^x-e^-x]/2 . Tässä skriptissä merkintätapa on:
  x=2; laske $(echo [e^x-e^-x]/2 | sed "s/x/$x/g" | awk '{print $1}' )   ; kaikki muu on siis vakiota paitsi x=???.
  Sinh:in merkityksestä: hämähäkki kutoo verkkonsa aivoihinsa koodattujen sinh-taulukoiden mukaan, rakennuksien kestävyyslaskelmat tarvitsee sinh-funktioita - vaikka mitkä laskelmat tarvitsee myös sinh:iä.

- bc ei tunne desimaalisia eksponentteja. Niille ei taidakaan olla laskukaavaa, vaan ne täytyy laskea iteroimalla. Laskusta tuleekin vielä hitaampi kuin bc:llä normaalisti. Mutta lohdutusta saa siitä että rajoittamaton tarkkuuskin on tärkeämpää kuin normaalisti: tarkkoja desimaali-exponentteja on vaikea kehittää: exelistähänSuomalaiset sivut loistivat: ohjeet vain hieman epämääräiset, mutta esimerkiksi yksi arvovaltainen kansainvälinen sivu neuvoi tyyliin: miksi tehdä helposti kun voi tehdä vaikeastikin. niitä saa niinkuin taskukalkulaattoristakin, mutta tarkkuus on niillä huono.

Itseasiassa Linuxin työkalut myös matemaattisiin tehtäviin ovat erinomaiset. Mutta me kaikki olemme Wintoosan pilaamia ja yritykset toimia sillä tavalla kuin Wintoosa opettaa ovat epäonnistumaan tuomittuja. Lisäksi Linuxin merkintätavat ovat pitkiä ja ennenkaikkea täysin mahdottomia muistaa.
- esimerkiksi missä muussa kielessä voi tehdä matemaattisia vertailuja rajoittamattoman suurille, pienille ja pitkille numeroille? Vertailu täytyy ehdottomasti tehdä bc:n sisällä.
- bc: tä käytetäänkin lähes kaikkiin matemaattisiin ongelmiin; Linuxin omat matematiikka-käskyt opetellaan vain jotta saataisiin kielestä mielikuva.

- bc laskee ihan kiltisti myös negatiiviset desimaali-potenssit ilman kikkailua. Ongelmaksi kylläkin muodostui se ettei yksinkertaisillekaan laskuille riittänyt bc:n oletus-tarkkuus: 66-desimaalin tarkkuus. 

Tämä alkoi käydä mielenkiintoiseksi, sillä seuraava aste on enää toteutusta vaille ja sillä onnistuisi jo vähän edistyneempikin matikka - jaa, matriisi-matikkaa on jossakin. Mutta tarkoitus olisi siirtyä toteuttamaan kunnollisia kovalevy-funktioita: toimimattoman käyttöjärjestelmän päivitys ja muuta semmoista.

Kovalevy_funktiot onkin ensialkuun toteutettu itsenäisenä skriptinä jossa on myös versio hiiri_funktioista. Noviiseille tarkoitettu kovalevyn automaattinen korjaus ja boottauskykyiseksi tekeminen on jo tehty ja parhaillaan pähkäilen ammattikäyttöön tarkoitetun version kanssa.

- matematiikka toimii Trusty Tahrissa

btrfs toi taas kerran esiin tarpeen grubin editoimiseen:

Grubin "editointi" grub.cfg:ssä ehdotetulla tavalla
-----------------------------------------------------------------

Ubuntu 13.10:n grub on  kelvoton sillä varsinkin käytettäessä kokeellisia menetelmiä grubista pitää ehdottomasti näkyä mikä Ubuntun versio ja millä varusteilla on käytössä.
Samoin pankkiin ei pitäisi mennä saastuneella koneella, ja sen varmistamiseksi pankki-Ubuntun on syytä olla USB-tikulla joka on koneessa kiinni vain käytettäessä pankki-yhteyttä. (mikäli käyt internetissä niin koneesi voi olla saastunut. Pankki-sivuilla käynti on varsin turvallista.)
Myöskin käsiteltäessä sellaista jonka sekoittaminen mukuloiden toimesta on syytä estää täytyy tehdä sille tehtävälle oma ubuntu ja suojata grubin se kohta omalla salasanallaan jotta sitä ei voisi muuttaa.
 
- painotan siis: tämä on kehittäjien suosittelema keino, eikä tässä editoida grub.cfg:tä vaan sen aputiedostoa.
- Ubuntun kehittäjät tarjoavat tällaisen "staattisen valikon" tekemiseksi monia helpotuksia - esimerkiksi kuinka staattinen valikko saadaan ottamaan huomioon kerneleiden muuttumiseen päivitettäessä..
- tämä editoitu grub säilyy useimmiten myös asennettaessa uusi käyttöjärjestelmä. Jos asennetaan Windows niin alkulataaja täytyy omia takaisin. 
- koodi on sivulla: http://forum.ubuntu-fi.org/index.php?topic=30413.0
**
Jonkunsortin käsky kerrallaan askellus BASH-skripteille:
Tarkistettavan kohdan eteen kirjoitetaan koodiin rivi : set -x; trap "echo paina return;read x" DEBUG
Tarkistettanan kohdan perään kirjoitetaan koodiin rivi: set +x; trap "" DEBUG
- voihan siihen lisätä sillähetkellä tärkeiden muuttujien tulostusta ja sensemmoista.
- tämä trace-hommahan on parempi kuin muilla kielillä. Näennäisesti se on susi sillä kukaan ei ole vaivautunut selvittämään sitä vaan tiedot löytyvät vain man-sivujen sokkeloista. Ja tällähetkellä minä en jaksa hommaa selvittää.

 

 


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 23.01.14 - klo:23.12
bash muuttujat ja taulukkotyyppisyys... kuiva aihe ?


Johdanto

Ehkä pieni kertaus ensin.  Ehdotan että avaat päätteen ja kokeilet siinä --- vaikka uteliaisuudesta.

Kaikki bash muuttujat ovat pohjimmiltaan taulukoita olkoonkin että sitä ei usein huomaa.  Muuttujaa ei määritellä erikseen mitenkään vaan sitä vain aletaan käyttää antamalla sille arvo jollakin tavalla jolloin muuttujan merkitys "kiinnittyy" samalla.  Riippuen miten annat arvon muuttujalle on tulos ja merkitys aivan erilainen.  Annetaan kokeeksi arvoja kahdella eri tavalla:

Koodia: [Valitse]
ekalista="kissa koira papukaija"
Koodia: [Valitse]
tokalista=(kissa koira papukaija)
Kuten tuttua "echo" komennolla voi näyttää muuttujan sisällön mutta silloin kun muuttujan sisältöä udellaan siihen laitetaan eteen dollarimerkki.  Katsotaanpa:

Koodia: [Valitse]
echo $ekalista
kissa koira papukaija

Koodia: [Valitse]
echo $tokalista
kissa
No minnekäs ne loput hävisivät tokalista: sta ?

Ei minnekään.  Ne menivät vain eri "laatikoihin" eli hienommin sanottuna kenttiin tuossa muuttujassa.  Voit kuvitella sekä muuttujaa "ekalista" että "tokalista"  lokerikkona jossa on vasemmalta oikealle numeroituja paikkoja.  Itse aloittaisit ehkä numeroinnin ykkösestä mutta näissä numerointi (hienommin sanottuna indeksi) alkaa nollasta.  Paikkanumerot juoksevat siis 0, 1, 2, 3,...    Yksittäisen paikan sisältöä voit kysellä antamalla sen numeron, katsotaanpa ensimmäisen paikan sisältö (jonka indeksi siis on nolla):

Koodia: [Valitse]
echo ${ekalista[0]}  
kissa koira papukaija
 
Koodia: [Valitse]
echo ${tokalista[0]}
kissa

Kurkitaan pidemmälle tuota tokalistaa:

Koodia: [Valitse]
echo ${ekalista[1]}
koira
Koodia: [Valitse]
echo ${ekalista[2]}
papukaija

Ekalistan tapauksessa nuo menivät kaikki yhdessä siihen ekaan laatikkoon jonka indeksi on nolla.
Tokalistan tapauksessa kukin meni omaan laatikkoonsa eli täytettiin paikat järjestyksessä 0, 1, 2.

Jos ihmettelet mitä niissä ekalistan "lopuissa" laatikoissa on niin ei mitään.  Kokeile itse:

Koodia: [Valitse]
echo ${ekalista[1]}
Koodia: [Valitse]
echo ${ekalista[2]}
Jos paikkanumeroa eli indeksiä ei anneta niin silloin kyseessä on vain se ensimmäinen paikka jonka indeksi siis on nolla.

Arvaan seuraavan ihmettelyn aiheen:  Mitenkäs sitten tulostan yhdellä kertaa kaikki laatikot tuosta tokalistasta ?
Tuohon tarkoitukseen on käytettävissä tähti "*" eli:

Koodia: [Valitse]
echo ${tokalista[*]}
kissa koira papukaija

Tuo tähti tarkoittaa tässä "kaikki".   Myös sähköpostiosoitteista tuttu "@" merkki tekee saman tässä yhteydessä.

Koodia: [Valitse]
echo ${ekalista[@]}
kissa koira papukaija

Toivottavasti uteliaisuutesi heräsi sillä mielenkiintoiset asiat oikeastaan alkavat tästä !


Ominaisuudet ja muunnokset

Molempia datamuotoja käytetään riippuen tilanteesta.  Joskus on kätevää että kukin olio on omassa laatikossaan jolloin ohjelmassa voi käydä laatikoita eli indeksejä läpi järjestyksessä vaikka "for-do-done" tai "while-do-done" komennolla.  Joskus taasen on kätevää saada yhdellä kertaa kaikki yhdestä indeksistä.

Merkkijonoille (joita nuo "kissa" "koira" ja "papukaija" ovat) samoin kuin koko "laatikostolle" voi tehdä suuruustarkastuksen.  Katsotaanpa mitä numeroita saadaan muuttujista "ekalista" ja "tokalista":

Koodia: [Valitse]
echo ${#ekalista[*]}
1

Koodia: [Valitse]
echo ${#tokalista[*]}
3

Saimme ilmiselvästi kenttien lukumäärän muuttujissa ekalista ja tokalista kuten pitääkin. Meitä voisi kiinnostaa myös minkä pituinen merkkijono on kussakin laatikossa.  bash ymmärtää kysymyksemme kun kerromme sille indeksin:

Koodia: [Valitse]
echo ${#ekalista[0]}
21

Tuo pitää paikkansa sillä välilyönti vie myös aina yhden merkkipaikan, kaikki lemmikithän olivat tuolla rinnakkain erotettuna yhdellä välilyönnillä: "kissa koira papukaija".  Tuon lokeron sisällä emme pysty indeksillä osoittamaan erikseen yksittäisiä lemmikkejä eli jos tarvitsemme sitä niin tulee käyttää tuota toista vaihtoehtoa (kukin lemmikki omaan soluunsa = tokalista).  

echo ${#tokalista[0]}
5

echo ${#tokalista[1]}
5

echo ${#tokalista[2]}
9

Näin saimme kustakin solusta siellä majailevan merkkijonon pituuden.

Voimme myös tallettaa mitä haluamme yksittäiseen soluun, muutetaanpas solu nolla muuttujassa tokalista niin että laitamme ylimääräisen välilyönnin kissan alkuun ja loppuun:

Koodia: [Valitse]
tokalista[0]=" kissa "
Tarkastetaan merkkijonon pituus sen jälkeen:

Koodia: [Valitse]
echo ${#tokalista[0]}
7

Tallessa ovat sekä kissa että välilyönnit sen kahta puolta.

Olenko sitten sidottu siihen kun tein "yksi-indeksisesti" tai "moni-indeksisesti" muuttujan alkuaan ?
Bash on ratkaissut muunnoksen muodosta toiseen hyvin yksinkertaisesti.
Jos muistat tuolta aivan laatikkoleikin alusta niin tavalliset sulut oli se taikasana joka sai aikaan sijoituksen monisoluisesti, kukin olio laitettiin omaan soluunsa eli laatikkoonsa.   Muunnoksen

kaikki yhdessä solussa  ------> kukin omaan soluun

voi tehdä helposti.  Otetaanpa tuo "ekalista"  missä meillä on "kissa koira papukaija" vieri vieressä mutta kaikki samassa solussa (eli indeksi nollan solu)  ja tipautetaan sisältö sieltä uuteen laatikkoon, olkoon nimeltään vaikka "uusilista" tuo muuttuja:

Koodia: [Valitse]
uusilista=(${ekalista[*]})
Jos nyt tarkastamme kuten jo edellä on kerrottu kustakin solusta niin huomaamme että kissa, koira ja papukaija ovat saaneet kukin oman solunsa tuossa uudessa muuttujassa eli löytyvät indekseistä 0, 1 ja 2:

Koodia: [Valitse]
echo ${#uusilista[*]}
3

Koodia: [Valitse]
echo ${uusilista[2]}
papukaija

Huomautus kokeneille lukijoille:  Tuo muunnos on riippuvainen asiasta nimeltään "IFS" jonka tulee olla vakioasetuksissaan.  Jos olet saanut sen muuksi niin sen saa palautettua oletusarvoihinsa komennolla
Koodia: [Valitse]
IFS=$' \t\n'IFS arvon säätely ohjelmassa on tarpeen joissakin erityistilanteissa, ei siitä sen enempää tässä.

Toiseen suuntaan muunnos on hyvin samanlainen, huomaa että sulut puuttuvat:

Koodia: [Valitse]
yksisoluinen=${uusilista[*]}

Bash: in muuttujat ovat aika käteviä "automaattisuutensa" vuoksi, ei tarvitse ihmeemmin miettiä datatyyppejä vaan alat vain käyttää muuttujaa. Kaikki muuttujat ovat yksiulotteisia taulukoita ja jos et käytä taulukkoindeksiä niin se on sama asia kuin että indeksi nolla on käytössä (ilman että sitä erikseen on laitettu näkyviin).
Tehdäänpä pieni koe havainnollistamaan mitä tapahtuu indeksin kanssa ja ilman:

Koodia: [Valitse]
lemmikki[0]=kissa
lemmikki[1]=koira
lemmikki[2]=papukaija

Tuo tekee 3 soluisen muuttujan jonka nimi on "lemmikki".  Tarkastetaanpa koko lemmikki eli

Koodia: [Valitse]
echo ${lemmikki[*]}
kissa koira papukaija

Totuuden paikka:  Muutetaan muuttujan "lemmikki" sisältöä niin että emme kerrokaan indeksiä:

Koodia: [Valitse]
lemmikki=marsu
Tarkastetaan:

Koodia: [Valitse]
echo ${lemmikki[*]}
marsu koira papukaija

eli muutos kohdistuu indeksiin nolla jos et kerro indeksiä.

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.03.14 - klo:17.12
bash muuttujat ja taulukkotyyppisyys

Kiitosta taas kertaalleen ! Kamalan iso homma tämmöisten tekemisessä on mutta on ne sitten hyödyllisiäkin.
**
Kyllä BASH-rintamalla tapahtuu kokoajan vieläkin, sillä BASH4.3 tuli äskettäin. Usoitteessa: http://www.tldp.org/LDP/abs/html/ on sen ohjekirja

Jossain vaiheessa oman koneen suojaamisesta tulee tärkeää. Eihän sitä tiedä onko BASH oikea
työkalu tietoturvan parantamiseen - tai siis BASH ei varmasti paranna mitään vaan parantaminen tehdään säätämällä Linuxia ja käyttämällä ulkoisia ohjelmia. Lisäksi välttämätön lisä on monimuotoisuus - ja silloin BASH loistaa sillä jokainen osaa tehdä jotakin käyttämällä BASHia.
**
Kyllä tällä BASH:illa saa tuloksia nopeastikin jos osaa käskeä. Minä en kylläkään osaa muuten kuin sattumalta. Esimerkiksi BASH osaa laskea 2500 kertoman noin 0.1 sekunnissa; kaikki sen 7000 numeroa jotka kaikki taitavat myös olla oikein.
Eihän laskukone ole BASH vaan bc - mutta BASH johtaa orkesteria ja yksinään bc ei toimisi sekään.
Tuommoisilla laskuilla on muuten merkitystä: joissain laskuissa on lopputulos pienehkö mutta laskujen väliarvot niin suuria ettei niihin pysty oikein mikään ja syntyvät pyöristysvirheet ovat suurempia kuin lopputulos.
**
Lasku "ln e" toimi aina vain yhden kerran ja toisintakerroilla ei tulostanut mitään; ei tosin päätynyt virheeseenkään. Kunnes lisäsin laskurutiinin loppuun: bash --login .
**
Matematiikkaohjelmaa ei käytännössä tarvitse kutsua vaan pääte toimii automaattisesti myös kalkulaattorina vaikka päätteen vanhat ominaisuudet säilyvätkin: senkun kirjoitat päätteeseen 1+1 niin painettuasi enteriä tulee vastaus: 2. Samoin voit skripteissäsi käytää matematiikkaa. Kaikki perus-merkintätavat ja -laskutoimitukset tunnetaan, siis vaikka tuon testilaskun: round[log[.11e2-2*sin+30]+ln[e^[round[16*[sin30*cos60]]]]] voi laittaa skriptiinsä ja vastaus: 5 tulee hetkenkuluttua. Eikä skriptiäsi tarvitse muuten muokata mitenkään.

Sieluna tässä on linuksin oma bc. Muuten "rajoittamaton tarkkuus" ei ole markkinointihöpinää. Mutta se on kylläkin tosi että se vaatii hieman tukea BASH:ilta.

Tietenkään tätä matematiikkaohjelmaa ei "kannata" käyttää sillä se on todella hidas, se tuntee vain yksinkertaisia laskuja ja sillä on omituisia ominaisuuksia paljon. Mutta sen arvo onkin siinä että se on itsetehty. Ja tietenkään tämä minun tekemäni ei ole sinun itsesi tekemä. Mutta tämä skripti osoittaa mitkä mahdollisuudet ainakin löytyvät ja alkuun on silloin helppo lähteä kun tietää että homma onnistuu vaikka työtä siinä voikin olla. Sinä voit tehdä skriptin jolla on paljon paremmat ominaisuudet ja nopeudellehan ei kattoa olekaan.

Esimerkiksi olisi varmaan kivaa käyttää jotain parhaista matematiikka-ohjelmista tämän orjana jolloin käytöstä saisi niin yksinkertaista kuin haluja-ja-kykyjä piisaa.
**
Uusien kykyjen lisääminen on helppoa, ja jostain ihmeellisestä syystä tämä itsetehty matikkaohjelma pystyy opettamaan minullekin matikkaa. Nyt lisäsin kyvyn seuraavankaltaisiin laskuihin: sin e^4
Lähes automaattisesti mukaan tulivat myös kantalukumuunnokset tyyppiä: kahteen e
**
Taas kertaalleen skriptiini oli tullut kontrollimerkkejä. Vaikka teen kehitettävästä skriptistä back-up:in aamuin-päivin-illoin niin nuo kontrollimerkit pilasivat jokaisen back-upinkin jonka latasin ja töistäni kerkisi mennä pilalle monen kuukauden työt ja pääsin takaisin toimintaan vasta kun latasin heti boottauksen jälkeen tuon parikuukautta sitten tehdyn skriptin ja hylkäsin kaikki sen jälkeiset skriptit.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 07.05.14 - klo:15.28
Taas kertaalleen skriptiini oli tullut kontrollimerkkejä. Vaikka teen kehitettävästä skriptistä back-up:in aamuin-päivin-illoin niin nuo kontrollimerkit pilasivat jokaisen back-upinkin jonka latasin ja töistäni kerkisi mennä pilalle monen kuukauden työt ja pääsin takaisin toimintaan vasta kun latasin heti boottauksen jälkeen tuon parikuukautta sitten tehdyn skriptin ja hylkäsin kaikki sen jälkeiset skriptit.

Koodiin eksyneet väärät merkit saa kyllä poistettua vaikka tr-ohjelmalla. Laita joku vioittunut esimerkkitiedosto tänne niin voin vilkaista.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.05.14 - klo:18.28
Koodia on liikaa jotta sen saisi laitettua foorumille - koetan kokoajan supistaa mutta hidasta tuntuu olevan. Ohjelman kokeileminen onnistuu kuitenkin helposti jahka saan koodia supistettua tarpeeksi. Lisään silloin ohjeet.
Toimiva versio kyllä on. Ohjelman logiikka on erittäin omituinen sillä pääohjelmana toimii terminal-ohjelma ja se kutsuu koodissa esitettyjä funktioita. Toiminta kalkulaattorina on vielä omituisempi, sillä kun terminaalin riville kirjoittaa jotakin jota ei vastaa yksikään funktio niin sen aiheuttama virhe aiheuttaa funktion command_not_found_handle kutsumisen. Funktio command_not_found_handle puolestaa kutsuu funktiota laske joka puolestaan kutsuu tarpeellisina pitämiään toisia funktioita. Tämän ohjelman tekeminen oli liian vaativaa minun ikäiselleni: työskentelyni oli liian hidasta joten työpäivistä tuli liian pitkät.

Ja kyllä tuota tr-käskyäkin on käytetty niinkuin käskyä od ja ohjelmaa bless - se kyllä täytyy tunnustaa että apu olisi tarpeen jotta saisi sovellettua käskyjä oikein. Sekin on epäselvää missä muuallakin sitä apua tarvitsisi, sillä vaikka puhunkin kontrollimerkeistä niin tuntuu siltä että niitä näkymättömiä merkkejä on runsaasti muitakin - ja esimerkiksi ainakin väri- ja kursori-kikkailu toimii kontrollimerkeillä joten niiden poistaminenkin on joskus vahingollista.

Koko konntrollimerkki-rumba olisi helppoa josvain gedit-ohjelma näyttäisi ne. Mutta en ole löytänyt siihen mitään konstia - netistäkään en ole löytänyt mitään. Kun nimittäin löytää rivin jossa niitä kontrollimerkkejä on niin senkuvaan pyyhkii rivin pois ja kirjoittaa uudestaan varoen hipaisemastakaan mitään kummallista nappia tai kosketuslevyä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 07.05.14 - klo:18.44
esimerkiksi ainakin väri- ja kursori-kikkailu toimii kontrollimerkeillä joten niiden poistaminenkin on joskus vahingollista.

Näiden pitäisi kuitenkin olla koodissa escape-koodeina ascii-tekstinä, eikä raakoina kontrollimerkkeinä.

Koko konntrollimerkki-rumba olisi helppoa josvain gedit-ohjelma näyttäisi ne.

Heksaeditori on tähän oikea työkalu. Esimerkiksi Bless.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.05.14 - klo:06.25
Perus-matematiikkaa päätteelle opettava skripti mahtuu nyttemmin foorumille hyvin ja toimiikin kelvollisesti - sen lisäkehittäminen on mukavaa vaikka eihän koko skriptillä ole mitään käytännön merkitystä. Mutta en sitä ole tuon merkityksellisyyden takia tehnytkään vaikka sen rajoittamaton tarkkuus onkin mielenkiintoinen.

Sikälikin skripti on mielenkiintoinen että sen ominaisuudet olisivat olleet toteutettavissa jo kolmekymmentä vuotta sitten jolloin BASH jo menetti nopeasti merkitystään. Sekin mielenkiintoinen seikka selvisi skriptiä tehdessäni että vastaavankaltaisia skriptejä on jo ollutkin, mutta niiden koodia ei ole levitetty.

Sekin kummallisuus ilmeni että vastaavankaltaisia virheen-käsittelyyn soveltuvia skriptejä on ollut: ajattelepa että BASH:issa olisi jo valmiina erinomainen virheistä kertova rutiini !

Skriptin koodi on paikassa: http://forum.ubuntu-fi.org/index.php?topic=303.msg306401#msg306401
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 12.05.14 - klo:11.17
Vihdoin sain perus-matematiikan lisäävän skriptin supistettua tarpeeksi jotta se mahtuu foorumille. Siis matematiikkaa voit käyttää skripteissäsikin kunhan lisäät matematiikkarutiinit käyttöön ottavan käskyn. Pitänee aikanaan lisätä matriiseja käsitteleviä käskyjä, mutta ne ei vielä mahdu mukaan.

Toiminta: lataa koodi verkkosivulta http://forum.ubuntu-fi.org/index.php?topic=303.msg306401#msg306401

Koodi ja tämän keskustelun viimeiset viestit maaliskuulta lähtien kuuluisivat varmaan jonkin oman aiheen alle. Laita skripti viestiin liitetiedostoksi (Lisätoiminnot->Liite viestin kirjoituksen yhteydessä). Esimerkiksi .sh-tiedostopääte on sallittu, joten tiedoston nimeksi käy vaikkapa laskin.sh). Maksimikoko on 768 kB / liitetiedosto.

Vaihtoehtoisesti voit ladata tiedostot Dropboxiin tai johonkin muuhun pilvipalveluun. Ubuntuun saa Dropbox-asiakasohjelman, joka synkronoi koneellasi olevan hakemiston automaattisesti ja välittömästi verkkoon ja muille koneille, joilla on vastaava asiakasohjelma. Tätä voi hyödyntää sekä julkaisuun että omien tiedostojen varmuuskopiointiin. Nämä kannattaa tosin pitää erillään, eli julkaiset vain ajoittain toimivia versioita.

Seuraava askel onkin sitten versionhallinta gitillä, jolloin julkaisu onnistuu GitHubin kautta.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 21.07.14 - klo:07.55
Yritän scriptissä laskea mm prosentteja mutta saan tulokset jotka ovat yhteenlaskettuna yleensä vain 99%:a.
Eli on kolme arvoa joista lasken prosentit 100*arvo1/kaikki ja 100*arvo2/kaikki ja 100*arvo3/kaikki ja näiden tulokset sitten plussailen yhteen. Epäilen, että itse laskut menevät oikein mutta scripti pyöristää arvoista desimaalit pois (arvo1,arvo2,arvo3 ovat aina lukuja ilman desimaaleja).
Mitenköhän saisi tuohon myös desimaalit mukaan? Nyt arvo1 on 38 ja kaikki on 1866 josta saan laskimella laskettuna 2,036 % mutta scripti näyttää 2% ja ilmeisesti kaikissa prosentteissa samanlaista heittoa jolloin lopputulos on melkein oikein.
arvo3 on 428 ja yhteensä yhä 1866 ja tästä laskettuna laskimella 22,93% mutta scriptini näyttää 22% :-/

Edit:
Niin juu ja prosentit lasken näin: luku=$((100*$ARVO1/$KAIKKI))
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 21.07.14 - klo:09.56
arvo3 on 428 ja yhteensä yhä 1866 ja tästä laskettuna laskimella 22,93% mutta scriptini näyttää 22% :-/

Edit:
Niin juu ja prosentit lasken näin: luku=$((100*$ARVO1/$KAIKKI))

Bash tukee vain kokonaislukulaskentaa, eli mitään desimaaliosia ei saa ulos, eikä niitä voi myöskään syöttää sisään. Jakolaskut pyöristyvät alaspäin, kuten esimerkistäsi nähdään.

Tarkempaa laskentaa voit tehdä esimerkiksi bc:n tai awk:n avulla.

bc:

Koodia: [Valitse]
luku=$(bc <<< "scale=4; 100*$ARVO/$KAIKKI")
Koodia: [Valitse]
ARVO=428 KAIKKI=1866 luku=$(bc <<< "scale=4; 100*$ARVO/$KAIKKI"); echo $luku
22.9367

awk:

Koodia: [Valitse]
luku=$(awk "BEGIN{printf \"%.4f\n\",100*$ARVO/$KAIKKI}")
Koodia: [Valitse]
ARVO=428 KAIKKI=1866 luku=$(awk "BEGIN{printf \"%.4f\n\",100*$ARVO/$KAIKKI}"); echo $luku
22.9368


bc:n ongelmana on tulosteen leikkautuminen, eli se ei pyöristä oikein. Lisäksi etunolla puuttuu tulosteesta, kun arvo on välillä (-1, 1):

Koodia: [Valitse]
ARVO=1 KAIKKI=1000 luku=$(bc <<< "scale=2; 100*$ARVO/$KAIKKI"); echo $luku
.10
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 21.07.14 - klo:10.26
Bash tukee vain kokonaislukulaskentaa, eli mitään desimaaliosia ei saa ulos, eikä niitä voi myöskään syöttää sisään. Jakolaskut pyöristyvät alaspäin, kuten esimerkistäsi nähdään.

Tarkempaa laskentaa voit tehdä esimerkiksi bc:n tai awk:n avulla.

bc:

Koodia: [Valitse]
luku=$(bc <<< "scale=4; 100*$ARVO/$KAIKKI")

Kiitän. Laitoin tällä (scale=2:lla tosin) ja jo näytti paremmalta.

Lainaus
bc:n ongelmana on tulosteen leikkautuminen, eli se ei pyöristä oikein. Lisäksi etunolla puuttuu tulosteesta, kun arvo on välillä (-1, 1):

Koodia: [Valitse]
ARVO=1 KAIKKI=1000 luku=$(bc <<< "scale=2; 100*$ARVO/$KAIKKI"); echo $luku
.10

Täytyy seurailla tilannetta tuleeko tästä ongelmaa pienin arvo luvulla tässätapauksessa voi olla 0 joten etunollasta ei varmaankaan tule ongelmaa mutta tuon pyöristyksen näkee sitten, kun saan tuon kunnolla testiin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 21.07.14 - klo:11.21
Käyttäisin itse awk:ta, erityisesti jos viimeisellä desimaalilla on jotain merkitystä. Sen kanssa ei tule kumpaakaan ongelmaa. bc on hyödyllinen, jos laskutoimituksissa tarvitaan suurta tarkkuutta (enemmän kuin normaalien double-liukulukujen 15 numeroa).
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: rationaalinen - 08.08.14 - klo:13.07
Tervehdys Ubuntu & shell skriptaus gurut!

Kiitän näin aluksi tämän alueen keskustelijoita hyvistä vinkeistä ja esimerkeistä joiden avulla olen kirjoittanut muutaman valokuvien ja videoiden käsittelyä automatisoivaa shell-skriptiä.

Kysymys:
Miten shell-scriptillä saisi välitettyä useamman argumentin komentorivi-ohjelmalle vaiheittain ja sitten suljettua ohjelman?
Esim. echo 1+2 | octave
käynnistää octave:n, palauttaa tuloksen ans = 3 ja sulkee automaattisesti octave:n.

Entä jos haluaisin octave:n laskevan kaksi operaatiota ja sulkeutuvan vasta sitten? Esim. 1+2 ja 2+3 ja sulkeutuvan vasta toisen operaation jälkeen. Esimerkkien lopputulokset tulostuisivat shell:iin "ans = 3" ja "ans = 5".

t. Rationaalinen
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 08.08.14 - klo:13.21
Kysymys:
Miten shell-scriptillä saisi välitettyä useamman argumentin komentorivi-ohjelmalle vaiheittain ja sitten suljettua ohjelman?
Esim. echo 1+2 | octave
käynnistää octave:n, palauttaa tuloksen ans = 3 ja sulkee automaattisesti octave:n.

Entä jos haluaisin octave:n laskevan kaksi operaatiota ja sulkeutuvan vasta sitten? Esim. 1+2 ja 2+3 ja sulkeutuvan vasta toisen operaation jälkeen. Esimerkkien lopputulokset tulostuisivat shell:iin "ans = 3" ja "ans = 5".

Haetko jotain tällaista?
Koodia: [Valitse]
echo $1+$2|octave
eli tee nanolla oct.sh, ja anna sille suoritusoikeudet ja aja ohjelma päätteessä:
Koodia: [Valitse]
chmod 755 oct.sh
./oct.sh

Parametrien määrän saa seuraavasti:
Koodia: [Valitse]
echo ${#@}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 08.08.14 - klo:13.43
Vai ajatkohan tätä takaa?
Koodia: [Valitse]
echo "2+3,1+2"|octave

parametreina:
Koodia: [Valitse]
echo "$1+$2,$3+$4"|octave
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: rationaalinen - 09.08.14 - klo:21.21
Tervehdys!

Kamara, kiitos nopeasta ja käypäisestä vastauksesta. Testailin antamista shell:stä useammalle ohjelmalle ja esim. octavea yksinkertaisempi genius-komentorivi ohjelma ei tunnu hyväksyvän useampaa argumenttia lainausmerkkien "" sisällä pilkulla erotettuna. Komentokehoitteessa testaten:
echo "2+3,1+2" | genius
line 1: ERROR: syntax error before ','

echo "2+3" | genius
5

Mitenköhän ylemmässä tapauksessa saisi useamman argumentin menemään sisälle ohjelmaan (esimerkissä geniukseen). Lisäksi kaikki ohjelmat eivät sulkeudu automaattisesti vaan niille pitäisi saada välitettyä lopetusargumentti "quit".

t. Rationaalinen
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 09.08.14 - klo:21.53
Taitaa eri ohjelmissa olevan eri käytännöt. geniuksessa näyttää olevan print-käsky, jolla voi tulostaa useampia argumentteja:
Koodia: [Valitse]
echo "print(2+3);print(4+5)"|genius

Edit - edellisestä viestistä kuulin ensimmäisen kerran genius-ohjelmasta, joten en todellakaan ole mestari sen kanssa, mutta osasin manuaalia tavata vähän.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 10.08.14 - klo:11.46
Skripti palauttaa kutsuvalle ohjelmalle yhden paluuarvon ( = exit code, return code) mutta esimerkiksi tiedoston kautta voi välittää takaisin mielivaltaisen kokoisia rakenteita.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: rationaalinen - 10.08.14 - klo:14.57
Ciao!

Kiitoksia hyvistä neuvoista taasen kamara ja ajaaskel.

Milläköhän loitsulla debugfs nimiselle ohjelmalle saa syötettyä argumentteja? Debugfs:n manpage www:ssä:
http://linux.die.net/man/8/debugfs

tai shell:stä man debugfs.

Kaikenlaisia optioita löytyy mutten otsallani löydä kelpoista argumenttien putkittamistapaa.

echo "print(arg1);print(arg2)" | debugfs -optiot
ja
echo "arg1,arg2" | debugfs -optiot

eivät tunnu kelpaavan.

Ei ole ihan helppo laji harrastajalle;-).
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 10.08.14 - klo:15.18
Olisikohan jotain seuraavaa...
Yksi komento...
Koodia: [Valitse]
debugfs -R Komento

tai useampi komento filen avulla:
Koodia: [Valitse]
debugfs -f Komentofile

Rohkea voisi kokeilla myös seuraavaa...
Koodia: [Valitse]
echo "montakomentoa" | debugfs -f /dev/stdin

Mutta en takaa toimivuutta, enkä tiedä jääkö kyseinen käsky päälle.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 10.08.14 - klo:23.16
En arvaa onko ongelmasi tämä mutta...
Noin yleisesti, syötettä voi mennä kahta täysin eri tietä:  komentoriviparametrit (command line parameters) ja vakiosyöte (standard input). Kaikki ohjelmat eivät tue molempia tai joskus pitää erikseen ilmaista jos halutaan käyttää vakiosyötettä eikä komentoriviparametreja.  Tuo putkitus käyttää vakiosyötettä.  Esimerkki "echo": n avulla joka ei lue vakiosyötettä:

Koodia: [Valitse]
echo "kissa" | echo
ei tulosta mitään (tai oikeastaan pelkän rivin siirron).

Jotta saisimme putken kautta tulevan tavaran tuonne echon perään hännille tarvitsemme jonkin apukeinon siirtää putkesta tuleva syöte "echo" komennon perään komentoriviparametreiksi.  "xargs" komento tekee tuon asian:

Koodia: [Valitse]
echo "kissa" | xargs echokissa

On myös toinenkin tapa käyttämällä "read" komentoa:

Koodia: [Valitse]
echo "kissa" | (read aaa; echo $aaa)
Aina ei ole viisasta putkittaa vaan käyttää komennon korvausta (command replacement) "$()":

Koodia: [Valitse]
echo $(echo "kissa")

Tuo sama on kerrottu esimerkiksi täällä:

http://superuser.com/questions/189362/how-to-pipe-command-output-to-other-commands (http://superuser.com/questions/189362/how-to-pipe-command-output-to-other-commands)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.09.14 - klo:14.16
Suhtaudun sed:iin, awk:iin, grep:iin ... nyt hieman toisin kuin aikaisemmin. Netistä luin että skriptiä optimoidessa on järkevää pyrkiä noista ulkoisista ohjelmista eroon. Matematiikka-skriptissä korvasinkin niistä ne joihin osasin koodata vastaavan toiminnan BASH:in alkeis-funktioilla - ja tosiaan joskus sedit-sun-muut hidastavat..

Netissähän nyt puhutaan mitä vaan. Ja taidottoman skriptaajan havainnot merkitsee aika olemattomasti ohjelmointi-virtuoosien vuosi-kymmenien työn rinnalla. Mutta kun lisäksi ottaa huomioon että syy skriptin kääntämiseen on yleensä se ettei ulkopuoliset saa katsottua kuinka kyvytöntä koodia on tehty niin enää en usko sokeasti noihin ulkoisiin ohjelmiin.
**
Jokaisen matriisin jokaisella jäsenellä on sekä arvo että osoite. Koska on totuttu siihen että osoitteet alkavat aina nollasta ja ovat perättäisiä niin osoitteen käsittely unohdetaan. Eikä siitä tuollaisella yksinkertaisella matriisilla tulekaan harmia.

BASH:issa on vain yksiulotteinen matriisi mutta toisin kuin yleensä niin BASH:issa matriisien osoitteiksi kelpaavat kaikki positiiviset kokonaisluvut: ne voivat alkaa mistä luvusta vaan eikä niiden tarvitse olla perättäisiä. Ja arvoiksi kelpaavat kaikki luvut ja tekstijonot joissa voi olla useimpia erikoismerkkejäkin mutta ei välilyöntejä. BASH:in matriisin erikoisominaisuudet ovat matematiikassa joskus tarpeellisia ja on harmi mikäli nuo erikoisominaisuudet hukataan käsittely-rutiinien takia.

Funktioit hyväksyvät matriiseille minkä nimen hyvänsä eikä niissä tarvitse määritellä matriisin kokoa. Koko voi olla määritelty pää-ohjelmassa tai ei.

Muihinkin matriisi-operaatioihin on helppo luoda nopeat funktiot - niinkuin esimerkiksi käänteis-matriisiin, determinanttiin, rivi- ja sarakesummiin ...

Tämäntapaisten funktioiden tekemisessä joku skriptiajurin kaltainen on ehdoton edellytys, sillä kokeillessa täytyi skriptistä tehdä satoja versioita ja aikaa kuluu skriptiajurin kanssa kymmeniä tunteja mutta ilman skriptiajuria kuluisi niin monta viikkoa että eihän sitä kukaan jaksaisi.
 
Koodia: [Valitse]
#!/bin/bash

function tulosta_matriisi_näytölle () {
(echo -n "Matriisin $1 jäsenten arvot     : "; eval echo -e \${$1[@]}
echo -n "Matriisin $1 jäsenten osoitteet : "; eval echo \${!$1[*]}) | column -t
echo
}

function kirjoita_matriisi_levylle () {
eval echo  \${$1[@]} > ~/tiedosto1   # Matriisin $1 jäsenten arvot   
eval echo \${!$1[*]} > ~/tiedosto2   # Matriisin $1 jäsenten osoitteet
}

function lue_matriisi_levyltä () {
apu0=0
apu1=($(cat  ~/tiedosto1))
apu2=($(cat  ~/tiedosto2))
while true; do
  [[ -z ${apu1[$apu0]} ]] && break 
  eval "$1[${apu2[$apu0]}]"="${apu1[$apu0]}" # mielenkiintoinen lause
  let apu0++
done
unset apu0 apu1 apu2 
}

# seuraavat lauseet ovat vain sen tarkistamista että kaikki tapahtuu oikein.
matriisi1[7]=55.5
matriisi1[123]=joo
matriisi1[334455]=-777
matriisi1[1]=15e23 # matriisin jäsenet voi määritellä missä järjestyklsessä haluaa. Tulostu tapahtuu kuitenkin osoitteiden suuruusjärjestyksessä.
echo matriisi1 ennen tallettamista levylle :
tulosta_matriisi_näytölle matriisi1
kirjoita_matriisi_levylle matriisi1
unset matriisi1
lue_matriisi_levyltä matriisi1
echo matriisi1 levytä lukemisen jälkeen:
tulosta_matriisi_näytölle matriisi1
unset matriisi1
echo

matriisi2=({a..z})
echo matriisi2 ennen tallettamista levylle :
tulosta_matriisi_näytölle matriisi2
kirjoita_matriisi_levylle matriisi2
unset matriisi2
lue_matriisi_levyltä matriisi2
echo matriisi2 levyltä lukemisen jälkeen:
tulosta_matriisi_näytölle matriisi2

**
Tulipa taas uutta tietoa ja asiasta josta saa erittäin huonosti ohjeita. Homma on seuraava: käskyn tuloste siirtyy matriisiin käskyllä: matrix=($(käsky)). Mutta menetelmä ei toimi oikein mikäli tulosteessa on välilyöntejä.  Käsky muuttuu silloin hieman:
Koodia: [Valitse]

IFS=$'\n' matrix=($(käsky)); unset IFS . (unset IFS ihan vain varmuuden takia sillä teoriassahan sitä ei tarvita).

**
Yksikään virtuoosi ei dokumentoi tekemisiään kunnolla ellei ole pakko sillä ymmärrettävän dokumentin tekemisessä on paljon suurempi työ kuin koodaamisessa eikä ymmärrettävästä dokumentista ole virtuoosille itselleen hyötyä vaan hyvin ylimalkainen dokumentti on riittävä; lisäksi virtuoosit ovat neroja dokumenttien hukkaamisessa. Tunnustettuja BASH-virtuooseja oli BASH:in alkuaikoina tuhansia ja heidän työnsä päätyi dokumentoimattomana viralliseen koodiin  sillä pakkoa dokumentointiin ei juurikaan ollut. Voi olla montaakin mieltä siitä onko järkevää käyttää dokumentoimattomia ominaisuuksia, mutta sen osaan kertoa että BASH:issa on ziljoonia dokumentoimattomia ominaisuuksia.   
**
Mistähän on oikeastaan lähtöisin käsitys ettei BASH kelpaa mihinkään? Vaikka toisaalta sitähän käytetään vain linux-kielisten käyttöjärjestelmien toiminnan aloittamisessa - siis ollenkaan tärkeää?
**
Toistuvat tehtävät on tarkoitus suorittaa funktioissa. Toistot (loopit) kannattaa myös muodostaa funktiossa. Esimerkiksi
function Kertaa () { MontakoKertaa=$1; shift; for n in $(seq $MontakoKertaa); do $@; done;}; Kertaa 5 echo "tässä voi olla minkähyvänsä käsky tai funktio"
**
Taas kertaalleen yritin tutustua BASH:in rinnakkaisprosessointiin mutta kokonaiskuvan saamista haittaa se, että rinnakkaisprosessointiin on 27615 ratkaisua joista useimmat osittaisia ja ohjeissa löytyy pääasiassa sutta&sekundaa:
1. Itse linux voi ajaa yhtaikaisesti monia prosesseja ja BASH hallitsee sitä käskyillään bg ja fg. Myös käskyn tai funktiokutsun perään voi lisätä: & joka siirtää suorituksen tausta-ajoksi. Käsky wait taas odottaa kunnes kaikki on tehty ja: wait $! odottaa viimeksi määrätyn prosessin loppumista ja wait $pid sen prosessin loppumista jonka pid annetaan.
2. Rinnakkaisprosessointiin on myös ohjelma nimeltään: parallel. Yksinkertainen esimerkki: time parallel sh -c ::: "sleep 1; echo 1loppu" "sleep 1; echo 2loppu" "sleep 1; echo 3loppu" joka tulostaa mitä pitäisikin ja vie aikaa 1.212 sekuntia niinkuin odottaa sopiikin.
3. Myos käskyä xargs voi käyttää

- käskynsuorituksen aikana tarvitaan usein näyttöä missä vaiheessa käskyn suoritus on eikä useimmissa käskyissä ole suoritusajan näyttöä. Siispä on useita menetelmiä lisätä tämä ominaisuus esimerkiksi rinnakkaisprosessilla. Tällaisia ohjelmia on muunmuassa: bar, pv (pipe viewer), dialog ...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 24.11.14 - klo:22.30
Lainaus
...josta mitkään ohjeet, man-sivut tai netissä olevat organisaatoiden super-virtuoosit eivät puhu...

Tuli mieleen joitakin asioita tuosta edellä olevasta.  Pieni johdanto niin tästä on iloa ehkä muillekin... 

Muuttuja joka on taulukko (engl. array)

Jossain aiemmin kerroin että Bashin muuttujat ovat luonnostaan yksiulotteisia taulukoita --- jos käytät niitä sillä tavalla.  Se mikä joissain muissa ohjelmointikielissä tunnetaan muuttujan tyyppinä määräytyy Bashissä hyvin yksinkertaisesti ilman mitään määritystä: Alat käyttää muuttujaa kuin se olisi taulukko ja se on sellainen.   Mitä tuo sitten oikeastaan tarkoittaa ?  Mainitsit tuolla sijoituslauseen joka on muotoa:

Koodia: [Valitse]
taulukko=( ...jotain...)
Tuossa  =()    -muoto kertoo Bashille että muuttujaan jolle keksin nimen "taulukko" halutaan tiputtaa sisään jotain monikenttäistä ja Bash alkaa tehdä "numeroituja laatikoita" peräkkäin alkaen numero nollasta eteenpäin eli itseasiassa meillä on sen jälkeen:

Koodia: [Valitse]
taulukko[0], taulukko[1], taulukko[2],..... taulukko[n]
eli indeksinumeroita on niin pitkälle kuin yksittäisiä asioita tuli tuolta sulkujen sisältä.

Jos haluat kurkistaa laatikkoon tai käyttää sitä johonkin niin et voi kirjoittaa pelkästään
Koodia: [Valitse]
$taulukko[0] vaan aaltosulkujen kanssa
Koodia: [Valitse]
${taulukko[0]}. Ja ne kaikki kentät yhdellä kertaa saa ilmauksella
Koodia: [Valitse]
${taulukko[@]} sijoitettua eteenpäin, tulostettua tai mitä sitten halutaankin tehdä.

Ajatellaan pientä esimerkkiä tästä  =()  -asiasta:   

Koodia: [Valitse]
teksti="kissa koira hevonen"
Tuossa meillä näyttäisi olevan kolme merkkijonoa joiden välissä on välilyönti.  Tuossa muodossa tuo koko hoito on tuolla $teksti muuttujassa kuitenkin yhdessä ainoassa "laatikossa" eli jos ajatellaan sitä taulukkona niin indeksissä nolla.   Eli jos tulostat sen niin

Koodia: [Valitse]
echo ${teksti[0]}
kissa koira hevonen

Oikeasti kyseessä onkin yksi merkkijono johon sisältyvät nuo välilyönnit eli tuo kaikki on yhtä kokonaisuutta.

Koodia: [Valitse]
taulukko=($teksti)
Tuo tiputtaa kunkin välilyönnillä erotellun sanan omaan säilöönsä (sen sijaan että ne olisivat kaikki yhdessä):

Koodia: [Valitse]
echo ${taulukko[0]}
kissa
Koodia: [Valitse]
echo ${taulukko[1]}
koira
Koodia: [Valitse]
echo ${taulukko[2]}
hevonen

Mutta:  Millä perusteella Bash pätkii sen juuri välilyönnin kohdalta ?  Vastaus on tuo "IFS" joka on yksi Bashin ennaltamääritetty muuttuja.   Oletuksena se sisältää välilyönnin eli "SPACE 20h", Tab-merkin eli "TAB 09h" ja rivinsiirron "LF  0ah".   Kurkataanpa vielä "hexdump -ohjelmalla":

Koodia: [Valitse]
hexdump -C <<< "$IFS"
00000000  20 09 0a 0a                                       | ...|
00000004

Näyttäisi olevan.

Nyt herää kysymys jos haluaisinkin pätkiä jostain muusta kohdasta kuin välilyönnin tai tabin kohdalta.  Tuo onnistuu kyllä yksinkertaisesti muuttamalla tuon IFS muuttujan arvon mutta:   Hyvä tapa on tallettaa sen sisältö ennen kuin muuttelee ja palauttaa takaisin mitä se oli kun ei enää tarvita tätä muutettua asetusta.  Ei ole suositeltavaa oikaista ja antaa "unset" sille lopuksi vaan  tehdä talletus ja palautus.  Tuo on selitetty tarkemmin edistyneissä Bash-oppaissa. Tyypillinen tallennus/palautus:

Koodia: [Valitse]
oldifs="$IFS"
...tehdään_jotain...
IFS="$oldifs"

IFS muuttujan käyttöön liittyy yksi mielenkiintoinen poikkeus jolloin sitä ei tarvitse erikseen tallettaa/palauttaa:

Jos IFS asetuksen jälkeen seuraa välittömästi samalla rivillä heti perässä joko komento tai funktio.  Tällöin muutettu IFS sisältö on voimassa vain tuon komennon tai funktion ajan.   Tuota näkee usein käytettävän "read" komennon + silmukan yhteydessä:

Koodia: [Valitse]
while IFS= read -r tiedostonimi; do
echo "$tiedostonimi"
done  < tiedostonimiä_tiedostossa.txt


jolloin lukeminen ei epäonnistu jos tiedostonimessä on välilyönti tai useampikin.

Yksi hyödyllinen mutta harvemmin tunnettu ilmaisu Bashissä on 
Koodia: [Valitse]
+= eli "append" jossa alkuperäistä muuttujaa ei korvata vaan sen perään lisätään.  Tuo "plus-yhtäläisyysmerkki" sijoitus toimii sekä tekstillä että taulukoilla.  Sillä voi tehdä tekstin ketjuttamista peräkkäin mutta taulukoiden yhteydessä se on kätevä lisäämään taulukkoon uutta sisältöä. Tekstiesimerkki:

Koodia: [Valitse]
a=mäyrä
a+=koira
echo "$a"
mäyräkoira

ja taulukolla:

Koodia: [Valitse]
taulukko+=("kissa")
taulukko+=("koira")
taulukko+=("hevonen")
echo ${taulukko[@]}
kissa koira hevonen

Tuossa nuo menivät siis indekseihin 0, 1 ja 2 muuttujassa "taulukko".  Bashissä on paljon erilaisia tapoja ilmaista asioita ja ne on dokumentoitu melko hyvin mutta silti sen oikean asian löytäminen voi olla joskus vaikeaa.  Nämä taulukkoasiat (array) löytyvät täältä:

http://tldp.org/LDP/abs/html/arrays.html (http://tldp.org/LDP/abs/html/arrays.html)

Muutenkin tuo on se opas jota koodaajat käyttävät hallitsevana dokumentaationa, alkusivu on tässä:

http://tldp.org/LDP/abs/html/ (http://tldp.org/LDP/abs/html/)


Teksti-indeksin käyttö (associative array)

On yksi tilanne jossa taulukko _täytyy_ myös Bashin tapauksessa määritellä ennakolta:  Jos halutaan käyttää hieman erikoisempaa taulukkoa jossa taulukon solujen indeksit  eivät ole numeroita 0, 1, 2,...  vaan niille annetaan nimet (jotka voivat olla mitä vain haluat keksiä tilanteen mukaan).  Tällöin sekä taulukkoon tallettaminen että sinne lisäys tapahtuu noiden nimi-indeksien avulla eli voidaan ajatella mielikuvana että tavarat ovat tallessa levällään omissa nimetyissä paikoissaan missä lienevätkin mutta nimi-indeksillä löytyy indeksinumeron asemasta. Ehkä postilaatikosto jossain taloyhtiössä kuvaisi sitä hyvin:

Koodia: [Valitse]
declare -A postilaatikosto
eli määrittelimme muuttujan "postilaatikosto". Lukumäärää ei tarvitse kertoa missään Bashin taulukossa, niitä laatikoita on aina se mikä tarvitaan.

Tiputetaan jotain noihin postilaatikoihin nimi-indeksillä:

Koodia: [Valitse]
postilaatikosto[Nieminen]="myyntikirje"
postilaatikosto[Mattila]="karhukirje"
postilaatikosto[Sievänen]="rakkauskirje"
postilaatikosto[Matti Nykänen]="suksiesite"

ja tulostetaan koko laatikosto:

Koodia: [Valitse]
echo ${postilaatikosto[@]}myyntikirje karhukirje rakkauskirje suksiesite

Pieni mielenkiintoinen yksityiskohta:  Indeksi voi olla monisanainenkin tai aika mielivaltainen merkkijono, tuossa oli indeksinä "Matti Nykänen" eli skanditkin kelpaavat.  En ole tutkinut aiheuttaako välilyönti indeksin nimessä jossain yhteydessä kiusaa.

Tekisikö mieli kurkistaa mitä tapahtuu jos laittaakin numeron nyt indeksiksi ?   Mitä mahtaisi tapahtua ?   No ei oikeastaan mitään hyödyllistä sillä:   Numero 0, 1, 2... ei nyt olekaan numero --- vaan nimi.  Ei löydy mitään tuolta nimillä "0" tai "1".

Koko taulukko tyhjenee:

Koodia: [Valitse]
unset postilaatikosto
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 28.12.14 - klo:20.33
Olen etsinyt tietoa BASH:ista aikaisemmin melkein yksinomaan googlella sillä kun aina sillointällöin olen kokeillut muita hakukoneita en ole saanut mielestäni tyydyttävän monia laadukkaita tuloksia. Mutta puolisen vuotta sitten käytin jonkunverran DuckDuckGo:ta sillä sieltä alkoi tulla tuloksia. Koska gedit höpertää nyt etsin milloin "gedit 3.10.2" tulee Ubuntuun. Muut hakukoneet antoivat tuloksia jopa paremmin kuin google. Missä mennään ?
- muuten tuloaikataulu ei selvinnyt mistään.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 28.12.14 - klo:20.47
Itse käytän "geany" -editoria ohjelmien editointiin jos grafiikka on käytettävissä, muualla "nano" tai "vim". 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 29.12.14 - klo:06.34
Tattista taas kertaalleen. Olen harkinnut ennenkin josko siirtyisi käyttämään jotakin muuta kuin gedit:tiä, mutta olen pelännyt että siirtyminen aiheuttaisi määrättömästi työtä. Mutta osoittautuikin että toimiikin heti kun teki muutaman asetuksen - ja samalla selvisi että geanyssä on paljon sellaista mitä geditissä on kaivannut.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 29.12.14 - klo:12.02
Koska gedit höpertää nyt etsin milloin "gedit 3.10.2" tulee Ubuntuun.

Ubuntu 14.04:ssä on jo gedit 3.10.4: http://packages.ubuntu.com/trusty/gedit
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 29.12.14 - klo:16.03
Jokatapauksessa gedit höpertää ja geany ei. Olen ennenkin valittanut huonoa muistiani ja ilmeisesti olen etsinyt gedit3.12:sta. Jostakin luin että se tuo helpotuksen esimerkiksi siihen etteivät rivinumerot näy jja asetuksetkin alkaa taas toimia.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 29.12.14 - klo:17.13
Jostakin luin että se tuo helpotuksen esimerkiksi siihen etteivät rivinumerot näy jja asetuksetkin alkaa taas toimia.

Oletko asettanut rivinumerot näkyviin? Ainakin 14.04:ssä toimii kuten aina ennenkin, kun kytken numerot päälle: Muokkaa->Asetukset->Näytä, Näytä rivinumerot. Unityssä Muokkaa-valikko on työpöydän yläpaneelissa, jossa sijaitsevat valikot ilmaantuvat näkyviin vain, kun ohjelman ikkuna on valittuna ja viet hiiren osoittimen sinne yläpaneeliin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 06.01.15 - klo:21.37
Kyllä niiden rivinumeroiden näyttäminen on asetettu ja rivinumeroille tuleekin tila muttei ne silti näy. Kansainvälisillä foorumeilla sanottiin etteivät geditin rivinumerot toimi kaikilla teemoilla ihan niinkuin niillä rivinumeroiden ja taustan värit olisivat samat. En kuitenkaan alkanut tapella geditini kanssa koska kohtahan siihen tulee korjaus ihan automaattisesti.

Mutta myös kursori ei oikein näy ja kopiointi on siten melkein mahdotonta ja tekstiä on vaikea korjata.

Ja joskus tulee kummallisia virheviestejä, mutta kehitysversiollahan sen kyllä käsittää.

**

Asiasta toiseen: olen aina ihmetellyt BASH:in huonoa statusta sillä käsittääkseeni Linux nousee pystyyn BASH-skriptien ohjaamien sovellus-ohjelmien avulla. Ja silti mitään ei tehdä vaikka noita ShellShockin kaltaisia aukkoja kuulemma on useampiakin - siis niiden 8:n? jo löytyneen ja paikatun lisäksi. Mikä ihme mättää ? Luuleeko ne että BASH on tarkoitettu ohjelmointiin ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Tomin - 06.01.15 - klo:22.40
Asiasta toiseen: olen aina ihmetellyt BASH:in huonoa statusta sillä käsittääkseeni Linux nousee pystyyn BASH-skriptien ohjaamien sovellus-ohjelmien avulla. Ja silti mitään ei tehdä vaikka noita ShellShockin kaltaisia aukkoja kuulemma on useampiakin - siis niiden 8:n? jo löytyneen ja paikatun lisäksi. Mikä ihme mättää ? Luuleeko ne että BASH on tarkoitettu ohjelmointiin ?

Oletko kuullut systemd:stä? Sehän ei käytä bash-skriptejä, kuten perinteinen init ja on saanut siitä syystä paljon arvostelua.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.01.15 - klo:05.04
Oletko kuullut systemd:stä? Sehän ei käytä bash-skriptejä, kuten perinteinen init ja on saanut siitä syystä paljon arvostelua.

Enpä ollut kuullut, tattista kun kerroit - ja tiedänpä nyt tutkia. Siis toki olin kuullut systemd:stä mutten oletin että se olisi jokseenkin samanlainen kuin vanhatkin.  Tavallaan olen kyllä aavistellut että jotakin tämmöistä on tekeillä sillä tällätavoinhan on aina tehty; kyse ei kai ole niinkään että joku olisi parempi vaan kyse taitaa olla ohjelmoija-sukupolven vaihtamisesta. Eipä silti, kyllä systemd tuntuu ihan hyvältä näin pikaisesti arvostellen. 
- kuinka muuten systemd ja Ubuntu ? Sillä joku distro ei siirry siihen.

**

Yksi BASH:in puutteista luullaan olevan ettei se tunne funktiokutsun tarkennetta elikä kutsumuotoa: a.b  . Mutta itseasiassa BASH:issa on tuo tarkenne jo luonnostaan ja sitä käytetään seuraavasti:
Koodia: [Valitse]
#!/bin/bash
function a () {
  $@
}

function b () {
  echo toimii
}

a b   # jos välttämättä haluaa käyttää kutsussa pistettä täytyy kutsu kirjoittaa: $(echo a.b | tr . " ")

Tapa on hankala ohjelmoida, mutta muissa kielissä se on kielen ohjelmointimäärityksissä yhtä hankala - siis määrityksissä jotka ovat kirjasto-ohjelmissa eikä niitä muilla kielillä tarvitse itse määritellä. Mutta koska BASH:issa ei ole valmis-kirjastoja jää kaikille väärä käsitys ja hankala-käyttöisyys ellei tee itse kirjastoa.
- niinkuin kaikkiin muihinkin ongelmiin löytyy BASH:in käskyistä tähänkin leegio muitakin ratkaisuja.
- maailmalla on moniakin paikkoja joissa on valmiita BASH-kirjastoja. Mutta nuo maailmalta löytyvät kirjastot ovat niin nössöjä ettei niitä kannata käyttää.

Monitarkenteinen kutsu on vielä hankalampi, mutta painotan taas että kaikissa kielissä homma on yhtä hankala jos ei käytetä kirjastoa.  Monitarkenteisen funktion rungon malli on seuraava (siis ennenkuin käskyä voi käyttää täytyy jokaisella tarkennetasolla määritellä oma funktio sen kaikille mahdollisuuksille ja sijoittaa vaadittavat lisäykset oikeaan paikkaan, siis todella iso tehtävä jonka BASH:ia käyttäessään joutuu tekemään itse):
Koodia: [Valitse]
#!/bin/bash
# petteriIII 10.1.1015
function a () {
echo suoritetaan funktiota a jonka jälkeen mennään suorittamaan tarkenteen osaa ${1#.}
[[ ${1%%.*} != ${1#*.} ]] && ${1/./" "} || $1; exit # selväkielellä=>mikäli funktiossa on pisteitä niin poistetaan niistä ensimmäinen ja
                                                            kutsutaan  muodostuneella seuraavaa funktiota ja jos pistettä ei ole on kutsuna itse
                                                            parametri.
}

function b () {
echo suoritetaan funktiota b jonka jälkeen mennään suorittamaan tarkenteen osaa ${1#.}
[[ ${1%%.*} != ${1#*.} ]] && ${1/./" "} || $1; exit
}

function c () {
echo suoritetaan funktiota c, jonka jälkeen mennään suorittamaan tarkenteen osaa ${1#.}
[[ ${1%%.*} != ${1#*.} ]] && ${1/./" "} || $1; exit
}

function d () {
echo suoritetaan funktiota d, jonka jälkeen mennään suorittamaan tarkenteen osaa ${1#.}
[[ ${1%%.*} != ${1#*.} ]] && ${1/./" "} || $1; exit
}
 
a b.c.d # toki kutsu voi olla millainen hyvänsä, esimerkiksi; a d.a

- mukana on myös kutsun esimerkki. Huomioi että se on kovin "epästandardi"
- koko hässäkän suorittaminen kestaa noin 8 ms.
**
BASH on todella epäsopiva ohjelmointiin. Mutta onneksi BASH:illa ei tarvitsekaan ohjelmoida vaan yksinomaan käyttää sitä sovellus-ohjelmien kutsumiseen. Koska BASH noudattaa omaa kieroutunutta logiikkaansa niin mikäli sillä haluaa tehdä tehokkaan skriptin täytyy hyväksyä BASH pomoksi ja itse toimia vain juoksupoikana. Jolloin päädytään siihen että parhaita BASH-koodereita ovat vasta-alkajat ja idiootit.

Myös BASH:in tekstinkäsittelyn huonoutta valitetaan. Mutta ensinnäkin BASH:illa ei käsitellä tekstiä vaan tekstinkäsittelyyn on tarkoitus käyttää siihen tatkoitettuja sovellus-ohjelmia. Toisekseenkin kyllä kaikki tekstinkäsittelyn perus-toiminnot löytyvät BASH:inkin käskyistä mutta lisäksi seuraavankaltaisia muuttujienkin kanssa toimivia "käskyjä" löytyy ziljoonia:

Koodia: [Valitse]
[[ eaaabc =~ aa ]] && echo jees # onko jälkimmäinen edellisen osa.
[[ aaabc =~ ^aa ]] && echo jees # alkaako edellinen jälkimmäisellä
[[ bcaaa =~ aa$ ]] && echo jees # loppuuko edellinen jälkimmäisellä

[[ ! abac =~ aa ]] && echo jees  # eihän vain edellisen sisällä ole jälkimmäistä
[[ ! bcaa =~ aa$ ]] && echo jees # eihän vain edellinen lopu jälkimmäiseen
[[ ! bcaa =~ ^aa ]] && echo jees # eihän vain edellinen ala jälkimmäisellä

[[ $(echo dddaaggggaasss | grep aa.*aa) ]] && echo jees # onko tekstijonossa kirjainryhmää: määrätty teksti[mitä tekstiä hyvänsä]määrätty teksti
[[ $(echo gg aasss | grep -w gg) ]] && echo jees        # onko tekstijonossa gg siten että sen vieressä ei ole aakkonumeroa (siis muita merkkejä voi olla)

echo 'Marker11234567890Marker2' | grep -Po '(?<=Marker1).*(?=Marker2)' # tulostaa tekstin joka on hakuavaimien välissä - jos kumpaakin avainta ei ole niin mitään ei tulostu
echo 'Marker11234567890Marker2' | grep -Po '(?<=Marker1).*'            # tulostaa tekstin joka on hakuavaimen edessä   - jos avainta ei ole niin mitään ei tulostu
echo 'Marker11234567890Marker2' | grep -Po '.*(?=Marker2)'             # tulostaa tekstin joka on hakuavaimen perässä  - jos avainta ei ole niin mitään ei tulostu
**
Aikoinaan aloin etsiä netistä kuinka BASH:illa tarkistetaan ennen matemaattista operaatiota onko lasku-lausekkeessa yhtämonta avaavaa ja sulkevaa sulkua. Kun yhteisö alkoi ratkaista ongelmaa muodosti ratkaisu aluksi kymmeniä rivejä. Ajan mittaann esitettiin jatkuvasti lyhyempiä skriptejä kunnes lopulta yksi tajuton esitti että seuraava mielipuolinen skripti muodostaa vain yhden rivin ja samalla se on nopea:
[[ $(tr -dc '\[' <<< "lauseke" | awk '{ print length; }') -ne $(tr -dc '\]' <<< "lauseke" | awk '{ print length; }') ]] && echo 'sulut epätasapainossa'
**
Yksi toteutus kaksi-ulotteisesta matriisista; tässä vaiheessa kylläkin vain periaatteen toimivuuden varmistus. Tehtävä on käyttökelvottomaksi vanhentunut mutta mielenkiintoinen ja se saattaa johtaa johonkin:

Koodia: [Valitse]
#!/bin/bash
function tulosta_matriisi () {
for n in $(eval echo -e \${$1[*]\n}§); do echo -ne ${n/§/\\n}' '; done | column -t 
}

a=(sin17 .2 3§.4 .5 6e8§ -7 8*8 \$!) # yksi menetelmä kaksiulotteisen matriisin kirjoittamiseksi sillä Linuxissahan ei selaista ole
                                    # siis §:llä merkitään että siirrytään täyttämään seuraavaa riviä. Nuo funktiot ja käskyt ovat mukana
                                    # siksi että näkee voiko niitä liittää mukaan - funktiot ratkaistaan toisaalla.
tulosta_matriisi a

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: tathhu - 07.01.15 - klo:20.09
Moi, olen luonut pienen skriptin, joka symlinkkaa Kuvat-kansion jne kotikansiosta, joka on SSD'llä, normaalille, pyörivälle kiintolevylle. Se on kuitenkin "sidottu" siihen, että käyttiksen/kansioiden pitää olla suomen kielellä. Miten saisin järkevämmin toteutettua sen niin, että symlinkkaus onnistuu, oli kielenä mikä tahansa? Siis niin, että kotikansion "normaali" rakenne ei muutu.

Tällä hetkellä on näin:

ln -s [sijainti]/Kuvat /home/user/Kuvat
jne

E: lisää juttua;
Osaisko joku potkaista oikeaan suuntaan tässä:
Tarkoituksena olisi luoda skripti, josta saa valittua että asennetaan vaikka Libreoffice PPA'sta, päivitetään paketit ja asennetaan suljetut ajurit. Eli suora "lista" josta saa valita tehtäviä juttuja ja ne sitten suoritetaan.. Ei toimi pää yhtään.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 07.01.15 - klo:20.22
Moi, olen luonut pienen skriptin, joka symlinkkaa Kuvat-kansion jne kotikansiosta, joka on SSD'llä, normaalille, pyörivälle kiintolevylle. Se on kuitenkin "sidottu" siihen, että käyttiksen/kansioiden pitää olla suomen kielellä. Miten saisin järkevämmin toteutettua sen niin, että symlinkkaus onnistuu, oli kielenä mikä tahansa? Siis niin, että kotikansion "normaali" rakenne ei muutu.

Hakemistot on määritelty kunkin käyttäjän kotihakemiston tiedostossa ~/.config/user-dirs.dirs:

Koodia: [Valitse]
$ cat ~/.config/user-dirs.dirs

# This file is written by xdg-user-dirs-update
# If you want to change or add directories, just edit the line you're
# interested in. All local changes will be retained on the next run
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
# absolute path. No other format is supported.
#
XDG_DESKTOP_DIR="$HOME/Työpöytä"
XDG_DOWNLOAD_DIR="$HOME/Lataukset"
XDG_TEMPLATES_DIR="$HOME/Mallit"
XDG_PUBLICSHARE_DIR="$HOME/Julkinen"
XDG_DOCUMENTS_DIR="$HOME/Asiakirjat"
XDG_MUSIC_DIR="$HOME/Musiikki"
XDG_PICTURES_DIR="$HOME/Kuvat"
XDG_VIDEOS_DIR="$HOME/

Tiedoston voi ladata skriptin alussa . -komennolla, jolloin muuttujia voi käyttää normaalisti skriptin sisällä:

Koodia: [Valitse]
#!/bin/sh

. "$HOME/.config/user-dirs.dirs"

echo "$XDG_PICTURES_DIR"

Toki voi olla fiksua myös tarkistaa, että muuttujalla on jokin arvo, ja asettaa joku oletushakemisto tai varoittaa käyttäjää:

Koodia: [Valitse]
if [ -z "$XDG_PICTURES_DIR" ]; then
    echo "Warning: XDG_PICTURES_DIR not set, defaulting to $HOME/Pictures"
    XDG_PICTURES_DIR="$HOME/Pictures"
fi
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.01.15 - klo:12.13
BASH:illa ei pidä käsitellä mitään vaan ainoastaan antaa käsiteltävät ulkoisten ohjelmien käsiteltäviksi. Esimerkiksi kaksi seuraavaa lausetta, jotka aikaansaavat aivan saman asian elikä tekstijonon perättäisten samojen merkkien supistamisen yhdeksi:

echo $tekstijono | sed 's/\(.\)\1\+/\1/g'
set -- $(echo $tekstijono | grep -Eo '(*.)\1+'); for n in $@; do tekstijono=$(echo ${tekstijono/$n/${n:0:1}}); done; echo $tekstijono

Ensimmäinen käyttää sed-ohjelmaa ja se on nopea ja luotettavan oloinen. Siinä BASH toimiikin vain sed:in kutsujana.
Toinen pää-asiassa kikkailee BASH:illa ja se on hidas eikä oikein luotettavan oloinen.

- ongelmana on vain se, että vaikka BASH:ia osataankin enää vain jotenkin niin sekä sed:in mutta varsinkin awk:in taitajia ei enää ole ollenkaan ja on pakko tehdä BASH:illa ontuvia skriptejä.
- tiesitkö muuten että awk on paljon-paljon edistyneempi skriptauskieli kuin BASH ?
- hakutermin *. kuuluu tässä tapauksessa olla kirjoitettu noin; kyseessä on Mikrosoft-Linux logiikan ero.
**
BASH tuntee myös "globien-laajennokset", esimerkiksi "rekursiiviset *:n". Ne ovat hitaita eikä niitä kannata käyttää, mutta silti on syytä tuntea ne. Jostain syystä halusin nähdä kuinka homma toimii koska olin unohtanut kaiken muun paitsi että se on koneessani kuvattuna ja että siinä on merkkiyhdistelmä ** . Etsintä täytyi kokeilla ja löytihän sen regex:  \*\\* .
- extglobit ovat seuraavat:
Koodia: [Valitse]

  Extglob           Regex                           Kuvaus toiminnasta
?(lista)          (...|...)?     nolla tai yksi lista
*(lista)          (...|...)*     nolla tai useampia listoja
+(lista)          (...|...)+     yksi tai useampia listoja
@(lista)          (...|...)      täsmälleen yksi lista
!(lista)                         "kaikki muu kelpaa paitsi lista"
**                               "rekursiivinen *"

- merkki | on merkitykseltään tai (OR)
- shopt -s extglob; cat ~/**/*   # listaa koko kotihakemiston kaikki tiedostot
- ajaaskel osasi kertoa että extglob on päätteessä oletuksena päällä, mutta skripteissä ei
**
regex on ahne, se valitsee aina pisimmän mikä sopii ehtoon. Joskus kuitenkin halutaan erottaa erikseen kaikki sopivat. Esimerkki tästä lazy-regex:ästä:
  echo "123 abc 456 def 789" | grep -o -P [0-9].*[a-z]   # tulostaa: 123 abc 456 def
  echo "123 abc 456 def 789" | grep -o -P [0-9].*?[a-z]  # tulostaa kaksi riviä: 123 c   ja: 456 d
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 23.01.15 - klo:12.37
"tr" tekee tuon pienellä vaivalla:

Koodia: [Valitse]
a=12233344455555
 tr -s [[:alnum:]] <<< "$a"
12345

Ei tuo paha ole Bashilläkään:

Koodia: [Valitse]
for ((i=0; i<${#a};i++)); do [[ "$vm" != "${a:i:1}" ]] && echo -n "${a:i:1}";vm="${a:i:1}"; done;echo
12345

Tuossa yllä ajetaan silmukkaa niin että mennään sanan merkit yksitellen läpi. Silmukka alkaa nollasta (joka on ensimmäinen merkki sanassa) ja päättyy kun sanan pituus saavutetaan. Viimeksi käsitelty merkki talletetaan muuttujaan "vm" ja seuraava merkki tulostetaan vain jos se on eri kuin "$vm".  Vertailu on tehty käyttäen
Koodia: [Valitse]
[[jokin_ehto]]  &&
syntaksia.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.01.15 - klo:19.06
Tuo tr ... on kyllä nopea ja elegantti, mutta en saa sitä käsittelemään ääkkösiä.
- erikoismerkkit ja välilyönnit hoituvat kun muuttaa tuon [:alnum:]:in [:print:]:iksi, mutta ääkkösiä sitä ei saa käsittelemään mitenkään.
- hieman tutkittuani asiaa se johtuu jostakin LC_TYPE jutuista, mutta kuulemma se on vielä pahempi kuin IFS ja jos kokeilee sillä saa järjestelmänsä helposti sekaisin.

Sensijaan tuo
Koodia: [Valitse]
for ((i=0; i<${#a};i++)); do [[ "$vm" != "${a:i:1}" ]] && echo -n "${a:i:1}";vm="${a:i:1}"; done;echo
käsittelee kaikkia on vielä nopeakin - riippuen tekstistä joskus nopeampi ja joskus hitaampi. Mutta ei kumpikaan sed:ille pärjää varsinkin kun sed toimii semmoisenaan tiedostoillekin nopeudenkaan kärsimättä.

Tuota nopeuden mittausta ihmettelen kaikkienkin skriptien kanssa; skriptiajuri nimittäin lisää aina ilmanmuuta kaikkiin skripteihin time-käskyn ja  niin sen tuloksiin kiinnittääkin huomiotaan paremmin - kun määrää käsin time:n skriptin ajokäskyn eteen niin se antaa kyllä samat tulokset mutta sitä ei tule käytettyä aina. Tulokset saattavat vaihdella paljonkin kun nopeus on pieni,
esimerkiksi tulos voi vaihdella välillä 5-15 ms. Ja ajettaessa skripti päätteessä ensimmäisen keran on tulos usein paljon hitaampi kuin toistot - ihankuin tulkki oppisi tai jotakin olisi cacheissa ja joskus cachet nollautuisivat omia aikojaan.
- toki niin että sadasta mittauksesta 90 on 5-8 ms ja vain muutama on 12-15 ms.
- muuten ainoastaan tuo sed-versio osaa käsitellä sanan edessä olevia välilyöntejä.
**
Vastaan tuli sellainen ongelma että matriisiin perään piti liittää  toinen matriisi poistaen samalla duplikaatti-rivit. Ratkaisuksi tuli:

Koodia: [Valitse]
#!/bin/bash
# matriisien yhteenliittäminen poistaen duplikaatit petteriIII 4.2.2015
# matriisien määrittelytapa on normaali ja merkkivalikoima on melkein rajoittamaton. Muuten tässä skriptissä on vain kaksi riviä asiaa
# ja loput ovat vain oikeellisuuden tarkastelua.
a[0]="kukkivat/'   kummut" # matriisien arvot annetaan näin jotta alkuasetelma on varmasti yksiselitteinen. Erikoismerkit hyväksytään kun edessä on keno.
a[1]=3
a[2]="pähkäilyä komentorivi-komennoista!"
a[3]=5$
b[0]=1
b[2]="kukkivat/'   kummut"
b[1]=2.57e9
b[2]=3
b[3]="pähkäilyä komentorivi-komennoista!"
b[4]=4
c=("${a[@]}"); c+=("${b[@]}")    # matriisien yhteenliittäminen tapahtuu selväpiirteisimmin lisäämällä matriisi kerrallaan.
c=("$(echo -e "${c[@]/%/\\n}" | awk 'x[$0]++ == 0' )") # duplikaattien poistaminen
echo "${c[0]}"  # tulostetaan yhteen-nivottu matriisi c tällätavalla ettei jää pienintäkään epäilystä että jokin toimii omituisesti
echo "${c[1]}"
echo "${c[2]}"
echo "${c[3]}"
echo "${c[4]}"
echo "${c[5]}"
echo "${c[6]}"
echo "${c[7]}"
**
Tein skriptin nimeltään: tagien välisen tekstin etsiminen tiedostosta . Sen nopeus perustuu awk:iin ja saattaa muut kielet häpeään - ei sikäli että se olisi edes yhtä nopea kuin muut vaaan siksi että se on jo kymmeniä vuosia maannut haudassa ja vieläkin se täytyy ottaa huomioon. Seuraavan haku-skriptin hitaus johtuu melkein kokonaan tiedoston lukemisesta minkä kesto- " aika lienee melko sama kaikilla kielillä. Sed puolestaan tarjoaa helpon muuteltavuuden - seuraavat ominaisuudet saa näillä muutamilla lisillä:

Teksti voi olla yhdessä pötkössä tai siinä saa olla rivinsiirtoja kaikkialla muualla paitsi etsittävissä sano:issa. Siten voidaan etsiä vaikka wget:in tulosteesta jos vain saadaan etsitävät sana:t määriteltyä.

Mikäli teksti on riveissä niin haku-sanait saavat olla samalla tai eri riveillä ja ensimmäisen sana:n edessä saa olla mitävain tai ei mitään samoinkuin toisen sana:n perässä saa olla mitävain tai ei mitään.
Tekstissä saa olla myös rivejä jotka eivät ole haku-sanojen välissä eivätkä ne sekoita olipa niitä kuinka monta vaan ja olipa niillä mitähyvänsä, vaikka tyhjää.
Sekään ei ole virhetilanne että tiedostossa ei ole etsittäviä sanoja ollenkaan.

Tulosteessa on löydettyjen rivien välissä tyhjä rivi. Mikäli samassa löydöksessä on useampi rivi niin niiden välissä ei ole tyhjää riviä jotta näkisi mitkä rivit kuuluvat yhteen. Samassa etsinnässä ryhmiissä riviluku voi siis vaihdella.

Ja ennenkaikkea: merkkivalikoimaa ei ole mitenkään rajoitettu.
Muuten kaikki väittää, ettei awk tunne BASH:in muuttujie ellei niitä sille erikseen esitellä. Mutta hyvin näyttää onnistuvan.

Koodia: [Valitse]
#!/bin/bash
function haku () {
# mikäli tyhjien rivien tulostuminen halutaan estää lisätään "löytöloitsujen" perään: | awk 'NF'
[[ $tag = [KkYy] ]] && < $tiedosto sed "s/$tag1/\n$tag1/g" | awk "/$tag1/,/$tag2/" | sed "s/.*$tag1//g" | sed "s/$tag2.*/\n/g"  ||
< $tiedosto sed "s/$tag1/\n$tag1/g" | awk "/$tag1/,/$tag2/" | sed "s/.*$tag1/$tag1/g" | sed "s/$tag2.*/$tag2\n/g" ; }

[[ -f /tmp/haku ]] && read tiedosto tag1 tag2 tag <<< $(cat /tmp/haku)
read -ep "minkänimisestä tiedostosta etsitään: " -i " $tiedosto" tiedosto
read -ep "mistä sanasta haku alkaa: " -i " $tag1" tag1
read -ep "mihin sanaan haku loppuu: " -i " $tag2" tag2
read -ep "poistetaanko hakusanat tulosteesta: " -i " $tag" tag
echo $tiedosto $tag1 $tag2 $tag > /tmp/haku
echo
haku

**
Aloinpa tehdä testinkäsittely-funktioita. käytännössä senjälkeen kun nuo funktiot ovat koneessasi tuntee koneesi lisää käskyjä, esimerkiksi:
 
1. Sen paikan kertominen jossa teksti-palanen tekstirivillä on. Kutsumuoto:
   etsi_paikka "kolmekymmentä merkkiä edessä_koti_perässämitävaan" koti     

2. Tekstirivillä olevien reaalilukujen|kirjain-jaksojen|erikoismerkki-joukkojen|ip-osoitteiden ... irroittaminen. Kutsumuoto esimerkiksi:
   etsi_luvut  "mvkjngöejgn-2.2e-3 .33lfmbäamg"
   - kirjoittaa luvut näytölle ja myös asettaa ne matriisin peräkkäisten jäsenien arvoiksi.
   - lukujen välissä tulee olla jotakin - välilyöntikin kelpaa.
   - integerit, desimaaliluvut ja järjestysluvut kelpaavat myös.

3. tekstirivillä rajoittimien välissä oleva tekstin tulostaminen. Kutsumuoto:
   etsi_tekstit_tagien_välissä tag1 tag2 tiedostopolku
   - siis se käy läpi koko tiedoston ja antaa joukon tuloksia.
   - kirjoittaa tekstit näytölle ja myös asettaa ne matriisin peräkkäisten jäsenien arvoiksi.
   - tagit saavat olla samalla tai eri riveillä eikä se vaikuta tulostukseen.
   - merkkivalikoimaa ei rajoiteta, jopa kova lainausmerkki kelpaa vaikka yksikseen.
   - etsittävä voi olla joko riveissä tai yhtenä pötkönä, joten vaikka wget-tulosteesta voi etsiä, sillä samalla rivillä voi olla monta tag-paria.
**
Yksi tärkeä ohjelma joka mielestäni on BASAH:ista puuttunut on sellainen haku jossa etsittävää ei täydy määrätä tarkasti vaan esimerkiksi sallien hakusanassa kahden merkin eron. Luuloa se oli, sillä kyllä BASH:issa sellainen on. Nimeltään se on agrep ja Ubuntussa se kuulemma jopa toimii.
**
Vihdoinkin sain selvityksen time-komennon "real/user/sys"-arvoista . Yksinkertaisessa koneessa: real on kokonaisaika, user on aika joka on vietetty user-prosesseissa ja sys on aika joka on vietetty sys-prosesseissa joten silloin: real=user+sys+"aika joka on kulunut odotellessa", noin suurinpiirtein.  Mutta semmoista kuin "yksinkertainen kone" ei olekaan, varsinkin nykyään multi-prosessointi sotkee pahasti, sillä sys-ajaksi lasketaan kaikissa säikeissä kuluneiden aikojen summa. Siten sys saattaa olla ajoista suurinkin. 
- aikojen suhteet kertovat aina vaan vähemmän siitä kannattaako skriptiä yrittää nopeuttaa.
**
Pähkäilin matriisien erilaisuuden tulostavaa skriptiä ja etsiessäni netistä vihjeitä parhaista tavoista kohtasinkin näpsäkän awk-skriptin ja ihmettelin taas kertaalleen senjälkeen että kun tehtävälle on jo löytynyt hyvä ratkaisu esitetään paljon huonompia eikä ole opittu mitään. Mutta toisaalta awk-skriptejä ei oikein käsitä joten tein BASH-toteutuksen jossa on se hyvä puoli että sen toiminnan käsittää kukahyvänsä ja kun siinä ole looppia on se melko kelvollinenkin:
Koodia: [Valitse]
diff <(echo -e ${matriisi1[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) <(echo -e ${matriisi2[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) | grep [\<\>] | tr -d  [\<\>]  | awk '{$1=$1}1'
-  awk '{$1=$1} !x[$0]++'   poistaa etu-välilyönnit ja toistot

Mutta itse asia on se, että jos BASH-skriptissä on looppi-käskyjä on se hidas ja alkeellisesti tehty, sillä BASH käsittelee aina joukkoja joten looppi on sijoitettu jokaisen käskyyn sisälle eikä sitä tarvitse itse erikseen määrätä. Varsinkin awk, sed, grep ja xargs ovat varsinaisia joukonkäsittelijöitä ja niissä on sisäiset loopit.
- no ei se looppien käyttäminen täysin kelvotonta ole sillä looppeja käyttävien skriptitien toiminta on helpompi ymmärtää. On elämäntehtävä oppia skriptaamaan ilman looppeja, minä itse en ainakaan ole oppinut.
**
automaatit olivat tehneet tosisuuren tiedoston, jossa monet rivit ja kappaleet olivat monta kertaa eikä ihminen saanut asioista enää mitään selvää. Duplikaattirivit oli helppo poistaa niinkin että kappaleiden väliin jäi yksi tyhjä rivi. Mutta tämä kasasi tuhansia näennäisesti tyhjiä rivejä tiedoston loppuun; näennäisesti tyhjiä rivejä sillä  riveillä saattoi olla TAB:beja, kontrollimerkkejä ja mitä lie. Mutta kyllä siihenkin ratkaisu löytyi: koko toiminta kuvattuna:
Koodia: [Valitse]
cat ohje | awk '!NF || !x[$0]++' | awk '!NF {if (++n <= 2) print; next}; {n=0;print}'
 
- misiköhän noiden awk-lauseiden nopeus täytyisi laittaa ? Kun ne vievät aikaa tässä tapauksessa -200 millisekuntia.
- yleensäkin skriptien ajankäyttö tuntuu kummalliselta: mitatut ajat vaihtelevat ihmeellisesti ja suuremmat kokonaisuudet tuntuvat toimivan huomattavasti nopeammin kuin osiensa summa. Mutta toisaalta BASH esimerkiksi jakaa prosessorikuormaa eri ytimien kesken ilmanmuuta.
**
BASH:issa on huomattava määrä valmiiksi määriteltyjä funktioita. Selvitin funktion in_array kutsun:
Koodia: [Valitse]
a=(1 "2 3" 4 5); in_array "2 3" "${a[@]}" && echo joo || echo ei

Maailmalta löytyykin samanlaisia toteutuksia ziljoona, mutta itseasiassa skriptissä in_array on lukemattomia puutteita ja päälle päätteeksi looppi. Esimerkiksi seuraava karkea loopiton skripti olisi parempi:
Koodia: [Valitse]
matriisi=(1 glmnämthn 87 2 82 7 89 asd 81); etsittava=8[0-9]; echo -n 'löydöt muodossa: alkio/mitä_siellä_on: '; echo -e ${matriisi[@]/%/\\n} | grep -no $etsittava | xargs echo | tr : /
**
Perättäisten merkkien poistamiseksi tekstistä käsky: tr -s [[:print:]] <<< "$a" ei kelpaa, sillä vaikka se on tehokas niin se ei toimi skandien tai välilyöntien suhteen ja erikoismerkit saavat sen ihan sekaisin. Kaikki muu toimii kyllä käskyllä: cat tiedosto | tr -s [[:print:]] paitsi skandien kanssa se ei toimi sittenkään.

Toimiva toteutus on sed-skripti:
Koodia: [Valitse]
sed 's/\(.\)\1\+/\1/g' ohje
- se on huomattavast hitaampi kuin: tr -s [[:print:]] , vaan kaikki kelpaa: välilyönnit, erikoismerkit, skandit ....
- echo:a pehmeiden lainausmerkkien kanssa, printf:ää tai <<< ei voi käyttää mikäli tekstissä on erikoismerkkejä - tai toimivathan ne kun jokaisen erikoismerkin eteen kirjoitetaan keno.
  Kyllä echo:takin voi mikäli lauseessa ei ole erikoismerkkejä.
**
- googlen regex-haku toimii ainakin jotenkin. Monessa muussakin haussa olen käyttänyt sitä mutta esimerkiksi haku: bash awk \$0=
löysi myös mitä pitikin.
**
itseopiskelu johtaa mielipuolisiin tilanteisiin. Olenko vielä unesta sekaisin kun luulen etten ole tämmöistä tavannut: kun näppäilen koneeseenii:  a=echo; $a c      niin se tulostaa: c

Edelleenkin olen sitä mieltä että tästä ei ole puhuttu missään. Vaikenemisella saattaa olla joku syykin, esimerkiksi se että tuommoinen johtaa erittäin tehokkaisiin itseään muuttaviin ohjelmiin jotka herraties miksi ovat täydellisessä pannassa.
Mutta nyt kun käyttäytyminen selvisi niin selityskin löytyi: BASH-skriptissä voi olla muuttuja missävaan. Kun tulkki kohtaa rakenteen: $muuttuja niin se korvaa rakenteen välittömästi sen arvolla ennenkuin tekee mitään muuta.

Nopeasti pieni esimerkki:
Koodia: [Valitse]
#!/bin/bash
function tehdään_mitä_määrätään () {
$@
}

tehdään_mitä_määrätään ls
read -p "paina return saadaksesi seuraavan tulosteen"
tehdään_mitä_määrätään ls -l
read -p "paina return saadaksesi seuraavan tulosteen"
tehdään_mitä_määrätään sudo blkid | awk '{print $1}' | tr -d : | sed 's,/dev/,,g' # tulosta kovalevyn tunnetut osiot
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 17.03.15 - klo:16.46
Minulla on scripti joka hakee cu:lla USB liitännäisestä palikasta (WatchPort/H) lämpötilan ja ilmankosteuden.
Lämpötila saadaan sanomalla palikalle T ja ilmankosteus sanomalla H.
Scripti toimii täysin lämpötilan kanssa eli T:llä mutta jos vaihdan T:n tilalle H:n eli kyselisin ilmankosteutta en saa minkäänlaista outputtia.
Lueskelin tuota cu:n ohjetta: https://docs.freebsd.org/info/uucp/uucp.info.cu_Commands.html ja siinä on kohta:
" To send an escape character to
the remote system at the start of a line, it must be entered twice.  All
commands are either a single character or a word beginning with `%'
(percent sign)."

Lämpötila, kun on muodossa +29.5625C ja kosteus 38% niin voiko tuo % merkki palikan outputissa sotkea CU:ta? Ja jos niin mitenköhän tuon saisi kierrettyä?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 17.03.15 - klo:19.00
"cu": n dokumenttisi mukaan komennot lähetetään tilde-alkuisina eli ~<jotain>. Koodisi näkeminen saattaisi auttaa, ei tuosta arvaamaan pysty mitään.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 17.03.15 - klo:20.29
"cu": n dokumenttisi mukaan komennot lähetetään tilde-alkuisina eli ~<jotain>. Koodisi näkeminen saattaisi auttaa, ei tuosta arvaamaan pysty mitään.

Tässä tämä koodi:



#!/bin/bash
# # This script reads the temparature value from Watchport/T Temperature sensor
# which is connected to the USB port.
 
# Working on Centos 5 Linux, run by crontab
# Change '\rT\r' (Celcius) to '\rTF\r' to get Fahrenheit
t_name=T1 # Sensor name
t_timesec=5
t_parity=none
t_speed=115200
t_file1=/var/log/watchport1.tmp
logfile=/var/log/watchport1.log
t_USBport=/dev/ttyUSB0
 
if [ ! -f ${logfile} ]; then
 echo "Sensor;Date;Time;Temperature">$logfile
fi
 
t_time=`date +%H:%M`
t_date=`date +%d:%m:%Y`
 
# Remark file is Binary !
(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Ja tuo tosiaan toimii OK mutta jos tuon \rT\r vaihtaa \rH\r niin eipä tule enää logiin mitään (.tmp:n tulee samat connect ja disconnect jutut, kuin T:tä käyttäessä).
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 18.03.15 - klo:08.56
Se kosteus ei varmaan ala plusmerkillä rivin alussa kuten koodisi seuloo "grep": llä ?

(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Kannattaa lisätä tuonne testauksen ajaksi rivi joka tulostaa tilapäisen tiedostosi "$t_file1" sisällön ennen kuin käsittelet sitä millään tavalla jolloin näkee missä menee väärin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 18.03.15 - klo:11.14
Se kosteus ei varmaan ala plusmerkillä rivin alussa kuten koodisi seuloo "grep": llä ?

(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Kannattaa lisätä tuonne testauksen ajaksi rivi joka tulostaa tilapäisen tiedostosi "$t_file1" sisällön ennen kuin käsittelet sitä millään tavalla jolloin näkee missä menee väärin.

Kas... joskus aikaisemminkin tappelin tuon kanssa ja huomasin tuon kohdan mutta nyt oli menny täysin ohi.
Testaan tuota poistaen tuon grep:n tai muuttaen sen hakemaan sen %:n loppuvan rivin. Palailen asiaan jos ongelma ei tällä ratkennut.
Kiitän =)


Edit:
Jep. Tällä korjaantui kiitoksia =)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 21.03.15 - klo:20.09
Kun päätteessä kirjoittaa: text="aaa<skipme>bbb<skipmetoo>ccc"; echo "${text//<*([^>])>/}" niin tulostuu aaabbbccc
Mutta kun kirjoittaa sen skriptiin niin tulostuu: aaa<skipme>bbb<skipmetoo>ccc   niin BASH:illa, DASH:illa kuin SH:llakin.

Mikä on syynä ? Tai onko tämä alkavaa dementiaa ?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 21.03.15 - klo:23.13
Kun päätteessä kirjoittaa: text="aaa<skipme>bbb<skipmetoo>ccc"; echo "${text//<*([^>])>/}" niin tulostuu aaabbbccc
Mutta kun kirjoittaa sen skriptiin niin tulostuu: aaa<skipme>bbb<skipmetoo>ccc   niin BASH:illa, DASH:illa kuin SH:llakin.

Mikä on syynä ? Tai onko tämä alkavaa dementiaa ?

Tuo johtuu siitä että ympäristössä on eroa kun ajetaan skriptiä päätekomennon sijaan eli "extglob" ei ole oletuksena päällä kun ajat skriptiä vaikka se suoraan komentoriviä käytettäessä on.

Voit korjata ongelman lisäämällä skriptisi alkuun

Koodia: [Valitse]
shopt -s extglob
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.03.15 - klo:04.58

Tuo johtuu siitä että ympäristössä on eroa kun ajetaan skriptiä päätekomennon sijaan eli "extglob" ei ole oletuksena päällä kun ajat skriptiä vaikka se suoraan komentoriviä käytettäessä on.

Voit korjata ongelman lisäämällä skriptisi alkuun
Koodia: [Valitse]
shopt -s extglob

Tattista taas kertaalleen. Kyllä on todella kiva että foorumilla on virtuooseja joilta saa vastauksen tämmöiseenkin.
- taas sain uutta päätteen käyttöön: päätteessä voi antaa käskyn: ls ~/**/* ja se listaa koko kotikansion
**
Mutta edelinen konsti ei toimi kun "tag" merkit ovat erilaisia, esimerkiksi << .... >> mutta tässä skriptissä mitkä hyvänsä käyvät kunhan ne vain kirjoittaa skriptiin.
Välilyönnit ovat sallittuja myös tagien välissä.
- eihän tag:ien lukumäärä ja muutkaan oletukset eivät ole mistään kotoisin. Mutta niiden huomioiminen merkitsee vain muutamia koodi-rivejä lisää.
- liian aikaista kokeilla wget:iä.

Koodia: [Valitse]
#!/bin/bash
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
kaikkien_tagien_tekstit=($(echo $teksti | grep -Po \<\<.*?\>\>))
ekan_tagin_teksti=$(echo ${kaikkien_tagien_tekstit[@]} | awk -F' <<' '{print $1}')
tokan_tagin_teksti=$(echo ${kaikkien_tagien_tekstit[@]} | awk -F' <<' '{print $2}')
echo "$teksti" | sed "s/$ekan_tagin_teksti//g" | sed "s/<<$tokan_tagin_teksti//g"
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 22.03.15 - klo:21.49
En ole aivan varma tarkoititko tätä:

Koodia: [Valitse]
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
sed 's/<<[^>]*>>//g' <<< "$teksti"
a a abbbc c c

eli halusit edelleen tiputtaa pois tuplaväkästen rajaaman tekstin ? Et tosin varsinaisesti kysynyt mitään --- mutta vastasin nyt kuitenkin kun skriptausjutut ovat hauskoja :)

Selitys:
Tuossa on käytetty "sed" -komennon (sed = string editor) "etsi ja korvaa" muotoa ja kauttaviivat osoittavat minne se etsitty ja korvaava teksti rajautuu.  Etsimme yhdistelmää joka alkaa kahdella väkäsellä vasemmalle "<<", sitten mitä hyvänsä merkkejä kunhan ei ole oikeaväkänen "[^>]", niitä voi olla peräkkäin miten monta vain "*" paitsi että pitää päättyä kahteen väkäseen oikealle ">>". Jos tuosta tulee osuma niin se korvataan "ei millään" eli kauttaviivojen välissä ei ole mitään "//".  Korvataan "ei millään" = poistetaan. 
Komennon alussa oleva "s" tarkoittaa "separator" eli erotinmerkki, ässän jälkeen heti tuleva merkki eli tässä tapauksessa kauttaviiva erottaa kenttiä kuten etsittävä ja korvattava.  Voisimme halutessamme käyttää jotain muutakin merkkiä sen tilalla:

Koodia: [Valitse]
sed 's|<<[^>]*>>||g' <<< "$teksti"
a a abbbc c c

Tuo "g" lopussa tarkoittaa että ei luovuteta ensimmäiseen osuman jälkeen vaan toistetaan samaa korvausta jos uusi osuma löytyy (kuten tuossa esimerkissä kävikin).

Aivan lopuksi, noilla kolmella väkäsellä vasemmalle voimme lähettää muuttujan sisällön "sed": lle melkein samalla tavalla kuin echo "$teksti " | sed ....
Sanoin "melkein" koska tätä pidetään joskus parempana tapana.   Erot putkituksella "|" ja "HERE string" tavalla tehdä asia ovat:
1) Putkitus avaa uuden ympäriston missä ajetaan (subshell).  Saat vietyä muuttujien sisällön putkeen mutta takaisin et saa muuttujan kautta mitään eli et pysty mitenkään putken aikana muuttamaan niiden muuttujien sisältöä jotka olivat olemassa ennen putken ajoa, muutosta ei välitetä.
2) Ainut rajoitus mikä tulee mieleen HERE stringin kanssa on se että lopun LF merkistä (hexa 0a) ei pääse eroon eli "<<<" lisää aina loppuun LF merkin, vertaa:

Koodia: [Valitse]
hexdump -C <<< 'a'
00000000  61 0a                                             |a.|
00000002

echo -n 'a' | hexdump -C
00000000  61                                                |a|
00000001

http://unix.stackexchange.com/questions/20157/why-does-a-bash-here-string-add-a-trailing-newline-char (http://unix.stackexchange.com/questions/20157/why-does-a-bash-here-string-add-a-trailing-newline-char)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.03.15 - klo:08.55
En ole aivan varma tarkoititko tätä:
Koodia: [Valitse]
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
sed 's/<<[^>]*>>//g' <<< "$teksti"

- justiinsa tuota tarkoitin. Lisäksi homma on sopii useampaankin tapaukseen pienin muutoksin. Oppirahat on kalliit. Tattista taas
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 23.03.15 - klo:09.58
Lainaus
- justiinsa tuota tarkoitin.

Laitoin hieman selitystä tuonne vastaukseeni niin siitä on hyötyä muillekin asiasta kiinnostuneille.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.03.15 - klo:11.21
Seuraavaksi kiinnostuin kuinka tuosta samasta lauseesta saa pelkästään nuo väkästen väliset tekstit. Mutta koska sed:issä ei ole negaatiota niin en keksinyt kuinka sen saisi sed:illä tehtyä - kuulemma se onnistuu kyllä jotenkin takaisinpäin-viittauksen avulla ja varmaankin se onnistuu jotenkin toisinkin. Mutta grep:illä se on:
Koodia: [Valitse]
grep -Po \<\<.*?\>\> <<< "aaa<<jotain tekstiä>>bbb<<jotain toista tekstiä>>ccc"

- noitten grepin tag:ien väsäämisessä joutuu käyttämään kenoja
- jos haluaa tulosteet samalle riville niin  kirjoitetaan:
(grep -Po \<\<.*?\>\> | tr '\n' ' ' ; echo) <<< "aaa<<jotain tekstiä>>bbb<<jotain toista tekstiä>>ccc"'
- grepin haussa oleva ? muuttaa grepin käyttäytymisen: normaalisti haku on "greedy" elikä suurin mahdollinen, ja ? tekeekin hausta "lazy":n elikä ensimmäinen tulos kelpaa (=PCRE ?). Oikeastaan samaan liittyy tuo parametri -o joka käskee tulostamaan pelkästään hakutuloksen ja perään tulee rivinsiirto sillä kun haku jatkuu heti löydön perästä uudestaan niin seuraava löytö tulostetaan eri riville.
**
Aina uutta ja ihmeellistä. Mikäli << ja >> voivat olla tiedoston eri riveillä niin käskyksi tuli:
Koodia: [Valitse]
cat tiedosto | sed 's/<</\n<</g' | sed 's/>>/>>\n/g' | sed -e '/<</,/>>/d'
- kyllä se samalla rivilläkin olevat poistaa.
- noiden:  | sed 's/<</\n<</g' | sed 's/>>/>>\n/g'   tarkoitus on säilyttää: << edessä-  ja: >> perässä-oleva teksti.
 


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.03.15 - klo:21.03
Olenko oikeassa siinä että jos skripti on BASH:issa niin BASH:in tulkitessa se ei välitä ollenkaan siitä puhutaanko tukittavassa sed:istä, awk:ista vai jostain muusta vaan suoritaa ainoastaan omaa koodiaan, esimerkiksi kohdatessaan ilmaisun: $muuttujan_nimi se kirjoittaa sen tilalle muuttujan arvon - ja tämäntakia sed:in jne. kuvaus suljetaan kovien lainausmerkkien sisään ettei BASH muuttaisi mitään.

siten vaihdettaessa lainausmerkit pehmeiksi käsky:
Koodia: [Valitse]
a=1; b=2; echo 1 | sed "s/$a/$b/g"

tulostaa 2. Sellaisen väitteen olen lukenut että vaikka pehmeät lainausmerkit toimivat useimmiten nekin niin lainausmerkkejä ei pitäisi muuttaa pehmeiksi ilman pätevää syytä. Mutta mitään väitteen perusteita ei kerrottu.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 25.03.15 - klo:21.36
Ei siinä asiassa ole muuta juonta kuin että osalle merkeistä on annettu erikoismerkitys.  Jos käytetään pehmeää lainaista (tavalliset lainausmerkit) niin kysessä on osittainen suojaus ja kova lainaus (yksinäiset hipsukat) puolestaan on täysi suojaus eli merkkiä ei tulkita miksikään muuksi kuin mitä se on.

Esimerkki:

Koodia: [Valitse]
teksti="kissa"

echo "$teksti"
kissa

echo '$teksti'
$teksti

eli taalan (tai minkä tahansa merkin) erityismerkitys estetään kovalla lainauksella.  Tästä ja muista tilanteista on tarkemmin vaikkapa täällä:

http://wiki.bash-hackers.org/syntax/quoting (http://wiki.bash-hackers.org/syntax/quoting)

Pehmeä lainaus estää koodiasi menemästä rikki jos muuttujalle annettu arvo sisältää välilyöntejä mutta kova lainaus muuttaisi jo merkityksen kuten yllä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 30.03.15 - klo:06.49
Tämäpä selvä.
**
AWK-skriptien tekemiseen löytyy ohjeita vielä vähemmän kuin BASH-skripteille vaikka se onkin paljon parempi skriptaus-kieli. Awk:ia voi kuitenkin käyttää BASH:ista. Mutta Ubuntu käyttää awk:in versiota nimeltään mawk ja sillä on yksi suuri puute: se ei tunne "inplace" editointia; sellaista niinkuin esimerkiksi sed. Gawk:in saa asennettua  käskyllä: sudo apt-get install gawk . Inplace editointi ei kuitenkaan toimi ihan samoin kuin sed:issä vaan esimerkiksi: 
gawk -i inplace '{print $1}' tiedosto .
Tai jos haluaa tehdä ensin bakupin niin: gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' tiedosto
**
Awk on tosiaan loistava. Esimerkiksi ohjelma joka poistaa tiedostosta duplikaatti-rivit on: awk '!x[$0]++'. Siinä on siis koko nopea ohjelma joka käsittelee koko tiedoston, olipa se vaikka giga-kokoa. Tottamaar awk:illa on puutteita - eihän muuta voi odottaakaan iki-vanhalta skriptauskieleltä jota ei päivitetä juuri koskaan. Mutta parannuksia toki löytyy, esimerkiksi:
awk '{$1=$1} !x[$0]++' jossa rivien alussa tai lopussa olevat välilyönnit eivät vaikuta. Tai:
awk '!NF || !x[$0]++' joka jättää tyhjät rivit rauhaan.

- $0 on koko rivi. Sen paikalle voidaan laittaa myös kentän numero tai kenttälista; esimerkiksi: awk '!x[$2,$3]++'
- BASH:in uniq ei kelpaa, sillä se edellyttää sorttausta.
- tuon awk '!x[$0]++' toiminnan selostus lienee yksinkertainen: awk:in kaikki matriisit ovat assosiatiivisia elikä niiden osoite voi olla tekstiä. Tassä assosiatiivisen matriisin x jäseneksi jonka osoite on tekstirivi yritetään asettaa jotakin. Jos asettaminen onnistuu niin jäsen tulostetaan (koskei toimenpide-kenttää ole niin suoritetaan oletus-toiminne joka on print) ja siirrytään seuraavaan tekstiriviin - ja jos asetus ei onnistu koska sillä osoitteella on jo jäsen niin siirrytään vain seuraavaan tekstiriviin tulostamatta mitään.
**
Olen tässä ihmetellyt regex:än osumien lukumäärää rajoittavia määreitä, esimerkiksi a{2\} jonka pitäisi rajoittaa a-kirjaimien maksimimäärän kahteen. Mutta esimerkiksi:
echo aaaa | egrep a{2\} ilmoittaa että löytyihän niitä a-kirjaimia. Mutta kyse taitaakin olla siitä, että regex:n omalta kannaltaan se tekee täsmälleen sen minkä lupaakin; se on vain minun tulkintani ettei neljää a:ta saa olla. Mielestäni tämä selviää kun egrep-käskyyn lisää määreen -o : echo aaaa | egrep -o a{2\}   
joka tulostaa kahdelle riville aa; siis regex hakee merkkijonoa aa ja kun löytää sen niin tulostaa sen ja jatkaa hakua ja kun löytää taas niin tulostaa sen ja jatkaa hakua ja kun ...

- osoittautui että on tosivaikeaa tehdä regex joka tarkistaa että "grepattavassa" on esimerkiksi täsmälleen kaksi perättäistä samaa merkkiä ja kolme tai enemmän hylätään. Mutta seuraava viritelmä toimii:
echo abc       däägghijklmnopq***r   söööuvvwxyyyz | grep -Eo '(.)\1{1,}' | sed -rn '/(.)\1{2,}/!p'
- Mitkä merkit hyvänsä kelpaavat, ainoastaan regex:än numeroita muutetaan eivätkä toiset merkkiryhmät sotke.
- välilyönnit eivät sotke toiminta. Tosin niitä ei kyllä näytetäkään.
- tyypiltään: grep  "\<[0-9]\{5\}\>" kelpaavat vain silloin kun etsityn luvun molemmin puolin on välilyönti tai tekstijonon alku tai loppu.
**
Olen muutamat päivät tutkinut sed:iä ja awk:ia ja on osoittautunut että kumpikin osaa oikein määriteltynä tehdä mitä vaan, mutta että kaikki on todella liukasta käsiteltävää.
**
taas kertaalleen vastaan tuli vanhojen kernelien poistaminen, ja se tuntuu olevan jatkuvasti muutteleva ikuisuusongelma sillä synapticilla niitä ei viitsi poistaa vaan yhdellä kootulla käskyllä.
- käyttämäni käskyn: sudo apt-get purge $(dpkg -l linux-{image,headers}-"[0-9]*" | awk '/ii/{print $2}' | grep -ve "$(uname -r | sed -r 's/-[a-z]+//')")   pitäisi toimia myös silloin kun kone on niin täynnä vanhoja kerneleitä että se menee tukkoon. Päätteeseen pääsy ja käskyn kopiointi jostain ovat kylläkin ongelmia, mutta ratkaistavissa ne ovat. Kaikki nämä järjestelmää "korjaavat" käskyt muuten ovat myös ongelma sillä ne korjaavat joskus koneen boottaus-kelvottomaksi.   
- onko tosiaan mahdollista ettei tähän turhien kerneleiden ongelmaan ole aina toimivaa ratkaisua ? Sillä voiko olla mitään muuta syytä ettei sitä ole lisätty recoveryn vaihtoehtoihin ? Tai synapticiin ? Tai peräti boottaukseen siten että vain uusin ja sen edellinen jätetään ?
**
hakukoneet ontuu. Kun nimittäin etsin tietoa operaattorista nimeltään ? (if...then...else-rakenne jossa if:iä ei kirjoiteta olenkaan, ? on then ja : on else) sain etsiä ilman että oikeat osumat häiritsivät. Kunnes sitten tajusin etsiä operaattoria nimeltään ternary. Yksi esimerkki lopputuloksesta:
Koodia: [Valitse]
a=1; (( a ? b=1 : (b=2) )); echo $bsen sanallinen kuvaus: jos muuttujalla a on arvo niin b saa arvon 1 ja jos a:lla ei ole arvoa niin b saa arvon 2. Mutta awk:illa se on mukavampi käyttää:
Koodia: [Valitse]
echo "1 2" | awk '{print ($1 < $2 ? "kakkularatsis" : "ja hilipatahoi")}'sillä se pystyy palauttamaan myös tieteellisen esitysmuodon tai tekstijonon samoinkuin vertailemaan niitä.

Käytännön ongelma: tiedoston kentässä 2 voi olla "0 tai 1" taikka "tosi tai epätosi". Alunperin kentässä on joko 1 tai 0 ja ne halutaan muuttaa arvoiksi tosi tai epätosi.
Ratkaisuun on helpointa tehdä awk-skripti (tämä on vain toimintaperiaatteen kuvaus):
Koodia: [Valitse]
awk '{$2=$2=="1" ? "tosi" : "epätosi"}{print $0}'
Mikäli tämmöinen ehto täytyy koodata BASH:illa kannattaa tehdä se näin:
Koodia: [Valitse]
[[ vertailu ]] && ... || ... **
Tiedostojen erojen tulostus on myös yksi ikuisuus-ongelma jolle ei sikäli kunnollista ratkaisua voi ollakaan koska jo sen määritteleminen on vaikeaa mitä eroavaisuudella tarkoittaa ja on usein mahdotonta tehdä ohjelma joka määritykset toteuttaa. Kuvittelisin että ainakin seuraavat ominaisuudet ohjelmalta tulee löytyä:
1. Tyhjät rivit eivät saa sotkea mitään, siis rivi-riviltä vertaaminen ei kelpaa.
2. Mikäli tiedostoa editoidaan siten, että jokin tekstinosa siirretään toiseen paikkaan ei se saa aiheuttaa eroa vertailussa.
3. Tiedostojen eroavaisuus tulee esittää esittää jomman-kumman tiedoston kannalta.

Nämä vaatimukset täyttää awk-skripti joka kertoo mitä tiedostossa1 on sellaista mitä ei löydy tiedostosta2 :
Koodia: [Valitse]
awk 'NR == FNR{a[$0];next} !($0 in a)' tiedosto1 tiedosto2 - skripti on hyvä runko josta lähteä tekemään lisä-määrittelyjä.
- tällätavoin awk:issa käsitellään kahden tiedoston ongelmia muutoinkin: esimerkiksi siirretään tietoja tiedostojen välillä, tarkistetaan ovatko uuteen tiedostoon kerätyt tiedot samansuuntaisia kuin päätiedostossa ...
- menetelmän periaatetta voi soveltaa vaikka kuinkamonen tiedoston nopeaan käsittelyyn.
**
Erilaisuutta määriteltäessä itse tekstirivin asia on joskus sama, mutta tekstirivin eteen tai perään on lisätty välilyöntejä ja tällöin hälytyksen erilaisuudesta soisi jäävän pois, sillä turhia hälytyksiä tulee muutenkin liikaa. awk:in menetelmä etu- ja jälkivälilyöntien poistamiseksi on käsky $1=$1 jolloin koko käskystä tulee:
Koodia: [Valitse]
awk 'NR == FNR{$1=$1;a[$0];next} {$1=$1}!($0 in a)' tiedosto1 tiedosto2**
Aamupäivän päivitys sotki ääkkös-käyttäytymisen. Iltapäivän  päivitys taisi palauttaa sen toimivaksi.
- muutenkin toiminta muuttui aavistuksen verran. Aivan kuin expansio-sääntöjä olisi viilattu.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 21.04.15 - klo:12.02
Olen äimänkäkenä ja joudun taas kysymään neuvoa: kohtasin verkkosivun joka väitti koodinsa olevan BASH:ia. Esimerkiksi yksi koodinpätkä oli tällainen:
Koodia: [Valitse]
function stack_destroy
{
    : ${1?'Missing stack name'}
    eval "unset _stack_$1 _stack_$1_i"
    return 0
}

Kysymys: mikä virka on tuolla kaksoispisteellä rivillä: 
: ${1?'Missing stack name'}

- kasasin skriptin ja koetin. Ilman kaksoispistettäkin toimi, mutta jonkinsortin virheen se silloin tekee. En siis edelleenkään tiedä kaksoispisteen merkitystä, mutta senverran kuitenkin että se on tarpeellinen.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 21.04.15 - klo:15.42
Olen äimänkäkenä ja joudun taas kysymään neuvoa: kohtasin verkkosivun joka väitti koodinsa olevan BASH:ia. Esimerkiksi yksi koodinpätkä oli tällainen:
Koodia: [Valitse]
function stack_destroy
{
    : ${1?'Missing stack name'}
    eval "unset _stack_$1 _stack_$1_i"
    return 0
}

Kysymys: mikä virka on tuolla kaksoispisteellä rivillä: 
: ${1?'Missing stack name'}

- kasasin skriptin ja koetin. Ilman kaksoispistettäkin toimi, mutta jonkinsortin virheen se silloin tekee. En siis edelleenkään tiedä kaksoispisteen merkitystä, mutta senverran kuitenkin että se on tarpeellinen.

Tuo kaksoispiste saa aikaan liki saman kuin  ohjaus --> /dev/null, lyhyempi kirjoittaa  mutta se ei liene suositeltava tapa jos tavaraa tulee paljon.  Tuolla on lisää siitä:

http://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin (http://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin)

Edit: Tässä käytetty eri tarkoituksella, tarkemmin seuraavassa kommentissani tuolla alempana.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 21.04.15 - klo:18.20
Tattista taas kertaalleen. Kestää kyllä pitkään ennenkuin nuo kaikki tajuaa edes jotenkin. Toisaalta masentavaa: en edes tiedä mistä kaikesta en tiedä mitään. Mutta yritetään ...kelataan kauan ... tämähän sanotaan jo antamasi verkkosivun alkuperäisesä viestissä ja senmukaan "selvennetty" selvitys taitaa olla:

: on käsky olla tekemättä mitään ja ${1?'Missing stack name'} on sen parametri

käskyä toteutettaessa BASH suorittaa ensin normaalin $1:n arvon määrityksen. Mikäli $1:llä on jokin arvo ei siis tapahdu mitään, mutta mikäli $1:llä ei ole arvoa niin näytetään oletusarvo joka tässä on: Missing stack name
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: ajaaskel - 22.04.15 - klo:09.55
Kiitos "petteriIII", kommenttisi sai minut pysähtymään hetkeksi ja miettimään lisää.  Kaksoispiste yleisesti merkitsee NOP komentoa joka palauttaa aina exit-arvon 0 (true). Toiminta on hämärämpi kun sitä käytetään komentona parametrin kanssa ja sitä voi myös erikoisella (mutta aika rumalla) tavalla käyttää oikotienä koodin katkaisuun ajamalla tulkki virheeseen tahallisesti.

Lainaus
  : ${1?'Missing stack name'}

Tuon koodin pätkän merkitys on:  Jos parametri yksi on jäänyt antamatta ajetaan bash-tulkki virheeseen tuolla rivillä jolloin myös kaikki koodin suoritus loppuu siihen. Tuo syntaksi on kuvattu täällä:

http://www.tldp.org/LDP/abs/html/abs-guide.html#DEFPARAM (http://www.tldp.org/LDP/abs/html/abs-guide.html#DEFPARAM)     
Lainaus
${parameter?err_msg}, ${parameter:?err_msg}

    If parameter set, use it, else print err_msg and abort the script with an exit status of 1.

    Both forms nearly equivalent. The : makes a difference only when parameter has been declared and is null, as above.
mutta en pidä suositeltavana tapana, virheet pitää mieluummin ottaa hallitusti kiinni sen sijaan että kaadetaan koodi tulkin virheeseen.  Toiminnan oivaltamista hämärtää juuri tuo ruma toteutus jota ei ensimmäisenä osaa ajatella.

Bash-ilmaisutapojen moninaisuus ja eri merkitykset tilannekohtaisesti on haastava asia. Vie pitkähkön ajan muilla kielillä ohjelmoineeltakin oppia jollakin tavalla kattavasti eri ilmaisutavat muunnoksineen ja vielä enemmän mikä on hyvä lähestymistapa/rakenne jonkin asian ratkaisuun.   Silti se on hyvin mielenkiintoinen asia.     
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 04.05.15 - klo:18.49
BASH taipuu apu-ohjelmineen kummallisuuksiinkin, vaikka käskyjen muodostaminen on yleensä monivaiheisempaa kuin kehittyneemmillä kielillä. Esimerkiksi kun haluaa erottaa tekstijonosta tai tiedostosta siinä olevat kokonaisluvut, desimaaliluvut ja eksponenttiesitykset etumerkkeineen niin se onnistuu käskyllä:
Koodia: [Valitse]
| sed 's/[e.+-].[^[:digit:]]//g' | grep -o '[e.0-9+-]'*

esimerkiksi: echo 'o+oo-1234567ddd h.--[-- e"&<<-333.34e-444>>' | sed 's/[e.+-].[^[:digit:]]//g' | grep -o '[e.0-9+-]'*
- tai mikäli halutaan poistaa kaikki numerot: | sed 's/[e.+-].[[:digit:]]//g' | sed 's/[0-9]//g'
- luvut voi kyllä erottaa matriisiinkin, mutta erottamisen periaate selviää paremmin edellisestä
- siis ensiksi täytyy määritellä onko merkeillä: .+-e aakkosellinen vai desimaalinen merkitys

Hieman tähän samaan kuuluu: positive-negative-"look-ahead"-"look-behind":
Koodia: [Valitse]
| grep -Po 'tätä_haetaan(?=.*tämän_täytyy_olla_perässä_mutta_tätä_ei_valita)'  # haku- ja kelpuutus-sanojen välissä saa olla tekstiä
| grep -Po 'tätä_haetaan(?!tämä_ei_saa_olla_perässä)'                          # haku- ja esto-sanojen välissä ei saa olla tekstiä
| grep -Po '(?<=tämän_täytyy_olla_edessä_mutta_tätä_ei_valita)tätä_haetaan'    # haku- ja kelpuutus-sanojen välissä ei saa olla tekstiä
| grep -Po 'tätä_haetaan(?<!tämä_ei_saa_olla_edessä)'                          # haku- ja esto-sanojen välissä saa olla tekstiä
| grep -Po '(?<=teksti_edessä).*(?=teksti_perässä)'                            # tulostaa tekstin joka on hakuavaimien välissä. Myös: grep -Po '(?<=:).*(?=:)' onnistuu
| grep -o teksti_edessä.*tekstiperässä                                         # sama kuin edellinen, mutta teksti_edessä ja teksti_perässä kirjoitetaan myös

# oikeastaan haettavat voivat olla aivan mitä hyvänsä regexiä kaikissa noissa viidessä, joten voidaan kirjoittaa myös:
echo "AlvlökmgkrmgoerrkgkertgkeåthåäpthkB" |  grep -Po '(?<=^[A-Z]).*(?=[A-Z]$)'  # käsky sanoin kerrottuna: jos rivi alkaa ja loppuu suurella kirjaimella niin tulosta teksti noiden suurten kirjainten välistä

# tai voidaan etsiä verkkosivun tag:ien välistä ( tag=<< ja >> ):
echo "a c <<lvlökmgkrmgoerrkgkertgkeåthåäpthk>>d fb" |  grep -Po '(?<=<<).*(?=>>)'

# tai esimerkiksi hakea forecan sivuilta sääennuste; tässä rajoitetussa esimerkissä kymmenen päivän ennustuksen ylä-lämpötilat:
rm -f ~/tenday; wget http://www.foreca.fi/Finland/Helsinki/tenday; cat ~/tenday | grep -Po '(?<=abbr title="Vuorokauden ylin lämpötila">Ylin: <strong>).[0-9]'
- kun noilta verkkosivuilta tietoa hakevaa skriptiä muotoilee kannattaa kun hakee oikeaa paikka tehdä se editorissa; esim: rm -f ~/tenday; wget http://www.foreca.fi/Finland/Helsinki/tenday; gedit ~/tenday
**
tulosta kolmemerkkiset lohkot joiden kaikki merkit kuuluvat joukkoon [abc] ( määrä on siis {3}+1 )
Koodia: [Valitse]
echo jjjjaacaahhhhhbbbbbbbbhh | grep -P '([abc])(?1){3}'
# esimerkiksi tästä voi kehittää:
tulosta lohkot jotka muodostavat kolmesta perättäisestä a:sta tai neljästä perättäisestä joukon [bcd] jäsenestä
echo jjjjaaccachhhhhbccbbbbhh | grep -P '([a]{3}|[bcd])(?1){3}'
tulosta teksti joka on ensimmäisen ja viimeisen numeron välissä - mutta tekstissä ei saa olla erikoismerkkejä
Koodia: [Valitse]
echo jjhblbk17x7yhHYH17hjjhjhblhblb | grep -P '(?=(\d(?U)))\w+\1'
mikä muu e:n sisältävä ei kelpaa paitsi sellainen jossa e:tä seuraa 1
Koodia: [Valitse]
echo koe | grep -P '.*e(?!1)'
tulosta samoista numeroista muodostuneet numerosarjat (tai [a-z] tai ....):
Koodia: [Valitse]
echo gjjhbljhb555567jbjhbjhb | grep -P '([0-9])\1+'

Koodia: [Valitse]
echo "23:s" | grep -P '23(:s)?'         # valitsee: 23:s koska regex on tilassa greedy
echo "23:s" | grep -P '23(:s)??'        # valitsee: 23   koska regex on tilassa lazy
echo "23:s" | grep -P '(?U)23(:s)?'     # valitsee: 23   koska regex on tilassa lazy

echo "<EM>first</EM>" | grep -P '<.+?>  # kysymysmerkki tekee lazy:n elikä valitsee vain molemmat <EM> ja: <\EM>

echo adhd | grep -P 'h\Kd'              # tulosta h:n jälkeinen d
echo adhd | grep -P '(?<=h)d'           # tulosta h:n jälkeinen d
echo adhd | grep -P 'h\K\w+'            # tulosta h:n jälkeinen teksti       
**
- seuraava erottaa lohkon joka alkaa begin ja loppuu end. Mutta ennenkaikkea tästä ilmenee kuinka regex:lle annetaaan ohjeita (?...-ryhmissä :
i=älä välitä merkkikoosta, s=begin ja end voivat olla eri riveillä, m=merkin:^ käyttö sallitaan lukitsemaan begin rivin alkuun; joskus ^ kannattaa jättää pois
Koodia: [Valitse]
echo 'begin12345  67890endtyey kkk' | grep -Poz  '(?ism:^BEGIN.*?END)'
-tai BEGIN-END lohkojen erottaminen tiedostosta:
Koodia: [Valitse]
cat -e /boot/grub/grub.cfg  | grep -Poz  '(?ism:BEGIN.*?END)' | sed 's/END/&\n/g'

- toinen esimerkki ohjeiden antamisesta regex:ille:
Koodia: [Valitse]
echo AAA | grep -Po '(?i)a*'
**
Usein valitetaan että kirjoittaessaan BASH:ista osaavat ihmiset kirjoittavat toisille osaaville eivätkä välitä siitä ettei silläkeinoin koskaan kasva uutta sukupolvea BASH-koodaajia.
Syytöksessä on vähäsen tottakin, mutta toisaalta yli-dokumentoitu skripti on inhottava - täysin dokumentoimatonkin skripti on yleensä parempi. Mutta esimerkiksi seuraavankaltaisista esitetään usein pelkkä funktio eikä kerrota kuinka funktiota käytetään, mikä tekee funktiosta monille käyttökelvottoman. Vähäisen käyttö-opastuksen lisäksi funktiota kannattaa myös dokumentoida hieman:
Koodia: [Valitse]
#!/bin/bash
function verifyIP() { # Muodostetaan muotti jossa IP:t tehdään, tarkistetaan onko tämäkin IP tehty sillä ja palautetaan 0 mikäli IP oli tehty sillä muotilla ja muuten palautetaan 1 
local o='(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])'; [[ $1 =~ ^$o\.$o\.$o\.$o$ ]]; }

TarkistettavaIP=1.2.333.25; verifyIP $TarkistettavaIP && echo kelvollinen ip || echo kelvoton ip    # esimerkki kuinka funktiota kutsutaan

tai voidaan käyttää edellistä funktiota toisessa funktiossa ja kutsua kokonaisuutta seuraavasti:
Koodia: [Valitse]
#!/bin/bash
function verifyIP() {  # Muodostetaan muotti jossa IP:t tehdään, tarkistetaan onko tämäkin IP tehty sillä ja palautetaan 0 mikäli IP oli tehty sillä muotilla ja muuten palautetaan 1   
local o='(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])'; [[ $1 =~ ^$o\.$o\.$o\.$o$ ]]; }

function summaryIP() { # funktio kertoo osoitetussa tiedostossa olevat IP-osoitteet ja jokaisesta osoitteesta montako kertaa se on tiedostossa
while read ip x; do verifyIP $ip && echo $ip; done < "$1" | sort | uniq -c | sort -rn; }

summaryIP ~/IPlista    # IPlista-nimisen tiedoston jokaisella rivillä on IPosoite ensimmäisessä kentässä ja perässä voi olla muita kenttiä tai sitten ei.

# Joskus tulee esiin tilanteita joissa täytyy lukea tiedoston sijasta näytöltä. Esimerkiksi tuossa funktiossa: summaryIP se tehdään kirjoittamalla "$1" paikalle  "/dev/stdin" tai jotta molemmat
# tavat sallittaisiin niin "${1:-/dev/stdin}" jolloin vanha kutsutapa säilyy mutta lisäksi voidaan kutsua myös: cat ~/IPlista | summaryIP
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 09.05.15 - klo:08.58
Netinssä olevista Atomic-group:in selvityksistä ei tule hullua hurskaammaksi. Olenkohan ymmärtänyt oikein päätyessäni seuraavaan:

ensimmäinen yritys on väärä joten poistin sen.
- alunperin sain pätevältä verkkosivulta "osittaisvihjeen" siitä ettei atomic-group:ista ole hyötyä yksinkäytettynä vaan sen tulee olla osana TAI-ryhmää. Hölmöyksissäni poistin tuon TAI:n koska toimihan se ilman sitäkin - mutta se toimi vajavaisesti mitä en huomannut. Väsäsinkin yritelmän 2:
Koodia: [Valitse]
echo abc | grep -P 'a(bc|b)c'     # tulostaa abc ( tuo | on looginen TAI )
echo abc | grep -P 'a(?>bc|b)c'   # ei tulosta mitään. Selvitys miksi: tuo (?>bc) tarkoittaa että ryhmää bc käsitellään kuten yhtä merkkiä. Siis siinävaiheessa
                                  # kun c:tä yritetään sovittaa niin ei ole enää mihin sovittaa, sillä tuo: (?>bc) on jo kuluttanut koko bc:n. 
echo abcc | grep -P 'a(?>bc|b)c'  # tulostaa abcc niinkuin pitääkin. On samantekevää mitä tuon |:n perässä on, vaikka ¤ kelpaa.





 
                               
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 09.05.15 - klo:14.49
Kysymyksestä olisi kannattanut avata uusi aihe, koska se ei koske erityisesti bashia ja shell-skriptausta vaan yleisesti säännöllisiä lausekkeita.

Netinssä olevista Atomic-group:in selvityksistä saa vain osittaisia vihjeitä. Olenkohan ymmärtänyt oikein päätyessäni seuraavaan:
Koodia: [Valitse]
echo abcc | grep -P 'a(?>bc)c'  # tulostaa: abcc
echo abc | grep -P 'a(?>bc)c'   # ei tulosta mitään. Selvitys miksi: tuo (?>bc) tarkoittaa että ryhmää bc käsitellään kuten yhtä merkkiä. Siis siinävaiheessa
                                # kun c:tä yritetään sovittaa niin ei ole enää mihin sovittaa, sillä tuo: (?>bc) on jo kuluttanut sen c:n.

(?>...) on siis ns. "Once-Only Subpattern" (http://php.net/manual/en/regexp.reference.onlyonce.php) aka "Atomic group" (http://www.regular-expressions.info/atomic.html), jolla estetään backtrack-tyyppistä säännöllisen lausekkeen tulkkia palaamasta takaisin ja yrittämästä uudelleen pykälää vähemmän ahneella täsmäyksellä. Syntaksin avulla voidaan suunnitella säännöllisiä lausekkeita, jotka välttävät katastrofaalisen backtrackauksen (http://www.regular-expressions.info/catastrophic.html) hankalilla merkkijonoilla.

Antamasi esimerkit toimivat samalla tavalla, vaikka jättäisit (?>)-määritteen pois eli hakemalla pelkällä lausekkeella "abcc". Muutenkin "(?>bc)" vastaa aina lauseketta "bc", joka on yksiselitteinen ja täsmää vain merkkijonoon "bc".

Sen sijaan backtrack-eston vaikutusta voi havainnollistaa näin:

Koodia: [Valitse]
$ echo "abc" | grep -P '.+'
abc

$ echo "abc" | grep -P '.+c'
abc

$ echo "abc" | grep -P '(?>.+)'
abc

$ echo "abc" | grep -P '(?>.+)c'


Täsmättäessä lauseketta ".+c" merkkijonoon "abc", backtrackaava säännöllisen lausekkeen tulkki täsmää ensin ".+":n koko merkkijonoon "abc". Sitten se huomaa, että jäjellä oleva lauseke "c" ei enää täsmää merkkijonoon, mutta lopettamisen sijaan tulkki palaa takaisin ja täsmää ".+":n vain merkkijonoon "ab". Sitten loppuosakin täsmää ja tulkki päätyy oikeaan tulokseen, eli ".+c" täsmää merkkijonoon "abc".

Viimeinen esimerkkilauseke toimii kuitenkin toisin, koska backtrackaus on estetty atomic groupilla. Siinä ".+" täsmää taas ensin koko merkkijonoon "abc" ja lausekkeen "c" jää yli. Tulkkia on kuitenkin käsketty kokeilemaan ".+":aa vain yhden kerran, joten se ei voi palata takaisin ja yrittää uudelleen lyhyemmällä täsmäyksellä. Sen sijaan tulkki päätyy tulokseen, ettei täsmäystä löydy.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 05.07.15 - klo:02.28
Aloinpa etsiä keinoa jolla saisi tietää onko kovalevy puolijohdetyyppinen vaiko mekaaninen. BASH:issa kaikkea voi viilata loputtomiin, mutta tällähetkellä skripti on seuraavanlainen:
Koodia: [Valitse]
#!/bin/bash
function Kovalevyntyyppi () { [[ $(udevadm info --query=all --name=/dev/$1 | grep DEVLINKS= | awk '{print $3}') ]] && echo 'levy:'$1' on puolijohdetyyppinen' || echo 'levy:'$1' on mekaaninen' ;}
# [[ -f /sys/block/$1/queue/rotational ]] && ( [[ $(cat /sys/block/$1/queue/rotational) -eq 0 ]] && echo 'levy:'$1' on puolijohdetyyppinen' || echo 'levy:'$1' on mekaaninen' ) || echo echo 'levyä:'$1' ei ole' # tämä on "virallinen" tapa määritellä levyntyyppi, mutta se ei toimi usb-levyjen kanssa

function ListaaLevyt () {
apu=$(ls -lF /dev/disk/by-id | grep ata- | grep -o sd.$); [[ $apu ]] && echo ata:lla liitetyt levyt: $apu && { for apuu in $apu; do Kovalevyntyyppi $apuu; done }
echo
apu=$(ls -lF /dev/disk/by-id | grep usb- | grep -o sd.$); [[ $apu ]] && echo usb:llä liitetyt levyt: $apu && { for apuu in $apu; do Kovalevyntyyppi $apuu; done }
}

ListaaLevyt


- ja tuloste esimerkiksi seuraavanlainen:

ata:lla liitetyt levyt: sda sdb
levy:sda on puolijohdetyyppinen
levy:sdb on mekaaninen

usb:llä liitetyt levyt: sdd sdc
levy:sdd on puolijohdetyyppinen
levy:sdc on puolijohdetyyppinen

**
Tulipa tarve mitata prosessori-ytimien kuormitusta. Siihen sopivaksi skriptiksi osoittautui:
- ytimien määrä otetaan automaattisesti huomioon
Koodia: [Valitse]
#!/bin/bash
apu=0; while true; do
  prosessorin_data=$( grep 'cpu'$apu /proc/stat)
  [[ ! $prosessorin_data ]] && break
  echo -n "ydin"$apu'  '; echo $prosessorin_data | awk '{usage=($2+$4)*100/($2+$4+$5)} END {printf "%2.2f %s\n", usage,"%"}'
  let apu++
done


Skriptin kokeilemiseksi aja se kun koneessa ei ole käynnissä mitään muuta (et siis saa roikkua netissä tämän kokeen aikana). Omassa neliytimisessä prosessorissani tulos oli seuraava:
- äskettäiset tapahtumat vaikuttavat, joten kannattaa odottaa minuutti ennenkuin ajaa skriptin. Tulos ei muten ole sama kuin mitä tulee järjestelmän valvonnassa.
ydin0  2.58%[[ $( apt-cache policy lm-sensors | grep "(ei mitään)" ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: lm-sensors . Haen sen' && sudo apt-get install lm-sensors
[[ $( apt-cache policy plotutils | grep "(ei mitään)" ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: plotutils . Haen sen' && sudo apt-get install plotutils
[[ $( apt-cache policy gv | grep "(ei mitään)" ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: gv . Haen sen' && sudo apt-get install gv
n=0; echo "" >~/xy_arvot
while true; do
  apu=$(sensors -Au | grep -Po '(?<=_input: ).*' | tail -$(nproc) | awk '{t+=$0} END {print t/NF}') # lasketaan kaikkien ytimien lämpötilojen keskiarvo - niin monta kuin niitä onkin.
  echo -e $n' ' $apu  >> ~/xy_arvot 
  graph -T ps ~/xy_arvot > ~/plot.ps; gv ~/plot.ps &
  let n+=2; sleep 2 # mittausten välillä noin 2 sekuntia - ajan suhteellinenkin arvo on melko merkityksetön
  killall gv
done
ydin1  2.54%
ydin2  2.86%
ydin3  2.31%
 
Avaa sitten pääte (ubuntussa paina ctrl-alt-t) ja anna käsky: yes
- tuo käsky ajaa prosessoreissa prosessin joka tulostaa jatkuvasti: y.
Avaa sitten toinen pääte (ubuntussa paina taas ctrl-alt-t) ja aja kuormitusta mittaava skripti odotettuasi taas minuutin. Tuloksen pitäisi olla jotakin seuraavankaltaista:
ydin0  9.57%
ydin1  10.0%
ydin2  9.79%
ydin3  9.69%
- prosentit kasvavat mitä kauemmin odotat.
- tottakai skriptissä olisi parantamisen varaa taas loputtomiin; tämä on vain ensimmäiseksi mieleentullut.
**
Halusinpa saada mitatuksi prosessori-ytimen lämpötilan. Osoittautui että ytimen lämpötila on varsin epäselvä asia ja sen mittaava skripti olisi haastava tehtävä, mutta kestoltaan "ikuisuus-projekti". Tässä kuitenkin jonkinlainen versio joka suorittaa mittauksen kymmenen kertaa tulostaen Kovalevyn attribuuttien selville saamiseksi on lukemattomia konsteja. Niiden yhteinen piirre tuntuu olevan se, että ne vaativat sudo:a. Esimerkiksi käsky: blkid toimii näennäisesti ilman sudo:a, mutta lähemmin tarkasteltuna osottautuu että se vaatii sittenkin kunnolla toimiakseen sudo:a. Mutta toki konsti löytyy: otetaanpa esimerkiksi osiotyypin selvittäminen:
 
udevadm info -n /dev/sda1 -q property | grep -Po '(?<=ID_FS_TYPE=).*' 

- ja samalla periaatteella saa muitakin attribuuttejajokakerran mittaustuloksen ja tulostaa lopuksi mittaustulosten keskiarvon:
Koodia: [Valitse]
#!/bin/bash
[[ $( apt-cache policy lm-sensors | grep "(ei mitään)" ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: lm-sensors . Haen sen' && sudo apt-get install "lm-sensors"
n=0
edellinen_tulos=0
while true; do
  mittaus[$n]=$(sensors -Au | grep -Po '(?<=_input: ).*' | tail -$(nproc) | tr "\n" " " | awk 'END {for(i=1;i<=NF;i++) t+=$i; print t/NF}')
  # mittaa kaikkien ytimien lämpötilat ja tulostaa niiden keskiarvon
  [[ ${mittaus[n]} != $edellinen_tulos ]]  && echo ${mittaus[n]} && edellinen_tulos=${mittaus[n]} && let n++
  [[ $n == 10 ]] && echo ${mittaus[*]} | awk '{for(i=1;i<=NF;i++) t+=$i; printf "%s %2.1f", "keskiarvo: ",t/NF}' && break 
done
**
Tässävaiheessa tulee tarve esittää mittaustulokset xy-käyrinä. Yksinkertainen alku on seuraavanlainen:
Koodia: [Valitse]
#!/bin/bash
# aluksi tarkistetaan onko koneessa tarvittava plotutils-ohjelma ja jos ei ole niin se ladataan
[[ $( apt-cache policy plotutils | grep "(ei mitään)" ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: plotutils . Haen sen' && sudo apt-get install "plotutils"

# ensin muodostetaan data-tiedosto jossa tiedot ovat muodossa:  x0 y0 x1 y1 x2 y2 ...   
# x-arvoissa jokaisen arvon tulee olla suurempi kuin edellinen x-arvo
echo 0.0  0.0 1.0  0.2 2.0 0.0 3.0 -0.4 4.0 0.2 5.0 0.6 > ~/abc

# seuraava käsky muodostaa datasta kippuran tiedostoon: ~/plot.ps
graph -T ps ~/abc > ~/plot.psA potential source of a full filesystem are large files left open but have been deleted. On Linux, a file may be deleted (removed/unlinked) while a process has it open. When this happens, the file is essentially invisible to other processes, but it still takes on physical space on the drive. Tools like du will not see it.
**
Seuraavaksi edelliset nivotaan yhteen. Skripti kuvaa reaaliaikaisesti prosessorin kaikkien ytimien lämpötilojen keskiarvon kehitystä graafisesti. X-akselilla ovat sekunnit ja y-akselilla asteet. Piirros skaalataan ja akselit merkitään automaattisesti. Ohjelman aikana voi ajaa samanaikaisesti jotain ohjelmaa joka kuormittaa prosessoria. Millä kuormitatkin niin laita kuormitus päälle ensiksi sillä tämän grafiikka-ohjelman keskeyttäminen on haastava tehtävä. Kirjoittaudu ulos jos et muuten saa skriptiä loppumaan.
grep -Pzo '(?s)(?<=begin).*(?=.*end)
Ohjelma toimii nytkähdellen ja epäilemättä ohjelma on muutenkin omituinen sillä en hallitse BASH:in graafisia toimintoja, esimerkiksi on minulle on täysin epäselvää mitä ohjelmaa kannattaisi käyttää.   

- skripti on hiomaton jotta se olisi helpompi ymmärtää.
- äskettäinen 23.7.2015 päivitys muutti sensors-ohjelman toimintaa ja se vaati muutoksen tähänkin skriptiin.
Koodia: [Valitse]
#!/bin/bash
[[ $( apt-cache policy lm-sensors | grep -E '(ei mitään)|none)' ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: lm-sensors . Haen sen' && sudo apt-get install lm-sensors
[[ $( apt-cache policy plotutils | grep -E '(ei mitään)|(none)' ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: plotutils . Haen sen' && sudo apt-get install plotutils
[[ $( apt-cache policy gv | grep -E '(ei mitään)|(none)' ) ]] && echo 'koneessasi ei ole pakettia  nimeltä: gv . Haen sen' && sudo apt-get install gv
n=0; echo "" >~/xy_arvot
while true; do
  apu=$(sensors | grep -Po '(?<=Core [0-9]).*' | awk '{print $2}' | awk '{t+=$0} END {print t/NR}') # lasketaan kaikkien ytimien lämpötilojen keskiarvo - niin monta kuin niitä ytimiä onkin.
  echo $(echo $n/60 | bc -l)' ' $apu  >> ~/xy_arvot 
  graph -T ps ~/xy_arvot > ~/plot.ps; killall gv; gv ~/plot.ps &
  let n+=2; sleep 2 # mittausten välillä noin 2 sekuntia - ajan suhteellinenkin arvo on melko merkityksetön
done
**
Tavallisella Live-muistitikulla voi taas päivittää salasanaa antamatta kaikki linuxit missä koneessa hyvänsä, vaikka siinä olisi EFI asennus eikä se edes boottaisi. Skripti ei siis ainoastaan päivitä vaan myös palauttaa boottaus-kyvyn. 
- muuten UEFI testi: [[ -d /sys/firmware/efi ]] && echo 'kone käyttää UEFI:a' || echo 'kone ei käytä UEFI:a' .
- skripti löytyy paikasta: http://forum.ubuntu-fi.org/index.php?topic=31223.msg241294#msg241294
**
Teoriassa kone pitäisi päivittää aamun ensimmäisenä tehtävänä. Ja kehitysversio pitää päivittää monesti päivässä ellei halua uus-asentaa tilttiin mennyttä konetta. Mutta varusohjelmiston päivitys ei kelpaa, sillä se jättää oleellisia asioita tekemättä saavuttamatta ajallistakaan hyötyä.

Harkitsin taas kerran siirtymistä BTRFS:ään sillä päivitys-skripti toimii silläkin pienin muutoksin - ja olisihan tuo BTRFS:n snapshot myös sovellettavissa tähän nopeine epäonnistuneen päivityksen hylkäämisineen. Vaan ei se kumminkan auttaisi, sillä recovery-menusta taitaa puuttua kohta: boottaa siihen imageen joka viimeksi toimi. Mutta kieltämättä houkuttelee sillä ei se luultavasti olisi suurikaan työ.
**
Tästä on kyllä annettu tälläkin foorumilla hyvät ohjeet mutta esitetäänpä nyt asiat hieman toisin kuin ennen: aina kun systeemissä joku sovellu kaatuu kirjoitetaan siitä raportti ja annetaan käyttäjälle kaksi vaihtoehtoa: ohita ja:jatka raportti kehittäjille. Mutta tuo "ohita" valinta on salahauta: maailmantappiin jokakerran kun boottaat niin tuo kysymys esitetään uudestaan jos siitä ei ole kertaakaan raportoitu kehittäjille.
Onhan se pikkuisen nolo homma olla kertomatta kehittäjille että Ubuntusi on kaatunut - "käytät vain ilmaista Ubuntua mutta et edes viitsi auttaa kehittäjiä". Mutta monet ohjelmoijatkin käskevät kylmän tyynesti:
Koodia: [Valitse]
sudo rm /var/crash/*
jolloin vanhat raportit nollautuvat tai peräti: sudo gedit /etc/default/apport ja muuttamalla sillä ykkönen nollaksi jolloin raportteja ei enää ylipäätään muodostu.
Ja täytyy myöntää että minäkin syyllistyn moiseen. Koska tekeeköhän ne mitään periferiasta tulleille raporteille ?
**
Piti seurata pesukoneen tehonkulutusta ajan funktiona. Etsiskelin millaisia tiedonkeruu-laitteita Ubuntuun löytyy. Kalliitahan ne ovat ja niiden ubuntu-yhteensopivuus on niin ja näin. Mutta toisaalta: melkeinpä jokainen kone on äänitaajuusalueen oskilloskooppi käytettäessä pakettivarastossa olevaa xoscopea. Siitä en yrittänytkään selvittää kuinka työlästä on saada tarkkoja tuloksia, mutta halpaa se ainakin olisi. Xoscopen seuraava versio osaa muuten fourier-muunnoksen joten spektrinkin saisi.

Kerrotaanpa hieman xoscope:sta:
- bittisyys kuulemma riippuu äänikortista: useimmat äänikortit ovat 16-bittisiä. Äänikorttikin voi kuulemma olla DC-kytketty jolloin silloin saisi myös DC:n. 24-bittisellä äänikortilla siis saa paremman resoluution kuin ammatti-vehkeillä.
- herkkyyttä löytyy vielä yli 10* - siis herkempi kuin useat ammattivehkeet.
- käyttö on hankalaa vasta-alkajalle: esimerkiksi pyyhkäisynopeutta säädetään napeista 9 ja 0, triggausta napista + , herkkyyttä napeista { ja } - jos tarvitsee vaimennusta niin vaimentimen kyllä joutuu itekemään itse ...
- sisäänmeno on mikrofoni-sisäänmeno. Kaksikanavainenkin siis ryökäle, saisi mitattua cosini-fiin:kin. Esimerkkinä toistaiseksi vain "sormihurina" (kun mittapään elikä mikrofonisisäänmenon toisen kuuman tökkää sormensa): tulos on sellainen kuin odottaa sopiikin.
  Kuva avautuu kun kirjoittaudut foorumille:
**
Joskus käyttäjä poistaa vahingossa itseltään sudo-oikeuden ja sudo-oikeuttahan ei käyttäjä itse voi silloin palauttaa koska sudo-oikeuden palauttaminen vaatii sudo-oikeutta. Sudo-oikeus voidaan palauttaa "live-CD:llä tai jollakin muulla sudo-oikeuden omaavalla" siirtymällä chroot:illa sudo-oikeuden menettäneen käyttäjän kovalevy-osiolle ja liittämällä käyttäjä sudo-ryhmään.
- joskus käyttäjä eroaa vahingossa myös jostain muustakin ryhmästä johon olisi syytä kuulua joten ne kannattaa kaikki palauttaa samallakertaa. Kaikki tapahtumat skriptin-pätkänä:
Koodia: [Valitse]
# varmistetaan että käyttäjä kuuluu kaikkiin niihin ryhmiin joihin hänet on liitetty asennettaessa. Huomaa ettei tämä poista mitään.
# käyttäjä liitetään Ubuntua asennettaessa seuraaviin ryhmiin: adm cdrom sudo dip plugdev lpadmin sambashare. Käyttäjä tulee liittää myös ryhmään jolla on käyttäjän nimi.
# käsky: $(echo ${0%} | tr "/" " " | awk '{print $(NF-2)}') kertoo minkä nimisen käyttäjän tiedostoissa ollaan.
for apu in adm cdrom sudo dip plugdev lpadmin sambashare $(echo ${0%} | tr "/" " " | awk '{print $(NF-2)}'); do
  [[ ! $(groups | grep $apu) ]] && sudo addgroup $(echo ${0%} | tr "/" " " | awk '{print $(NF-2)}') $apu
done
**
Kun tekee käyttäjälle tarkoitettua skriptiä on syytä välttää sudo:n käyttämistä ellei se ole ihan pakko sillä ei ihmiset halua salasanaa kirjoittaa - kun ei käytä skriptiä niin eipähän tarvitse salasanaa kirjoitella ja onhan se pieni turvallisuusriskikin. Mutta toki sudo:n voi määrätä mikäli sudo on annettu jo aikaisemmin ja on A potential source of a full filesystem are large files left open but have been deleted. On Linux, a file may be deleted (removed/unlinked) while a process has it open. When this happens, the file is essentially invisible to other processes, but it still takes on physical space on the drive. Tools like du will not see it.edelleen voimassa.

Sen määrittely onko sudo vielä voimassa:
Koodia: [Valitse]
[[ $(sudo -n uptime 2>&1 | grep "load") ]] && echo 'sudo on vielä voimassa' || echo 'sudo ei ole voimassa'
**
Kovalevyn attribuuttien selville saamiseksi on lukemattomia konsteja. Niiden yhteinen piirre tuntuu olevan se, että ne vaativat sudo:a. Esimerkiksi käsky: blkid toimii näennäisesti ilman sudo:a, mutta lähemmin tarkasteltuna osottautuu että se vaatii sittenkin kunnolla toimiakseen sudo:a. Mutta toki konsti löytyy: otetaanpa esimerkiksi osiotyypin selvittäminen:
Koodia: [Valitse]
udevadm info -n /dev/sda1 -q property | grep -Po '(?<=ID_FS_TYPE=).*' 

- ja samalla periaatteella saa muitakin attribuutteja
- tai viimeksi lisätyn USB-laitteen idProduct:idVendor:
Koodia: [Valitse]
echo $(journalctl | grep usb.*New.*idVendor= | tail -1 | grep -Po '(?<=idVendor=)[0-9A-Za-z]*')':'$(journalctl | grep usb.*New.*idVendor= | tail -1 | grep -Po '(?<=idProduct=)[0-9A-Za-z]*')
**
Koska levylle kirjoittaminen on hidasta niin kirjoittaminen suunnataan aina RAM:miin, tarkemmin sanoen buffereihin. Buffereista tuo tieto kirjoitetaan käyttöjärjestelmän toimesta aikanaan levylle mutta saattaa kestää muutaman minuutinkin ennenkuin kaikki on varmasti kirjoitettu levylle. Mikäli kone sammutetaan aikaisemmin niin buffereissa ollut tieto katoaa. Kun tehdään ohjelmaa niin sen kiireettömiin kohtiin voi kirjoittaa käskyn: sync joka tyhjentää buffereista kirjoitettavat levylle.
- tämän "kiireettömän" kohdan valinta on vaikeaa sillä väärään paikkaan kirjoitettuna se aiheuttaa ohjelmassa hidatelua.
- paljonko bufferoitua ja kenties levylle kirjoitettavaa on:  grep ^Dirty /proc/meminfo
- ei sync-käsky teoriassa USB-muistitikulle riitä vaan lisäksi tarvitaan: umount .... ennenkuin sen voi irroittaa - helpompi irroittaa se nautiluksessa.
**
Luin netistä Linux-magazinea. Sikäli hyödyllinen homma että sain taas kertaalleen opastusta siitä kuinka BASH-skriptejä opetetaan tekemään väärin niidenkin toimesta joiden pitäisi tietää. Minkähän takia sillä kunnollisia BASH-virtuooseja on vielä jonkunverran? Jokatapauksessa opin senkin, että seuraava käyttäjälistan tulostus toimii ainakin Ubuntussa:
Koodia: [Valitse]
awk -F: '/home/&&/bash/ {print $1}' /etc/passwd
- muuten || on tai-funktio jos sitä tarvitset.
**
Ohjelmat muodostuvat joskus monesta itsenäisestä osasta joista joku/jotkut keräävät tietoa ja kirjoittavat sen jonnekin ja joku/jotkut lukevat sitä ja tulostavat sen sitten näytölle. Käsiohjauksella työ on yksinkertainen: ensin ajetaan ohjelma joka kerää tiedon ja kirjoittaa sen jonnekin ja kun se on lopettanut niin ajetaan ohjelma joka lukee niitä tietoja ja tulostaa ne näytölle. Mutta entäpä jos tuo tiedonkeruu voi alkaa joko hetikohta tai kuukauden kuluttua tai peräti pätkittäin? Tämä ongelma ratkeaa nimetyn-putken avulla. Toiminnan kuvaus yksinkertaisimmillaam:

Avaa ensimmäinen pääte (esimerkiksi painamalla ctrl-ALT-t) ja:
anna käsky: mkfifo NimettyPutki   
anna käsky: echo hilipatarallaa > NimettyPutki   # enterin painamisen jälkeen ei tapahdu enää mitään, edes uutta kehotetta ei tule

avaa toinen pääte (esimerkiksi painamalla ctrl-ALT-t) ja anna käsky: cat NimettyPutki
se tulostaa: hilipatarallaa
palaa ensimmäiseen päätteseen ja huomaat että se on tulostanut jälleen kehotteen.

- muodostuvaan NimettyPutki-nimiseen tiedostoon voi soveltaa kaikkia niitä toimia joita tiedostoon yleensäkin.
**
NimettyäPutkea voidaan käyttää esimerkiksi kun kaksi skriptiä toimivat eripitkään, mutta silti haluna olisi että ne toimisivat yhtämonta kertaa. Tämän aikaansaamiseksi nopeampi laitetaan odottaman hitaamman valmistumista. Kumpihyvänsä saa olla se hitaampi, tulostusjärjestys kylläkin muuttuu. 
- tottakai BASH:issa on toisia parempiakin keinoja mutta onhan tämä yksi menetelmä.

Tee skripti nimeltä: skripti1
Koodia: [Valitse]
#!/bin/bash
rm -f NimettyPutki && mkfifo NimettyPutki
while true; do
  Alkuhetki=$(date +%s.%N)
  sleep 1 # sleep:in tilalle voi laittaa minkähyvänsä toiminnon ja sen kuluttama aika tulostuu
  Loppuhetki=$(date +%s.%N)
  echo -e 'skripti1:n suoritusaika: '$(echo $Loppuhetki-$Alkuhetki | bc -l)'\n' > NimettyPutki # skripti1 lukitsee itsensä
done
Tee toinen skripti nimeltä: skripti2
Koodia: [Valitse]
#!/bin/bash
while true; do
  Alkuhetki=$(date +%s.%N)
  sleep 10 # sleep:in tilalle voi laittaa minkähyvänsä toiminnon ja sen kuluttama aika tulostuu
  Loppuhetki=$(date +%s.%N)
  echo 'skripti2:n suoritusaika: '$(echo $Loppuhetki-$Alkuhetki | bc -l)
  cat NimettyPutki # tämä vapauttaa skripti1:n
done

Toiminta:
Avaa pääte (paina ctrl-ALT-t) ja käske:         . skripti1
Avaa toinen pääte (paina ctrl-ALT-t) ja käske:  . skripti2
 
Alkaa tulostua noin kymmenen sekunnin välein jotain seuraavankaltaista:
skripti2:n suoritusaika: 10.005455598
skripti1:n suoritusaika: 1.005531242

skripti2:n suoritusaika: 10.005275533
skripti1:n suoritusaika: 1.004949491
**
Tutkin taas kerran kuinka tekstistä saa erotettua tagien välisen lohkon. Tapoja on lukemattomia ja todella erikoisia konsteja on vaikka minkälaisia. Muutamia yksinkertaisia:
Koodia: [Valitse]
grep -Pzo '(?s)begin.*end'                         # (?s) on ohje regex:älle siitä että . vastaa myös rivinsiirtoa.
grep -Pzo '(?s)(?<=begin).*(?=.*end)               # tagien paikoilla on tyhjä rivi
sed -n '/begin/,/end+/ { p }'                      # kirjoita lohkojen väliin tyhjä rivi. Siis esimerkiksi: cat /boot/grub/grub.cfg | sed -n '/BEGIN/,/END+/ { p }'
awk '/BEGIN|begin|<</,/END|end|>>/ {print $0}'     # | merkki on TAI   
- myös regex:t sopii: lspci -v | awk '/VGA/,/^$/'  # lohkon alku on VGA ja loppu tyhjä rivi
- tiedostoissa on joskus epäkelpoja merkkejä. Tällöin esimerkiksi:
cat /boot/grub/grub.cfg | tr -dc [[:graph:]]+'\n' | grep -Pzoi '(?sU)begin.*end' | sed "s/BEGIN/******* lohkon raja *******\nBEGIN/g"




Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.09.15 - klo:11.52
On jatkuva yllätyksen aihe kuinka nopea BASH on - taikka BASH on todella hidas mutta se saa asiat tehtyä nopeasti kun oikein käsketään. Taas kertaalleen sain kokea oikean käskytavan löytämisen vaikeuden kun yritin tehdä skriptiä joka etsii joukon kaikki kombinaatiot. Siinä muuten kului aikaa, tupakkia ja luntatakin piti. Lopulta skripti supistui muotoon:
Koodia: [Valitse]
function kombinaatio (){ eval echo $(printf "{%s,}" "$@"); }    # kutsu: kombinaatio a b c d . . . .
- skripti toimii nopeasti mutta mikäli nopeampaa tarvitaan niin kyllä se BASH:ilta onnistuu vaikka minulta ei.
**
BASH:issa oikean käskytavan löytäminen on vaikeaa siksi että ongelmat yritetään ratkaista samallatavoin kuin ne muissa kielissä ratkaistaan. BASH on hidas, ja jos toiminnan pakottaa BASH:ille niin kaikki tahmaa. Myös kertoman laskussa skripti kuluttaa tuhottomasti aikaa ja resursseja mikäli skriptin tekee väärin. Mutta oikentehtynä skripti on nopea, sen arvoalue on rajoittamaton ja sen kirjoitusasu on yksinkertainen. Koetapa laskea muillakeinoin "100000 kertoma" vaikka eihän siinä juuri mieltä ole. Matikassa vaan ohjelmat eivät saa rajoittaa. Nopea keino kertoman laskemiseksi:
Koodia: [Valitse]
function kertoma () { [[ $1 -eq 0 ]] && kertoma=1 || kertoma=$(seq -s* $1 | bc | tr -d '\\\n' ) ;}; time kertoma 555; echo $kertoma
- kertoma-funktion nimeksi kelpaa kyllä ! ja huutomerkki toimisi vaikka se olisi perässäkin. Mutta silloin skripti olisi paljon isokokoisempi.
**
Aina oppii. Seuraavat käskyt kertovat mistä on kyse:
Koodia: [Valitse]
echo "<<tulostuu>>ei saa tulostua<<tulostuu>>" |  grep -Po '(?<=<<).*(?=>>)'      # mikäli tag-ryhmiä on samalla rivillä useampia niin tuloste on väärä
mutta:
Koodia: [Valitse]
echo "<<tulostuu>>ei saa tulostua<<tulostuu>>" |  grep -Po '(?U)(?<=<<).*(?=>>)'  # tämä taas tulostaa oikein. Ungreedy voi kylläkin myös sotkea
tätä voi jalostaa edelleen sillä edellinen ei tulosta oikein mikäli tag:ien välinen teksti on monella rivillä. Silloin pitää kirjoittaa:
Koodia: [Valitse]
cat tiedosto |  grep -Poz '(?Us)(?<=<<).*(?=>>)'                                 
tai ehkäpä tulostaisi selvälukuisemmin:
Koodia: [Valitse]
cat tiedosto |  grep -Pozn '(?Us)(?<=<<).*(?=>>)' | sed 's/^1:/\n/g'

- tuo << ..... >> on vai esimerkki tageista: kyllä mikä teksti hyvänsä kelpaa tag:iksi
**
Aivan toisenlaisella lähestymistavalla haetaan ratkaisua samantapaiseen asiaan - tässä kylläkin on tarkoituksena tulostaa tiedoston ne lohkot joissa lukee jollakin rivillä määrättävä sana missäkohtaa tahansa:
Koodia: [Valitse]
awk -v RS='' '/sana/ {print $0"\n"}' tiedosto
- lohko elikä kappale on rivijoukko jota kummaltakin puolelta rajoittaa tyhjärivi.
- tässä tulostuvien lohkojen väliin tulostuu tyhjärivi
- kaikki ne kappaleet tulostetaan joissa on tuo sana. Jos jossakin kappaleessa ei ole tuota sanaa ei sitä myöskään tulosteta.
- on aivan sama mitä sanaa etsitään, kunhan se on lohkoissa joita halutaan eikä ole lohkoissa joita ei haluta.
- sanaksi kelpaa regex - tai voihan siitä käyttää nimitystä tag sillä siitä on kysymys
- mikäli halutaan että tulostetaan sittenkin kahden tagin välinen teksti niin käsky muuttuu seuraavankaltaiseksi:
Koodia: [Valitse]
sed "s/tag1/\n\ntag1/g;s/tag2/\n\ntag2/g" tiedosto | awk -v RS='' '/tag1/ {print $0"\n"}' | sed -e 's/tag1//g'
- tag:it saavat olla mielivaltaisia merkkejä, vaikka << ja >>
- tiedoston teksti saa olla yhtenä pötkönä ilman yhtäkään rivinsiirtoa, mutta saa siinä rivinsiirtojakin olla.
- tällaista käskyhirviötä on pakko käyttää mikäli koneen regex ei ole oikeanlainen ja regex on jokatapauksessa erittäin hidas.
- 100.000-rivisen tiedoston käsitteleminen kestää luokkaa 150ms jos tiedostossa on paljon tulostettavaa. Kauankohan kestäisi Pythonilla, C:llä ...?
**
BASH:in matikka on raivostuttavaa, mutta eihän sitä numeronmurskaukseen ole tehtykään. Yksi ikuisuusongelma on lukujen vertaaminen. Helpointa taitaa olla kun suosiolla käyttää jotain seuraavankaltaista:
Koodia: [Valitse]
[[ $(echo $a'>'$b | bc) = 1 ]] && teejotakin
niin ei tarvitse miettiä. Tosin omituisuutensa tuolla BC:lläkin on, esimerkiksi tieteellisessä esitystavassa eksponentiaation merkki on iso E.
**
Osa Linuxin käskyistä on niin nopeita että Time-käsky väittää niiden nopeudeksi nolla ja muilla menetelmillä saadut tulokset heittelevät mahdottomasti. Arvoista saa vakaita ajoittamalla käskyt monta kertaa ja ottamalla tuloksesta keskiarvon.
Tällätavoin menetetään tulosten absoluuttinen arvo, sillä silloinhan todennäköiseti mitataan cache:ja, buffer:eita tai onhan noita muitakin. Mutta yhtä asiaa ei menetetä: samantapaisten käskyjen
suoritusaikojen suhteellista eroa: esimerkiksi kun mitataan suoritusajat käskyille <echo a>" ja <printf "%s\n"> niin voi olla varma että niiden suhteellinen ero on oikea.
Koska tämä ratkaisee sen, kuinka skripti kannattaa kasata voi ajat esittää myös absoluuttisina joten seuraava pitää paikkansa:

Echo:kin on nopea kuten itse voit todeta käskyllä: merkkijono=$(cat /dev/urandom | base64 | head -c 6000); time echo $merkkijono
joka muodostaa ensin 6000-merkkisen sanan ja tulostaa sen sitten ajoitettuna: echo kestää koneen nopeudesta riippuen 0-2ms. Siitä tulee siis alle 0.15 mikrosekuntia kirjainta kohti - echo-käskyn kutsu kylläkin kestää noin mikrosekunnin.
- printf on hieman hitaampi.
- muuten 0 tarkoittaa: alle 0.5 -> TIME-käsky osaa pyöristää oikein.

yksinkertainen vertailu elikä esimerkiksi: [[ $a = 1 ]] && tee_jotakin  kestää noin 5 mikrosekuntia. Nopea on myös käsky:
echo ${muuttujanimi//jotakin/joksikin_muuksi} kestää luokkaa .1ms+7mikrosekuntia/kirjain joten onhan tuo yleensä nopeampi kuin sed joka vie putkituksineen noin 3ms.
- mutta kun ei käsitelläkään tekstijonoja vaan tiedostoja niin BASH käsky on:
Koodia: [Valitse]
readarray a < tiedosto && echo -e "${a[*]//mikä/miksi}"
 
ja vastava sed-käsky:
Koodia: [Valitse]
sed 's/mikä/miksi/g' tiedosto
ja BASH-käsky onkin lyhyillä tiedostoilla nopeampi ja pitkillä tiedostoilla hitaampi kuin sed-käsky.
- tästä selvisi sekin että * on merkittävästi nopeampi kuin @
- suoritettaessa perättäisten käskyjen ajoitusta täytyy ajoitettavista käskyistä muodostaa ryhmä joka täytyy laittaa aaltosulkuihin näin:
Koodia: [Valitse]
{ käsky1; .... ; käskyN;}  # huomaa laittaa tuo ; ennen viimeistä aaltosulkua
näin siksi että uuden prosessin muodostaminen kestää vajaan millisekunnin. Siis käskyjä ei pidä ryhmittää näin: ( käskyryhmä ) sillä tapa lisää millisekunnin suoritusaikaan ja vaikka se perii muuttujien arvot muodostajaltaan niin se ei palauta niitä - joskus harvoin tästä on kyllä etua. Käskyjen ryhmittäminen kannattaakin yleensä tehdä näin: { käskyryhmä; } sillä siten ryhmittäminen vie ylimääräistä aikaa noin 2 mikrosekuntia ja muuttujat myös palautetaan (tai pikemminkin ne ovat samoja eikä niitä tarvitse palauttaa).

Koneessa käskyjen suoritusajat ovat epämääräisiä; joskus käsky kestää 2ms ja joskus se kestää 5ms. Ja alta puoli-millisekuntia kestävät suoritusajat pyöristetään nollaksi. Eikä aika ole tuonut tähän helpotusta eikä varmaan tuokaan - syyn tähän voisi esittää useammallakin tavalla:
- minkä intel tuo sen softa vie
- Torvalds esitti homman näin: "Linux ydin on turvonnut". Siis syyllinen on käyttöjärjestelmä eikä BASH. Saattaa käydä jopa niin että epämääräisyys alkaa kasvamaan sillä samalla kun ydin turpoaa niin sen ylläpitäjät vähenee eivätkä kerkiä tekemään aikaisemmin tehtyyn uusien lisäyksien vaatimia muutoksia.

Käskyn suoritusaikaa ei voi mitata mittaamalla kuinka kauan perättäisten samojen käskyjen suoritus kestää, sillä käskyä suoritettaessa sen koodi kännetään cacheen josta se toteutetaan.
- siis tällätavoin ei saa käskeä: for (( n=1; n<=1000; n++ )); do käsky; done
sillä kun seuraava käsky on sama kuin edellinenkin niin sen koodi on jo cachessa ja suoritus on tosinopea. Käskyn suoritusaika tuleekin mitata käskemällä esimerkiksi:
for (( n=1; n<=1000; n++ )); do { käsky; sleep .001}; done jolloin käsky-koodi joudutaa jokakerralla kääntämään uudestaan - loppuajasta täytyy sitten vähentää tuo "sleep .001":n aika.
- noita aikaisemmin esitetettyjä käskyjen suoritusaikoja on korjattu tämän mukaisiksi.


 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 09.10.15 - klo:20.26
Mitä ovat ne BASH:in valmiiksitehdyt funktiot joista saa listan käskyllä: declare ? Senverran olen katsonut että niitä voi käyttää mikäli "arvaa" kuinka niitä kutsutaan huolimatta siitä ettei niissä ole sanaa: function.
Listan pelkistä otsikoista saa käskyllä: declare | grep ()  . Löytyykö niille jostain käyttöohje ?

Niitä on 190klp. Taitavat olla paketin bash-completion määrittelemiä; bash-completion on tehty jottei systeemien ylläpitäjät joutuisi kirjoittamaan niin paljon.
Tosin ei pidä sekoittaa pakettia bash-completion toimintoon bash-completion. Tosin kyse ei ole täysin eri asiasta muttei siis samastakaan.
https://wiki.debian.org/Teams/BashCompletion  .
**
Aloinpa ratkaista ongelmaa johon ei edes kunnolisia osittaisratkaisuja osattu esittää. Lopetin tuon iäisyys-projektin aika nopeasti, silä sain iäisyys-projektista jo sen minkä takia siihen ryhdyinkin, nimittäin huomion että parhaan tuloksen saa kun käyttää mahdollisimman yksinkertaisia menetelmiä, esimerkiksi perus-käskyjä.
- paras tulos: nopein, koodiltaan selväpiirteinen eikä vääriä positiivisia- eikä negatiivisia harha-iskuja.
- se ongelma yksinkertaisissa ratkaisuissa on, että ihminen haluaa ajatella monimutkaisesti ja ei edes suostu ajattelemaan yksinkertaisesti; ihminen haluaa myöskin käyttää voimaa silloinkin kun siitä on haittaa.

Esimerkiksi kysypä kuinka tulostetaan tiedoston viimeinen rivi. Vastauksena tarjotaan ilmanmuuta: cat tiedosto | tail -1 . Kuitenkin helpompaa olisi käskeä: tail -1 tiedosto . Sen suoritusaikakin on pikkuisen nopeampi.
- mutta yleensä kysymyksellä tarkoitetaankin: kuinka tulostetaan tiedostosta viimeinen rivi jolla on tekstiä? Sen saa helpoiten käskyllä:
Koodia: [Valitse]
awk '{ if (NF > 0) n = $0 }END{print n}' tiedosto
Se on muuten nopeinkin.
**
Matriisi voidaan määritellä kahdella tavalla:
Koodia: [Valitse]
MatriisinNimi=(5 6)                      # jolloin BASH määrää itse indexit alkaen nollasta. Toinen tapa:
MatriisinNimi[0]=5; MatriisinNimi[1]=6   # indexit voi tällöin määrätä itse ja aivan mielivaltaisesti.
- kummallakin esitystavalla BASH:in sisäinen esitystapa on samanlainen:
Koodia: [Valitse]
MatriisinNimi=([0]="5" [1]="6")- BASH:issa ei ole kaksi- tai moniulotteisia matriiseja. Niitä voi tavallaan kyllä kuvata joko tavallisessa- tai assosiatiivisessa yksiulotteisessa matriisissa.
- matriisia voi käyttää vaikka sitä ei määrittelisikään, mutta jos matriisin määrittelee niin se tehdään näin: declare -a matriisi    # siis indexi-rajoja ei määritellä.
- BASH tuntee myös matriisin jonka osoite voi olla tekstiä. Tällainen assosiatiivinen matriisi määtitellään näin: declare -A matriisi.   
- matriisin jäsen tulostetaan esimerkiksi: echo ${matriisi[indexi]}: siis esimerkiksi: echo ${matriisi[2]
- matriisin kaikki jäsenten arvot voi tulostaa käskyllä:
Koodia: [Valitse]
echo ${a[@]}           # jolloin ne kirjoitetaan peräkkäin välilyöntien erottamina
echo -e ${a[@]/%/\\n}  # jolloin  arvot tulostuvat alekkain. Tätä voi käyttää testaamiseen: mikäli kyseessä on sittenkin tekstijono niin se tulostuu edelleen yhteen riviin.
- $muuttuja on sama kuin ${muuttuja[0]}.
- Matriisin käsittelemiseksi voi käyttää ohjelmaa nimeltään awk. awk:in toimintaa on paras kuvata esimerkeillä:
Koodia: [Valitse]
Rivisummien laskeminen  : cat matrix | awk 'BEGIN {j=0} {for(i=1;i<=NF;i++) t+=$i; print "rivin",j" summa: ",t; t=0 j++}'
Sarakesummien laskeminen: cat matrix | awk  'BEGIN {}{for(i=1;i<=NF;i++) s[i]+=$i} END {for(i=1;i<=NF;i++) print "sarakeen",i," summa=" s[i]}'
- mihin tahansa muuhunkin hommaan voi tehdä awk-skriptin. Ne toimivat nopeasti. Awk:iin tottunut koodaaja kodaakin sillä erittäin nopeasti, mutta
  toisaalta awk-koodaus on jäämässä unhoon vielä nopeammin kuin BASH.
- awk osaa myös tieteellisen esitysmuodon, desimaalit, sinit sun logaritmit ... esimerkiksi:
Koodia: [Valitse]
awk 'BEGIN{print .11e1+2.2}'    # siis tämä lause kirjoitetaan ihan yksikseen
**
BASH:ista kertovat oppaat eivät edes mainitse BASH:in tärkeintä käskyä enkä ole tajunnut että asiahan kaipaa selvittämistä: kun BASH:in koodi-rivillä lukee yksikseen: $muuttuja  niin se tulkitaan käskyksi: mene suorittamaan funktiota jonka nimi on muuttujassa ja suoritettuasi funktion palaa tätä käskyä seuraavan käskyyn. Kun BASH:ia alkaa käyttämään ei funktioita tehdä itse joten käskyllä ei olekaan alussa oikein mitään mieltä. Mutta kun funktioita alkaa käyttämään tulee käskystä nopeasti peruspilari.

Mikäli muuttuja on lisäksi assosiatiivisen matriisin jäsen niin saadaan samalla tehtyä 'koodimuunnos'. Lisäksi muuttujan eteen voidaan laittaa ehto: [[ ehto ]] && $muuttuja
- myös sisäisiä funktioita voi kutsua: apu=ls; $apu. Muuten parametreja annettaessa pitää olla lainausmerkit: apu="ls *"; $apu
**
Ei ole koskaan tullut mieleen että se ettei find-ohjelma hyväksy regex:iä ei tarkoita sitä ettei find-ohjelma tuntisi regex:iä. Mutta findille täytyy kertoa se: find $HOME -regex '.*[a-z]'
- käsky selväsanaisena: etsi kotikansiostasi tiedostot joiden nimissä on ainakin yksi pieni kirjain. 
**
Samanlaisten rivien poistamiseen BASH:issa on käsky uniq, mutta tekstinkäsittelyyn se on jokseenkin sopimaton. Sensijaan kannattaa käyttää seuraavia awk-käskyjä:
Koodia: [Valitse]
awk '!x[$0]++'                                                # poistaa tiedostosta duplikaattirivit
awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }' | awk '!x[$0]++' # poistaa ensin välilyönnit ja tab:it rivien alusta ja lopusta - ja sitten vasta poistaa duplikaattirivit
awk '!NF || !x[$0]++'                                         # poistaa tiedostosta duplikaattirivit jättäen tekstin tyhjät rivit rauhaan
**
Dokumentti muodostuu kappaleista. Eri kappaleissa saa yleensä olla samanlaisia rivejä joten vain kussakin kappaleessa sen omat duplikaatti-rivit voi poistaa. Tämänvuoksi dokumentin jokainen kappale täytyy erottaa omaan tiedostoonsa, suorittaa niille duplikaattien poisto ja koota sitten tiedostot yhteen. Tämä täytyy tehdä kahdessa vaiheessa sillä ensimmäinen vaihe tehdään tilanteesta riippuen erilailla:
1. Kun kappaleen alkurivillä on jotakin kappaleelle ominaista; esimerkiksi koodissa sana BEGIN.
Koodia: [Valitse]
sed  '/kappaleelle_ominaista/ilisätty_rivi' tiedosto_jossa_kappaleet_ovat > /tmp/delmee02. Kun kappaleiden välissa on tyhjä rivi:
Koodia: [Valitse]
sed 's/^$/lisätty_rivi/g' tiedosto_jossa_kappaleet_ovat > /tmp/delmee0
Näin aikaansaatu aputiedostosta kappaleet sitten jaetaan omiin tiedostoihinsa:
Koodia: [Valitse]
awk -v RS="lisätty_rivi" '{ print $0 > "/tmp/delmee"NR }' /tmp/delmee0
kappaleet liitetään yhteen tiedostoksi( perättäiset tyhjät rivit supistetaan yhdeksi):
Koodia: [Valitse]
rm -f /tmp/delmee; n=1; while true; do [[ -f /tmp/delmee$n ]] && { awk '!NF || !x[$0]++' /tmp/delmee$n > apu; cat -s /tmp/delmee apu >> /tmp/delmee ; ((n++));} || exit; done
lopuksi siistitty tiedosto on paikassa: /tmp/delmee
- roskanpoisto puuttuu ja epämääräisyyksiäkin on mutta toimii silti.
**
BASH_REMATCH:ista puhutaan paljon mutta ymmärrettäviä esimerkkejä ei juurikaan esitetä. Tarkoitan sellaista esimerkkiä jossa tulostetaan BASH_REMATCH, BASH_REMATCH[1], BASH_REMATCH[2], BASH_REMATCH[3] ... sotkematta asiaan mitään muuta jotta asiasta tulisi kunnollinen rautalankamalli jota ei voi käsittää väärin.

Yhden esimerkin löysin ... no jaa, tein ziljoona korjaus-yritystä ennenkuin sain toimimattomasta toimivan. Ja tämänjälkeen alkoi löytyä muitakin  kun tiesi mitä pitää etsiä. Esimerkki on tämmöinen:
Koodia: [Valitse]
#!/bin/bash
  [[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]] && {
      # REMATCH tehdän kohdassa:[[ "012 a34567b89cdefgh" =~ ([a-z])[0-9]*([a-z])[0-9]*([a-z]) ]]."
      # Regex muodostuu useasta regex:stä. Tässä esimerkissä on kolme regex:ää ympäröity kaarisuluilla."
      # mikäli sanassa mikään ei täytä koko regexää niin ei tulosteta mitään; siis kaikille regex:än BASH_REMATCH-kentille pitää löytyä arvo.
      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]}" ;}

Esimerkiksi:[[ "teksti_112233.txt" =~ (.*[[:punct:]])([0-9][0-9])([0-9][0-9])([0-9][0-9])(.*) ]] && echo ${BASH_REMATCH[1]}${BASH_REMATCH[4]}${BASH_REMATCH[3]}${BASH_REMATCH[2]}${BASH_REMATCH[5]}  # tulostaa: teksti_332211.txt
**
Yleiskäyttöinen lukurutiini. Kokeilua varten lisäsin monia ominaisuuksia joiden kanssa on hyvä kokeilla mihin tämä pystyy; käytössähän voi turhat ominaisuudet poistaa.
- mitäpä olet mieltä määrittelystä: eval eval      # siis peränjälkeen kaksi eval:ia.
Koodia: [Valitse]
#!/bin/bash
# petteriIII 31.10.2015
# declare -A jotakin # mikäli halutaan muodostaa assosiatiivisia matriiseja täytyy tämä lause olla. Mutta jos assosiatiiviset alkavat toimia niin tavalliset lakkaavat toimimasta.

function tekijät () { apu=$( factor $1 ); echo $apu | grep -Po '(?<=:).*' ;}

function permutaatiot () { eval echo $(printf "{%s,}" "$@"); }    # kutsu-esimerkki: permutaatio a b c d

function TulostaMatriisi () {
[[ $(eval echo \${$1[*]}) ]] && {
echo -n $1':n arvot     : '; eval echo \${$1[*]}   # arvojen väliin tulostuu välilyönti
echo -n $1':n osoitteet : '; eval echo \${!$1[*]}  # osoitteiden väliin tulostuu välilyönti.
} | column -t; echo ;}


function lue_näppis {
local -n array=$1 # tämä lause määrää että kun tehdään array:lle jotakin niin se siirtyy myös siihen jonka nimi on tullut parametrissa $1.
                  # jos array:ssa ei ole kuin jäsen 0 niin se ei eroa mitenkään array-nimisestä tavallisesta muuttujasta - se voidaan tulostaakin käskyllä: echo $array.

echo "testata voi kirjoittamalla jonkun seuraavista:"
echo '- tekstijonon jossa saa olla välilyöntejä tai toisia tekstijonoja, esim: 1 2 "3 4" 5 . Jos matriisin jäsenessä on välilyöntejä on jäsen kirjoitettava lainausmerkkien väliin.'
echo '- tekstijonon voi määritellä myöskin:{1..5} tai:{1..5}{1..5} tai: {$alku..$loppu}'
echo '- numerot käsitellään tekstijonoina joten niissä voi olla mitävaan'
echo '- matriisi kirjoitetaan näin:(1 2 3) mikäli tyydytään automaattisesti määrättäviin osoitteisiin, mutta mikäli osoitteet halutaan antaa itse täytyy kirjoittaa: ([0]="1" [1]="2" [2]="3")'
echo '- matriisin voi määritellä näinkin: ({1..5})  tai: ({1..5}{6..10})  tai: ({1..5}1 2 3) mutta tuollaisissa määritelmissä alkaa tulla eteen se että kone tulkitsee ja sinä vikiset.'
echo '- pääohjelman muuttujia voi käyttää, joten mikäli alku ja loppu ovat määriteltyjä niin ihan ihan käypä käsky on: ({$alku..$loppu}{$loppu..$alku})'
echo '- tulosteessa tekstijonon erottaa matriisista siitä, että mariisin jäsenistä tulostetaan myös osoitteet.'
echo '- assosiatiivinen matriisi täytyy aina kirjoittaa näin: ([bbb]="eee" [ccc]="fff" [aaa]="ddd" )'
echo '- permutaatiot saa tietää tämänmuotoisella käskyllä: permutaatiot a b c d e'
echo '- luvun tekijät saa tietää tämänmuotoisella käskyllä: tekijät 256'
echo '- myös tiedoston sisällöstä voi muodostaa matriisin:  /etc/fstab tai:/boot/grub/grub.cfg'
echo '- vanhoja vastauksia voi selailla nuolinäppämillä ja ctrl-r toimii normaalisti.'
echo '- editointi toimii kokoajan'
echo 'kirjoita jotakin: '

HISTFILE=~/.user_history # tämän skriptin toiminta-ajaksi siirretään BASH:in oma historia-toiminta tälle skriptille. Esimerkiksi "ctrl r" toimii niinkuin kaikki muukin.
set -o history 
read -e array ; echo $array >> ~/.user_history     
HISTFILE=~/.bash_history
set -o history

[[ $(echo $array | grep tekijät) ]] && array=($($array))      # array saa arvot funktion tekijät      kutsusta - parametrit ovat kutsussa
[[ $(echo $array | grep permutaatiot) ]] && array=($($array)) # array saa arvot funktion permutaatiot kutsusta - parametrit ovat kutsussa
[[ ${array:0:1} = '(' && ${array:${#array}-1} = ')'  ]] && { apu=$(echo $array | grep -o {.*}); apuu=$(eval echo $apu); array=$(echo $array | sed "s/$apu/$apuu/g"); eval array=$array ;}
[[ ${array:0:1} = '{' && ${array:${#array}-1} = '}'  ]] && array="$( eval eval echo $array)"
[[ ${array:0:1} = '/' ]] && readarray array < $array && echo -e "${array[*]}"
}

# pääohjelmassa oltaessa:
alku=1; loppu=5
lue_näppis jotakin # mikäli halutaan lukea jo olemassa oleva matriisi niin sen nimi tulee paikalle:jotakin tässä ja seuraavassa lauseessa jos seuraavaa lausetta edes tarvitaan.
[[ $(echo ${!jotakin[@]} | cut -sd' ' -f 2) ]] && TulostaMatriisi jotakin || echo $jotakin # [[ $(echo ${jotakin[@]} | cut -sd' ' -f 2) ]] on testi siitä onko kyseessä matriisi
# jos on luettu tiedosto on sen sisältö matriisissa jonka nimi tässä on:jotakin , mutta sen tilalle voi kirjoittaa halutun matriisin nimen.
**
Tein numeroiden syöttämistä varten skriptin jota voi käyttää kun näpytellee PC:lle numeroita sen näppäimillä. Se tarkistaa syötettäessä kokoajan että syötettävä merkki on sellainen että se on mahdollinen luvun silläkohdalla ja mikäli ei ole niin ei hyväksy merkkiä. Esimerkiksi kirjaimimia ja erikoismerkkejä ei hyväksytä koskaan, e:n perässä ei voi olla desimaalipistettä ...
Koodia: [Valitse]
#!/bin/bash
# numeron lukeminen näppäimistöltä siten ettei merkkejä kirjoitettaessa merkkiä hyväksytä ellei merkki ole mahdollinen silläkohtaa.
function aloitus () {
numero=''
sallitutmerkit=+-.e0123456789
clear
alku=1 ;}

aloitus
while true; do
#stty echo # oli koe - onko merkitystä
  read -rsN 1 merkki
  [[ $(echo $merkki | od | awk '{print $2}') = 005177 ]] && aloitus && continue # BS:n painaminen pyyhki näytön ja aloittaa alusta
  merkki=$(echo $merkki | tr , .)                                       # desimaalipilkkua ei hyväksytä vaan muutetaan se pisteeksi 
  [[ -z $merkki ]] && exit                                              # enteriä painettu
  [[ $merkki = . && $alku -eq 0 ]] && continue                          # pistettä ei hyväksytä e:n jälkeen
  [[ $merkki = e && ! $(echo $numero | tr -dc 012346789) ]] && continue # e poistetaan mikäli yhtään numeroa ei vielä ole annettu
  [[ $( echo $merkki | tr -d $sallitutmerkit) ]] && continue            # mitään merkkiä ei hyväksytä ellei se ole sallittu. Testi jotenkin vuotaa, siksi ensimmäinen poisto 
  (( alku++ ))
  sallitutmerkit=$(echo $sallitutmerkit | tr -d +-)                     # + ja - sallitaan vain alussa
  [[ $merkki = . ]] && sallitutmerkit=$(echo $sallitutmerkit | tr -d .) # pistettä ei tämänjälkeen hyväksytä.
  numero=$numero$merkki
  [[ $(echo $numero | tr -dc e ) ]] && sallitutmerkit=$(echo $sallitutmerkit | tr -d e. ) && alku=0 # vain yksi e sallitaan; ja kun e on annettu ei desimalipistettäkään enää hyväksytä
  [[ ${numero:${#numero}-1:1} = e ]] && sallitutmerkit="+-"$sallitutmerkit                          # heti e:n jäljessä voi taas olla yksi + tai -
  clear; echo -n $numero
done
**
Taas kertaalleen jouduin tekemään uuden testin IPv4-osoitteille sillä monimutkaisemmat regex:ät tökkivät, BASH_REMATCH ei innosta ja yleensä testit lisäksi vuotavat. Hyvä IP-testi onkin ihan perus-käskyistä väsätty ja sellainen mikä ei päästä läpi mitään jossa on fyysistä vikaa muttei myöskään anna vääriä hälytyksiä:
- testi ei välitä siitä mikä merkki toimii erottimena aliosoitteiden välissä mikä on ehkä hyväkin. Mutta se ei myöskään tarkista onko osoitteessa turhia merkkejä. Mutta toisaalta ipv4 onkin jo historiaa.
Koodia: [Valitse]
function ipv4testi () { for n in {0..255}; do echo $1 | grep -wo 0*$n; done | [[ $(wc -l) = 4 ]] ;}; ipv4testi 255:0:012:56 && echo ip hyvä || echo ip kelvoton
**
IPv6-osoitteen tarkastaminen:
Täydellinen IPv6-osoite on esimerkiksi: 2001:0db8:0000:0000:0000:ff00:0042:8329
- siten IPv6-osoitteessa voi olla korkeintaan 7kpl :-merkkien eroittamaa ryhmää ja hexdigitejä kussakin ryhmässä korkeintaan 4
- ryhmiä eroittava merkki on aina : joten muita merkkejä ei saa olla kuin : ja hexdigit:ejä

IPv6-osoiteen tarkistamisessa yhdistetään nuo kolme vaatimusta:
Koodia: [Valitse]
function ipv6testi () { [[ $(echo $1|tr -d :[[:xdigit:]]) = '' && $(echo $1|tr -dc :|wc -c)<8 && $(echo $1|grep -o '[[:xdigit:]]*'|wc -L)<5 ]];}; ipv6testi 9901:0db8::ff:42:29  && echo kunnossa || echo tökkii
- siis tuon: 9901:0db8::ff:42:29 tilalle kirjoitetaan testattava ipv6-osoite
- huomioi että vain fyysiset virheet selviävät näin ja loogiset virheet jäävät.

Tarkistuksessa täytyy lisäksi huomioida:
- jokaisesta ryhmästä etunollat voi poistaa: 2001:db8:0:0:0:ff00:42:8329
- perättäiset 0:t voi yhdistää kahdeksi :-merkiksi: 2001:db8::ff00:42:8329
- esimerkiksi loop-back osoite: 0000:0000:0000:0000:0000:0000:0000:0001 voidaan kirjoittaa ::1
   
 


 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 04.11.15 - klo:03.11
Tiedoston listaaminen korostaen kaikki siinä olevat ipv4-osoitteet

Paras ratkaisumenetelmä ipv4-osotteiden löytämiseksi on awk-perustainen, eikä regex-perustainen.
- täydellinen regex jolla voi tarkistaa ipv4:n on "kolme riviä pitkä hirviö". On kylläkin olemassa sovelluksia jotka tarkistavat ipv4-osoitteen käyttäen lyhyitä regex:iä. Sama tarkastus mikä tässäkin on, eikä edes nopeampi. Eikä niitäkään  voi käyttää tiedoston seulomiseen vaan ne vaativat tiedoston seulomiseen  samantapaiset kilkkeet kuin tässäkin on.

Koodia: [Valitse]
#!/bin/bash
# tarkoituksena on listata kotikansiosta tiedosto-niminen tiedosto siten että siinä olevat ipv4-osoitteet ovat tulostettu punaisella muun tekstin ollessa mustaa. 51
export GREP_COLOR="01;31" # red; 32=green; 33=yellow; 34=blue; 35=purple

# ensiksi funktio joka tulostaa sille syötetyn tekstin mikäli se on kelvollinen ipv4-osoite
function tulosta_kelvollinen_ipv4 () { [[ $(echo $1 | grep [a-zA-Z]) ]] || echo $1 | awk -F: 'NF==4 && $1<=255 && $1>=0 && $2<=255 && $2>=0 && $3<=255 && $3>=0 && $4<=255 && $4>=0' ;}

# etsitään tiedostosta ne numerosarjat jotka saattaisivat olla ipv4 osoitteita ja talletetaan niistä muodostuva lista kovalevylle:
grep -Po '[[:alpha:]]*[0-9]*[:0-9]*' ~/tiedosto > /tmp/delme1

# tarkistetaan listan jäsenet yksitellen ja tehdään kovalevylle uusi lista hyväksytyistä:
echo '' > /tmp/delme2; while IFS=$'\n' read -r rivi; do tulosta_kelvollinen_ipv4 $rivi >> /tmp/delme2; done < /tmp/delme1

# listataan tiedosto merkiten siihen ipv4-osoitteiksi hyväksytyt:
grep --color -E $(cat /tmp/delme2 | tr '\n' \|) ~/tiedosto
read -p 'paina enter jatkaakseesi'

**
Tiedoston listaaminen korostaen kaikki siinä olevat ipv6-osoitteet

Ipv6-osoitteiden suodattaminen tiedostosta onnistuu samantapaisella keinolla kuin ipv4:nkin. Ja tätä menetelmää onkin pakko käyttää, sillä ipv6:lle ei ole olemassa mitään kunnollista regex:ää. Se raaka-lista joka tässä muodostetaan regex:än avulla tiedostossa mahdollisesti olevista ipv6-osoitteista käsittää useampia harha-iskuja kuin oikeaan osuneita.
- muuten vaikka näissä menetelmässä käytetäänkin kovalevy-operaatioita eivät ne hidasta toimintaa, sillä skriptin kannalta toimitaan kokoajan RAM:missa olevissa buffereissa.
- ipv6 osoitteissa voi lopussa olla /64 mikä merkitsee kuulumista joukkoon. Tarkemmin: http://www.tutorialspoint.com/ipv6/ipv6_subnetting.htm. Tällaisista merkitään vain osoiteosa
Koodia: [Valitse]
ja
#!/bin/bash
# tarkoituksena on listata kotikansiosta tiedosto-niminen tiedosto siten että siinä olevat ipv4-osoitteet tulostetaan punaisella muun tekstin ollessa mustaa.
export GREP_COLOR="01;32" # green; 31=red; 33=yellow; 34=blue; 35=purple

# ensiksi funktio joka kertoo onko sille syötetty varmasti ipv6-osoite:                       
function ipv6testi () { [[ ! $(echo $1| tr -d :[[:xdigit:]]) ]] && [[ $(echo $1|tr -dc :|wc -c) = 7 || $(echo $1 | grep :: ) ]] && [[ $(echo $1|grep -o '[[:xdigit:]]*'|wc -L) < 5 ]] ;}


#etsitään tiedostosta ne jotka saattaisivat olla ipv6 osoitteita ja talletetaan ne levylle:
grep -Pow '::[:[:xdigit:]]*|[:[:xdigit:]]*' ~/tiedosto > /tmp/delme1

# tarkistetaan listan jäsenet yksitellen ja tehdään kovalevylle uusi lista hyväksytyistä:
echo '' > /tmp/delme2; while IFS=$'\n' read -r rivi; do ipv6testi $rivi && echo $rivi >> /tmp/delme2; done < /tmp/delme1

# listataan tiedosto merkiten siihen ipv6-osoitteiksi hyväksytyt:
grep --color -E $(cat /tmp/delme2 | tr '\n' \|) ~/tiedosto
read -p 'paina enter jatkaakseesi'
**
Myöd BASH:issa tekstilohkoja voi käsittellä mitenkä hyvänsä jos pystyy pukemaan tehtävän sanoiksi. Näitä tämäntyyppi:siä käskyjä on lukemattomia, niiden muodostaminen on hidasta eikä ole mahdollista muistaa niistä oikeastaan mitään. Käskyvarasto onkin täysin pakollinen ja täytyy olla jokin keino löytää sieltä haluamansa. Jos käskyvapetterurastoa ei ole niin käy juuri niin kuin BASH:ille on käynyt. Muut kielet porskuttaa juuri sentakia että niissä nuo varastot on hoidettu edes jollaintavoin. Seuraavat käskyesimerkit eivät oikeastaan ole esitetty senvuoksi että ne olisivat hyödyllisiä, vaan senvuoksi että tuo käskyvaraston pakollisuus selventyisi. Käskyesimerkkkejä:

1. Mikäli teksti muodostuu lohkoista niin todennäköisemmin haluttaessa poistaa samanlaiset rivit halutaan poistaa toisinnot vain kunkin lohkon sisällä. Siihen sopiva käsky on:
ensin jaetaan tiedosto ali-tiedostoiksi tyhjien rivien kohdilta:
Koodia: [Valitse]
awk -v RS='' '{ print $0 > "/tmp/delmee"NR }' ~/koe2
sitten käsitellään muodostetut ali-tiedostot ja kootaan tulos taas yhteen:
Koodia: [Valitse]
echo '' > /tmp/delmee_kokonaisuus; n=1; while true; do [[ -f /tmp/delmee$n ]] && awk '{$1=$1} !x[$0]++' /tmp/delmee$n >> /tmp/delmee_kokonaisuus || exit; ((n++)); done
   
- lopputulos on tiedostossa: /tmp/delmee_kokonaisuus

2. Kun halutaan tulostaa tiedostosta /boot/grub/grub.cfg ne BEGIN- ja END-rajoittimien väliset lohkot, joiden jollakulla rivillä lukee: source niin käsketään:
Koodia: [Valitse]
sed -n '/BEGIN/,/END+/ { p }' /boot/grub/grub.cfg | awk -v RS='' '/source/ {print $0"\n"}'
]
**
BASH:ista puuttuu käskyjä - tai ei oikeastaan puutu vaan käskyt on määritelty hassusti: esimerkiksi loopeista puuttuu helppokäyttöisin käsky: kertaa x tee jotakin
Tottamaar on hölmöä ohjelmoida looppi näin:
Koodia: [Valitse]
function Kertaa () { MontakoKertaa=$1; shift; for n in $(seq $MontakoKertaa); do $@; done;}; Kertaa 1000000 echo $((1+1)
- toki funktion voi laittaa koneessaan tiedostoon bashrc jolloin se on aina käytettävissä.
**
Oishan se kiva tietää mitä omassa koneessa tapahtuu? Yksi tähänsopiva käsky:
Koodia: [Valitse]
clear; echo -n 'viimeksi käsitellyt 40 kotikansiosi tiedostoa esittämättä piilotiedostoja:';find ~ -type
f  -printf '%T@ %p\n' | grep -v '/\.'| sort -n | tail -40 | cut --output-delimiter='  ' --complement -c 11-$((29+${#USER}))
**(dd of=~/kansio1)
Vaikka loopit ovat oleellisia rakennusosia myös BASH-skripteissä ei BASH-koodiin saa looppeja tehdä, vaan aina tulee käyttää sitä "automaatti-looppia" joka on awk, sed, grep ja useissa BASH-käskyissä sisäänrakennettuna. Sillä BASH-koodilla toteutetut loopit hidastavat skriptiä usein kymmenesosaansa ja koodia saa näpytellä lisää monta riviä.
Myös nämä "automaatti-loopit" osaavat hyödyntää regex:iä.

Esimerkiksi tiedoston jokaisen rivin ensimmäisen kentän tulostaminen:
Koodia: [Valitse]
awk '{print $1}' tiedosto

tai tiedostossa jokaisella rivillä jokaisen numeron muuttaminen merkiksi: ¤:
Koodia: [Valitse]
sed 's/[0-9]*/¤/g' tiedosto
tai kun halutaan tulostaa matriisin pisimmän jäsenen pituus voi toteutus olla seuraavanlainen:
Koodia: [Valitse]
function matriisin_pisin_jäsen () { echo $1 | tr ' ' '\n' | wc -L ;}; matriisi=(aaaa a aaaaa aa); matriisin_pisin_jäsen "$(echo ${matriisi[*]})"
**
Miksi vältän pakettivaraston ohjelmien käyttämistä:

Kaikki pakettivaraston ohjelmat ovat samanlaisia käskyjä ja jotkut niistä on vain ladattu jo boottaamisen yhteydessä - sekin vielä vaihtelee että mitkä paketit ovat milloinkin sellaisia että Ubuntun kehittäjät pitävät niitä tarpeeksi arvokkaina ladattaviksi boottaamisen yhteydessä. Elikkä usein monien pakettivaraston pakettien käyttämistä ei edes voi välttää.

Mutta suuri osa niistä ohjelmista joita ei ole ladattu koneeseen jo boottaamisen yhteydessä ovat jotenkin huonoja: esimerkiksi ne ainoastaan esittävät käyttöjärjestelmän keräämiä tietoja teoriassa paremmassa ulkoasussa. Mutta joskus esitys onkin huononnus ja paketti lisäksi toimii virheellisesti. Tällaisia paketteja tarkoitan sillä jos pakettivaraston ohjelma ei toimi oikein niin asialle ei ole tehtävissä mitään. Esimerkiksi levyn sarjanumeron esittäminen: käsky: lshw on todella hidas ja pakettivaraston miljoona muuta tähän tarkoitettua ohjelmaa toimivat kaikki poikkeuksetta jossain suhteessa väärin ja esittävät tiedot omalla tavallaan eikä niiden esitystapaa voi helposti muuttaa. Koska käyttöjärjestelmä kerää jokatapauksessa kaiken tarvittavan tiedon ei "itsetehdyn" skriptin tarvitse tehdä muuta kuin esittää ne kerätyt tiedot. Esimerkiksi tuo sarjanumeron esittäminen:
Koodia: [Valitse]
(dd of=~/kansio1)

function levyid () { apu=$(ls -lh /dev/disk/by-id | grep -v wwn- | grep $1$ | awk '{print $9}'); echo Liitäntä:${apu%%-*}; apu2=${apu#*-}; echo Levytyyppi:${apu2%_*}; echo Sarjanumero:${apu##*_};}; levyid sda
- samantien skripti esittää muutakin asiaankuuluvaa.
- pakettivaraston ohjelmien räätälöiminen on yleensä mahdotonta, mutta koska skriptit ovat aina lähdekoodia on niiden mukaeleminen aina mahdollista.
**
Käyttöjärjestelmä ei kirjoita kovalevylle suoraan, vaan kirjoitettava suuntautuu ensiksi RAM:miin ja sieltä käyttöjärjestelmän toimesta levylle. Siten vaikka levylle tietoa siirtyy levylle aina jokseenkin samalla nopeudella niin levylle kirjoittavan ohjelman kannalta levylle kirjoittaminen on alkuunsa nopeaa mutta jatkuessaan kauemmin saattaa laskea todella pieneksi. Tästä saa selvän kuvan seuraavan käskyn tulosteesta:
Koodia: [Valitse]
cat /dev/zero | pv > ~/delme

tämä kirjoittaa kotikansion tiedostoon nimeltään: delme eikä sen luomisesta tarvitse huolehtia. Siirron lopettamiseksi sulje pääte. Muuten minulla on nopea SSD-levy SATA2 liittimessä ja sillä kirjoitusnopeus muuttuu vielä mielenkiintoisemmin kuin pyörivällä levyllä. USB:lle kirjoitetaan minun koneessani käskyllä:
Koodia: [Valitse]
cat /dev/zero | pv > /media/petteri/tikku/delme
- katso nautiluksella mikä polku johtaa koneessasi muistitikulle - alustetun muistitikun tulee olla kiinni USB:ssä.
- muuten kaikki muutkin tiedonsiirtoa vaativat toiminnot toteutetaan samalla tavalla, esimerkiksi verkosta lataaminen ja paperille tulostaminen.
- anna vaadittaessa se käsky: sudo apt-get install pv
- kovalevy itsekin harrastaa tämänkaltaista toimintaa - silläkin on RAM:mia (levy spekseissä nimenä on Cache) ja se kirjoitaa ensin sinne ja sieltä toinen prosessi siirtää sen levylle.

**
prosessorin yhdelle ytimelle saat sadan prosentin kuorman käskyllä:
Koodia: [Valitse]
dd if=/dev/zero of=/dev/null
prosessorin kahdelle ytimelle saat kummallekin täyden kuorman käskyllä:
Koodia: [Valitse]
dd if=/dev/zero of=/dev/null | dd if=/dev/zero of=/dev/null
- samalla menetelmällä saa täyden kuorman niin monelle ytimelle kuin haluaa. Kuormat tippuu pois kun sulkee päätteen. Jos on varomatun niin prosessori saa lämpöhalvauksen,
- kaikille ytimille saat täyden kuorman käskyllä:
Koodia: [Valitse]
(dd of=~/kansio1)
apu=''; for i in $(seq $(grep -c ^processor /proc/cpuinfo)); do apu=$apu' '$(echo -n dd if=/dev/zero of=/dev/null' | '); done; eval "${apu:0:${#apu}-2}"
- mutta mikäli siirto tapahtuu samalla levyllä voi kåyttää seuraavantyyppistä "salaman-nopeaa" käskyä:

mkdir kansio1 kansio2; for i in img_[0-9]*\.jpg; do ln $i kansio1/$i; ln $i kansio2/$i; done; rm img_[0-9]*\.jpg
 

Toki muitakin keinoja prosessorin rasittamiseksi löytyy. Esimerkiksi käsky:
Koodia: [Valitse]
yes $(yes)
ei kylläkään kuormita ytimiä sataa prosenttia, mutta se alkaa myös syödä RAM:mia ja jos onni on oikeellaan niin se alkaa kuluttamaan swap:piakin. Varsin nopeasti se kylläkin tekee prosessorin olon niin tuskaiseksi että prosessori tappaa koko pääte-prosessin. Mutta kerkiäähän JärjestelmänValvonnassa kuitenkin seurata tapahtumia minuutin verran.

Prosessorin kuormittamiseen on syy: se kertoo aina kuinka järjestelmä kestää rasitusta ja sen avulla voit tarkistaa muuttuuko ytimien kellotaajuus ja toimiiko lämpötilan mittaus.
**
kopioitaessa yksi suurikokoinen tiedosto moneen paikkaan samallakertaa kannattaa käyttää seuraavankaltaista käskyä:
Koodia: [Valitse]
pv tekstiä | tee >(dd of=~/kansio1) >(dd of=~/kansio2) | dd of=/dev/null
- kopioitavia tiedostoja voi lisätä kuinkamonta vaan ja silti kopioinnin edistymisen osoitus koskee koko joukkoa. Uusi kansio johon kopioidaan lisätään lisäämällä ryhmä: >(dd of=~/kansioN)
- kopiointi voidaan suunnata samaĺla kertaa myös USB:lle (ja kaiketi myös verkkoon?)
- kopionti kylläkin kestää yhtä kauan kuin normaalistikin, sillä kopiointia rajoittaa se että usein kaikkien tiedostojen kopiointi kulkee samaa väylää pitkin ja tuo väylähän kyllästyy kopioitaessa mitä tiedostoa hyvänsä. Mutta nopeus kyllä kasvaa mikäli kopiot kulkevat eri väyliä pitkin: (joku monista sata-väylistä)/USB/verkko .... 
**
Kun joutuu korjaamaan konetta jonka koti-kansiossa on sekaisin suuri määrä jpg-kuvia ja tavallisia tiedostoja niin ne kuvat kannattaa siirtää KUVAT-kansioon ja poistaa ne koti-kansiosta. Koska sama sekasotku muodostuu varmasti uudelleen niin kannattaa varautua siirtämään uudet kuvat KUVAT-kansioon sillointällöin. Käytännössä siirto-käsky on esimerkiksi tällainen:
Koodia: [Valitse]
rsync --remove-source-files --info=progress2 -t ~/img_[0-9]*\.jpg ~/KUVAT
**
Jos ne lukemattomat kuvat täytyy siirtää moneen paikkaan niin se onnistuu seuraavantyyppisellä käskyllä:
Koodia: [Valitse]
for i in img_[0-9]*\.jpg; do cp $i kansio1; mv $i kansio2; done
- siihen on hankala saada minkäänlaista etenemisen osoitusta
- mutta mikäli siirto tapahtuu samalla levyllä voi käyttää seuraavantyyppistä "salaman-nopeaa" käskyä tiedostojen siirtämiseksi toiseen/toisiin kansioihin:
Koodia: [Valitse]
mkdir kansio1 kansio2; for i in img_[0-9]*\.jpg; do ln $i kansio1/$i; ln $i kansio2/$i; done; rm img_[0-9]*\.jpg
- siis silloinkin kun mv vie aikaa kaksi tuntia niin edellinen vie 100 ms. Vaikka kyse onkin "silmänkääntö-tempusta" niin käyttäjän kannalta tämä on silti verinen-totuus.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 31.12.15 - klo:23.29
Aloinpa tonkia muistoja; matikanopettaja oli aikoinaan ihastunut senkaltaisiin ongelmiin kuin "pienimmän yhteisen jaettavan" laskemiseen. Jotkut ovat tehneet siitä BASH-ohjelman joka on kooltaan kunnioitettava ja toiminnaltaan hidas. Samaan tuntuu pääsevän yhdellä nopeatoimisella käskyllä:
Koodia: [Valitse]
L1=10;L2=24;echo $(( $L1*$L2/$(comm -12 <(factor $L1 | tr ' ' '\n' | grep -v :) <(factor $L2 | tr ' ' '\n' | grep -v :))))
- siis PYJ lasketaan luvuista L1 ja L2.
- tämä on ontuen toimiva periaate ja siitä puuttuvat pillit ja viheltimet.
- tätähän BASH on: kokeiluja täysin hyödyttömillä asioilla, monia nöyryyttäviä epäonnistumisia ja itsensä voittamista.
**
samankaltaisesti lasketaan myös "suurin yhteinen tekijä":
- myös tähän lisäsin koodin joka lisää 1:n luvun tekilöiden joukkoon
Koodia: [Valitse]
L1=110;L2=11;echo $(comm -12 <(echo 1;factor $L1 | tr ' ' '\n' | grep -v : | sort) <(echo 1;factor $L2 | tr ' ' '\n' | grep -v : | sort)) | tr ' ' \* | bc
**
jokainen Ubuntu-BASH tuntee ilmanmuuta käskyn: factor   joka jakaa sille annetun luvun alkutekijöihinsä.
 
- ilmeisesti linuxia kasaamassa on ollut todellisia virtuooseja. Ja jotkut ovat saaneet kiksejä kun ovat uskotelleet näille virtuooseille että joku hölmö asia on ehdottoman tärkeä. Tätä paljon pahempi on laskentaohjelma bc:n Besselin funktiot. Tärkeitähän ne saattaa olla, mutta niiden käyttäminen ja tuloksien tulkinta vaativat hyvää matemaatikkoa. Vaikka onhan se niinkin että aika on rapauttanut hyvää hommaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Tomin - 01.01.16 - klo:06.17
Aika näppärää, tosin tuo ei toimi ihan kaikissa tilanteissa:
Koodia: [Valitse]
$ L1=10;L2=11;echo $(( $L1*$L2/$(comm -12 <(factor $L1 | tr ' ' '\n' | grep -v :) <(factor $L2 | tr ' ' '\n' | grep -v :))))
bash: 10*11/: lauseoppivirhe: odotettiin operandia (virheellinen avainsana on ”/”)
$ L1=45;L2=11;echo $(comm -12 <(factor $L1 | tr ' ' '\n' | grep -v :) <(factor $L2 | tr ' ' '\n' | grep -v : )) | tr ' ' \* | bc
$
Oikea vastaus olisi yksi, mutta sitä ei tulosteta, koska factor ei anna ykköstä vastauksena. Kaikki laskut joissa on alkulukuja heittävät virheen tai eivät palauta mitään. Tosin silloin vastaus on ilmeinen ilman skriptiäkin.

Muokkaus: Näköjään tuo epäonnistuu vielä dramaattisemmin, jos L1 = L2:
Koodia: [Valitse]
$ L1=10;L2=10;echo $(( $L1*$L2/$(comm -12 <(factor $L1 | tr ' ' '\n' | grep -v :) <(factor $L2 | tr ' ' '\n' | grep -v :))))
bash: 10*10/2
5: lauseoppivirhe lausekkeessa (virheellinen avainsana on ”5”)
$
Lisäksi kannattaa testata myös tapaus, jossa nuo kaksi lukua ovat samat alkuluvut.

Muokkaus 2: Hmmh:
Koodia: [Valitse]
$ L1=45;L2=150;echo $(( $L1*$L2/$(comm -12 <(factor $L1 | tr ' ' '\n' | grep -v :) <(factor $L2 | tr ' ' '\n' | grep -v :))))
bash: 45*150/3
5: lauseoppivirhe lausekkeessa (virheellinen avainsana on ”5”)
$

Taitaa tarvita vielä vähän lisää koodia ja testitapauksia. :)

Muokkaus 3: Tämä toimii vähän paremmin tuon suurimman yhteisen tekijän laskemiseen, mutta sekin tekee vielä väärin:
Koodia: [Valitse]
$ L1=45; L2=150;echo $(comm --nocheck-order -12 <(uniq <(factor $L1 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L1)) <(uniq <(factor $L2 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L2))) | tr ' ' \* | bc
15
$ L1=110; L2=11;echo $(comm --nocheck-order -12 <(uniq <(factor $L1 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L1)) <(uniq <(factor $L2 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L2))) | tr ' ' \* | bc
1
$
Alemman pitäisi olla 11.

Muokkaus 4: Aika hauskaa:
Koodia: [Valitse]
$ L1=11; L2=11;echo $(comm --nocheck-order -12 <(uniq <(factor $L1 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L1)) <(uniq <(factor $L2 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L2))) | tr ' ' \* | bc
11
$ L1=10; L2=10;echo $(comm --nocheck-order -12 <(uniq <(factor $L1 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L1)) <(uniq <(factor $L2 | sed 's/^[0-9]*:/1/' | tr ' ' '\n'; echo $L2))) | tr ' ' \* | bc
100
$
Alempi on väärin. Ehkäpä annan tämän nyt olla. ;D
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 01.01.16 - klo:09.23
Pikainen paikkaus. Mutta eiköhän vielä ilmene kaikenlaista - ja lopuksi varmaankin löytyy toinen ratkaisu joka on nopea ja virheetön:
Koodia: [Valitse]
L1=10;L2=11;echo $(($L1*$L2/$(echo $(comm -12 <(echo 1;factor $L1 | tr ' ' '\n' | grep -v :) <(echo 1;factor $L2 | tr ' ' '\n' | grep -v : )) | tr ' ' \* | bc))) | bc
kuvittelin että tässä on korjaus myös tilanteille joissa luvuilla on useampia yhteisiä tekijöitä kuten esimerkiksi: 6 ja 24. Se lasketaan kyllä oikein mutta samalla urputetaan vaikka syytä ei ole. Kai se virhe tässä minun laskussani on, mutta epäilen kyllä myös että käsky: comm horisee omiaan sillä muodostuva matriisi on moitteeton.
**
Ei tämä vieläkään ihan kunnossa ollut: Kun comm urputtaa: "tiedosto 2 ei ole lajitellussa järjestyksessä" niin se tarkoittaa että järjestys:
1
2
5
11 
ei kelpaa vaan järjestyksen tulee olla se koneen mielestä järjestyksessä oleva:
1
11
2
5   
Siis käskyyn täytyy lisätä vielä käsky: sort :
Koodia: [Valitse]
L1=110;L2=11;echo $(($L1*$L2/$(echo $(comm -12 <(echo 1;factor $L1 | tr ' ' '\n' | grep -v : | sort) <(echo 1;factor $L2 | tr ' ' '\n' | grep -v : | sort)) | tr ' ' \* | bc))) | bc
tai jos haluaa päästä eroon tuosta bc:stä:
Koodia: [Valitse]
L1=24;L2=6;echo $(($L1*$L2/$(($(echo $(comm -12 <(echo 1;factor $L1 | tr ' ' '\n' | grep -v : | sort) <(echo 1;factor $L2 | tr ' ' '\n' | grep -v : | sort)) | tr ' ' \* )))))
**
Jatkuvasti paheneva ongelma on se että kovalevyllä on saman tiedoston kopioita siellä-täällä ja kopioiden nimet voivat vaihdella. Tai yksi kopio voi olla jpg-muodosssa ja toiset joissain muussa muodossa. Tai kopioituun tekstiin on on voitu lisätä yksi välilyönti tai kuvassa jotakin pikseliä muutettu - ei ole mitään mahdollisuutta tehdä ohjelmaa joka osaisi erottaa onko kohde ainut kappale vai onko siitä lukemattonia melkein samoja kopioita. Asialle on kuitenkin pakko tehdä jotakin.

Mutta ennenkuin tekee mitään voi tarkastaa kuinka paha tuo ongelma omassa koneessa on, sillä pelkkä tarkastaminen ei tuhoa mitään. Esitettävä komentorivi-käsky ei läheskään kaikkia duplikaatteja löydä, mutta onhan tämä nopea keino ylimalkaiseen tarkistamiseen. Tarkistuskäsky on:
Koodia: [Valitse]
find ~/ -not -path '*/\.*' -type f 2>/dev/null  -exec md5sum '{}' ';' | sort | uniq --all-repeated=separate -w 66 | cut \
-c 35- > /tmp/delme; apu=0; cat /tmp/delme | while read rivi; do [[ -f "$rivi" ]] && apu=$(($apu+$(echo $(du -b "$rivi" \
| awk '{print $1}'))/2)); echo $apu; done
ja se ilmoittaa likimääräisesti duplikaattien viemän kovalevytilan sanoina
- tiedostot merkitään samoiksi kun niiden sisällöt ovat bitti-bitiltä samat. Kopion nimestä ei välitetä eikä siitä missä kansiossa kopio on. Tiedosto voi olla koodattu tai kryptattu mitenkä vaan kunhan kopio on muodostettu samalla tavalla.
- kopiotiedostojen nimiä voit tarkistella käskyllä: gedit /tmp/delme. Siellä on tiedostonimi-ryhmiä. Jokaisen ryhmän edessä ja perässä on tyhjä rivi. Jokaisessa ryhmässä on niin monta riviä kuin samansisältöisiä tiedostoja on - siis yksittäisiä rivejä ei ole.
- leikkaa-ĺiimaa käsky täältä päätteeseesi ja paina RETURN. Käskynsuoritus saattaa kestää useampia minuutteja.
- ~ paikalle voi laittaa tarkemman määrityksen siitä mistä duplikaatteja etsitään - esimerkiksi: ~/KUVAT
- tulostuva luku on duplikaattien viemä talletustila byteinä.
**
Edellä esitetty etsintämenetelmä toimii tekstitiedostojenkin kanssa, mutta tekstitiedostoja editoidaan usein ja vaikka tekstistä korjaisi vain yhden kirjaimen on sen md5sum  täysin erilainen eikä esitetty etsintä-menetelmä toimi, vaan etsimiseen täytyy käyttää käskyä:
Koodia: [Valitse]
grep "tiedostoille ominainen sana tai rivi" mistä_kansiosta_alkaen_etsitään -R | sed "s#:#\n#" | sed 's#^\/#\n&#'
- tulosteen 1 rivi on: sen tiedoston nimi josta etsitty löytyi
- tulosteen myöhemmät rivit ovat kuvaus siitä mitä löydetyllä rivullä on
- lopuksi tulostetaan tyhjä rivi jotta tuloste olisi helppolukuinen
**
- Kun teksti-tiedostoja on todella paljon löytää etsimänsä tiedoston parhaiten kun määrää sieltä hakutermiksi monta tekstinpätkää - samalta tai eri riveiltä - tekstinpätkät voivat olla yksi tai monta välilyöntien eroittamaa sanaa. Tässä käsky-versiossa kaikkien määriteltyjen tekstien kirjainten tulee olla täysin samat kuin on kirjoitettu, mutta sanojen edessä ja jäljessä saa olla tekstiä. Hakutermien pitää löytyä oikeassa järjestyksessä mutta sensijaan ei ole väliä sillä kuinka monta rivinsiirtoa niiden välissä on ja onko lohkon sisällä muutakin tekstiä. Esimerkiksi kun etsii kovalevyltä runoa:

marilla oli karitsa
sen villa lumivalkoinen

minne mari menikään
karitsa meni perässään

mutta ei muista siitä kuin palasia sieltä-täältä niin voidaan käskeä esimerkiksi näin:
Koodia: [Valitse]
grep -RPzl  '(?s)'"marilla".{0,99\}"karitsa".{0,99\}"villa lumivalkoinen".{0,99\}"perässään" mistä_kansiosta_etsintä_aloitetaan
**
- mikäli halutaan erottaa tiedostosta siinä olevat esimerkiksi BEGIN-END lohkot onnistuu se seuraavalla käskyllä:
cat -e /boot/grub/grub.cfg  | grep -Poz  '(?ism:BEGIN.*?END)' | sed 's/END/&\n/g'
**
Vihaan selityksiä sillä totuus on kaikille erilainen. Mutta pakko tässä on nyt selitellä:

Linuxissa on erinomaiset haku-työkalut. Niiden käyttö jää erittäin pieneksi sillä jotta haku-työkaluja voisi käyttää niin tarvitaan jotakin mistä hakea ja sitä ei yhdessäkään koneessa paljoakaan ole ennenkuin itse hakee. Esimerkiksi internet on täynnä esimerkkejä joten sieltähän niitä voi hakea - tosin vain jotkut noista esimerkeistä toimivat ja niistäkin harvoissa on mitään sellaista mistä kannattaa ottaa oppia. Kuitenkin niitäkin on rajattomasti ja niitä on syytä koneeseensa hakea jos haluaa jotain oppia.
- näinhän monet toimivatkin. Mutta toiminta hiipuu ajankuluessa sillä oikeastaan kukaan ei ole saanut ratkaistua ongelmaa: kuinka vanhasta roskasta pääsee eroon, joten kone täyttyy esimerkeistä joista "on imetty kaikki mehu" ja ne ovat enää taakka. Mutta ratkaisuksi ei kelpaa se että heittää kaiken vanhan roskikseen sillä joukossa on myös sellaisia kulta-jyviä joita ei mistään löydä.
- osittaisratkaisuna kannattaa harkita mihin kansioon internetistä hakemansa sijoittaa. Haetusta on heti tehtävä skripti ja dokumentti.
- vielä suurempi vaikeus on asioiden nimeäminen sillä tiedon löytää parhaiten sen nimellä. Ja jos nimeät jonkun alussa väärin muuttuukin nimen vaihtaminen jatkossa nopeasti mahdottomaksi. Ja väärin nimettyä on vaikea löytää.

Kerätyistä esimerkeistä voi sitten käydä katsomassa kuinka jokin asia on toteutettu seuraavankaltaisella käskyllä:
Koodia: [Valitse]
IFS=$'\n'; for n in $(find mistä_kansiosta_alkaen_etsitään -type f -not -path "*mistä_ei_etsitä*"); do cat -e "$n" | grep -Poz  '(?ism:case.*?esac)' | sed 's/esac/&\n/g'; done; unset IFS
- siis käskyssä käydään rekursiivisesti läpi kaikki tiedostot määrätystä kansiosta alkaen mutta ei etsitä jostain ja tulostetaan niistä kaikki case-esac lohkot.
- käskyn käyttö ei rajoitu BASH-skripteihin eikä edes ohjelmointiin
- toki itsekin vasta opettelen ja esimerkeissäni on joskus paljonkin mätää. Mutta kaikesta saa toimivan, siihen voimme luottaa.
**
Rekursiivinen haku kertoo missä saattaa olla jotakin - siis se on samantapainen kuin googlen antana hakulista.
Esimerkiksi haluttaessa tuloste niistä tiedostoista joissa puhutaan IPv4 osoiteista käsketään:
Koodia: [Valitse]
grep '(([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' mistä_kansiosta_alkaen_etsitään -PRw | grep -v ".*mistä_kansiosta_tuloksia_ei_esitetä.*" | sed "s#:#\n#" | sed 's#^\/#\n&#'
- tulosteessa on jokaisesta osumasta kaksi riviä: ensimmäisellä on polku ja toisella: <rivino:>löytynyt rivi
- nimittäin koneelle on mahdotonta sanoa onko 1.1.1.1 ipv4-osoite, kirjan kappale tai jotakin ihan muuta.
- jokaista löytöä vastavan kaksi-rivisen tulosteen perässä on tyhjä rivi eroittamassa löytöjä.
**
Maksimin laskemiseksi awk on nopea ja se käsittää positiiviset, negatiiviset, kokonaisluvut, liukuluvut ja tieteellisen esitysmuodon. Mutta desimaaleja se hyväksyy vain jotain 19. Toteutukseltaan se on:
Koodia: [Valitse]
a=(1 12 3 4 5 6e2 7 88 9); echo -e ${a[@]/%/\\n} | awk 'BEGIN {maksimi=$1} {n if ($1>maksimi) maksimi=$1 } END { print maksimi }'
Kun tarvitaan enemmän desimaaleja niin seuraava askel onkin rajoittamaton tarkkuus, siis desimaalien luku määräytyy automaattisesti niin suureksi kuin tarvitsee. Mutta laskenta on kymmenen-kertaa hitaampi. Mutta taas kaikenlaiset luvut hyväksytään. Sen toteutus on:
Koodia: [Valitse]
function max () { apu=$1; for n in $@; do [[ $(echo $apu'>'$n | bc) -eq 0 ]] && apu=$n; done; echo $apu ;}; a=(3.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333 3.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334); max ${a[*]}
**
BASH:in regex valitsee suurimman sopivan palasen jollei muuta määrätä:
Koodia: [Valitse]
echo a9b8c7d6e | grep -o [0-9].*[0-9]   
tämä tulostaa: 9b8c7d6

Mikäli halutaan valita pienin palanen niin käsky muuttuu:
Koodia: [Valitse]
echo a9b8c7d6e | grep -oP [0-9].*?[0-9]tämä tulostaa kaksi palasta:
9b8
7d6
 
-tätä ? lisämäärettä voidaan käyttää  * , + , ? ja {} merkkien kanssa (hienoilla sanoilla: tekee greedystä lazyn ).
- merkkiryhmä {} on ainoastaan 'placeholder' find-käskyssä - ei siitä voi olla kyse. Ja netin yksikään hakukone ei osaa sitä etsiä. Turhauttavaa.
**
Pitipä tulostaa tiedostosta siinä olevat ipv4-osoitteet ja osoitteiden edessä oleva osoitteen merkityksen kuvaus. Ratkaisu-esimerkiksi:
Koodia: [Valitse]
echo "osoitteen 1 kuvaus%1.1.1.1 osoitteen 2 kuvaus:2.2.2.2 osoitteen 3 kuvaus 3.3.3.3 'osoitteen 4 kuvaus'4.4.4.4 osoitteen 5 kuvaus'5.5.5.5'"\
 | grep -Po '.*?\b(([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b'
- ipv4-regex on muuten mielestäni täydellinen.

- tiedostoa käsittelevä versio:
Koodia: [Valitse]
grep -Po '.*?\b(([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b' <<< $(cat tiedosto | tr -d '\n')
- tuloste ei muutu vaikka osoitteen kuvaus alkaisi yhdellä riivillä jä loppusi toisella
**
Edellinen lause löytää kyllä kaikki osoitteet ja osoitteiden kuvaukset, mutta osoitteiden kuvauksiin jää usein niin paljon asiaankuulumatonta että tulosteesta tulee hyvin vaikeaselkoinen. Yksi menetelmä liirum-laarumeiden rajoittamiseksi on muodostaa tuloksista matriisi jonka jäseniä sitten tulostettaessa rajoitetaan.
Jotta esityksestä saisi selvemmän on se parasta esittää kunnollisena skriptinä:
Koodia: [Valitse]
#!/bin/bash
# Matriisin muodostaminen tiedoston ipv4-osoitteista ja niiden kuvauksista:
IFS=$'\n'
matriisiosoitteista=($(grep -Po '.*?\b(([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b' \
<<< $(cat TIEDOSTONIMI | tr -d '\n'))); unset IFS

# Matriisin jäsenien tulostaminen rajoittaen jokaisen jäsenen pituus rajoittamalla siinä olevan ipv4-osoitteen kuvaus yhteen lauseeseen
# (siis ipv4-osoitetta edeltävään pisteeseen jos pistettä kuvauksessa onkaan):
for (( n=0; n<=${#matriisiosoitteista[@]}; n++ )); do
  jasen=${matriisiosoitteista[$n]}; pisteita=0; for (( pisteenpaikka=${#jasen}; pisteenpaikka>1; pisteenpaikka-- )) ; do
  [[ ${jasen:$pisteenpaikka:1} = \. ]] && (( pisteita++ )); [[ $pisteita = 4 ]] && break; done; echo ${jasen:$pisteenpaikka}
done

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 26.01.16 - klo:08.28
Tieteellisessa esitysmuodossa olevien lukujen kunnollinen vertaaminen osoittautui vaikeaksi - awk toimii hyvin ja nopeasti mutta se on rajoitettu 19 numeroon. Ja matematiikka ei toimi jos sallitaan rajoituksia - varsinkaan sellaisia joita ei tarvitse sallia.
Tällä skriptillä ei ole rajoituksia verrattavien lukujen suhteen vaan se toimii kaikkien ratkaistujen reaalilukujen kanssa riippumatta numeroalueesta, desimaalien määrästä tai esitysmuodosta. Tieteellisen esitysmuodon lukujen vertailu toimii suuruusluokkaan  e-50 asti ja jos se ei riitä niin sehän on muutettavissa. Tieteellisessä esitysmuodossa voi käyttää pientä tai isoa E:tä.
Sovellustavasta riippuen skriptejä voi käyttää myös kattona tai lattiana (katosta ei mennä yli ja lattiasta ei mennä ali).
Koodia: [Valitse]
function minimi () { [[ $(echo "scale=66; ${1//[Ee]/*10^}>${2//[Ee]/*10^}" | bc) -eq 0 ]] && echo $1 || echo $2 ;}; minimi 1.0000003e-50 1.0000002E-50
Koodia: [Valitse]
function maximi () { [[ $(echo "scale=66; ${1//[Ee]/*10^}<${2//[Ee]/*10^}" | bc) -eq 0 ]] && echo $1 || echo $2 ;}; maximi 1.0000003e-50 1.0000002E-50

- vertailu muuten onnistuu olipa numerojärjestelmä mikähyvänsä alle 10-kantainen. Sisääntulevan suurin numerojärjestelmä on 16 ja sillä vertailu:
Koodia: [Valitse]
function minimi () { [[ $(echo "ibase=16; ${1^^}>${2^^}" | bc) -eq 0 ]] && echo $1 || echo $2 ;}; minimi ffffe ffffd 
- voi nuo hexat kirjoittaa isollakin.

Kaikilla lukujärjestelmillä on desimaalit. Mutta bc:ssä lasketaan hexadesimaali-numeroiden desimaalit väärin . Esimerkiksi lasku: "echo 'ibase=16; .F' | bc" tulostaa .9 vaikka pitäisi tulostaa .9375 . Ja lasku ei oikaistu millään muulla kuin käymässä laskemassa desimaalien kokonaisluku-arvosta; siis kummanssakin verrattavassa kokonaisosa ja desimaalit lasketaan eritavoin. Lisäksi muutamat varmistukset ja päädytään seuraavaan aikaavievään laskuun (tätä laskua siis sovelletaan vain kun lasketaan hexadesimaali-lukuja joissa saattaa olla desimaaleja. ):
Koodia: [Valitse]
#!/bin/bash
# lisäys: | tr -d '\\\n'   bc:n perässä tekee bc:n tulostuksesta yhden numeron jossa ei ole kenoja eikä rivinsiirtoja
function minimi () {
apu1=${1^^}; desi1=${apu1##*.}; [[ $(echo $apu1 | grep .) ]] || desi1=0; koko1=${apu1%%.*}; koko1=${koko1:-0}; len1=${#desi1} # verrattavan1 paloittelua
sign1=1; [[ ${koko1:0:1} = - ]] && sign1=-1 && koko1=${koko1:1} && [[ $koko1 = '' ]] && koko1=0                               # verrattavan1 paloittelua   

apu2=${2^^}; desi2=${apu2##*.}; [[ $(echo $apu2 | grep .) ]] || desi2=0; koko2=${apu2%%.*}; koko2=${koko2:-0}; len2=${#desi2} # verrattavan2 paloittelua
sign2=1; [[ ${koko2:0:1} = - ]] && sign2=-1 && koko2=${koko2:1} && [[ $koko2 = '' ]] && koko2=0                               # verrattavan2 paloittelua

verrattava1desimaalisena=$(echo "scale=99; $(echo "scale=99;ibase=16; $koko1" | bc | tr -d '\\\n' )"+\
$(echo "scale=99;ibase=16; $desi1" | bc | tr -d '\\\n' )/$(echo "scale=99;16^$len1" | bc | tr -d '\\\n' ) | bc | tr -d '\\\n' )

verrattava2desimaalisena=$(echo "scale=99; $(echo "scale=99;ibase=16; $koko2" | bc | tr -d '\\\n' )"+\
$(echo "scale=99;ibase=16; $desi2" | bc | tr -d '\\\n' )/$(echo "scale=99;16^$len2" | bc | tr -d '\\\n' ) | bc | tr -d '\\\n' )
echo -e $verrattava1desimaalisena'\n'$verrattava2desimaalisena # tmän rivin eteen voi kirjoittaa merkin # kun tulosteesta haluaa siistimmän.

[[ $(echo $verrattava1desimaalisena*$sign1'>'$verrattava2desimaalisena*$sign2 | bc ) -eq 0 ]] && echo $1 || echo $2 ;};
minimi -.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc
**
Kyllä BASH itsekin kykenee näihin vertailuihin - tosin se kykenee ilman apua vertaamaan vain alle 14 numeroa pitkiä positiivisia ja negatiivisia hexadesimaalilukuja. Niissä voi olla 14 desimaaliakin, mutta tieteellinen esitysmuoto ei onnistu:
Koodia: [Valitse]
function minimi () {
apu1=${1^^}; desi1=${apu1##*.}; [[ $(echo $apu1 | grep .) ]] || desi1=0; koko1=${apu1%%.*}  # verrattavan1 paloittelua
[[ ${koko1:0:1} = - ]] && [[ ${koko1:1:1} = '' ]] && koko1=-1                               # verrattavan1 paloittelua   
apu2=${2^^}; desi2=${apu2##*.}; [[ $(echo $apu2 | grep .) ]] || desi2=0; koko2=${apu2%%.*}  # verrattavan2 paloittelua
[[ ${koko2:0:1} = - ]] && [[ ${koko2:1:1} = '' ]] && koko2=-1                               # verrattavan2 paloittelua
desimaalienvertailuoperaattori='<'; [[ ${koko1:0:1} = - ]] && [[ ${koko2:0:1} = - ]] && desimaalienvertailuoperaattori='>' 
if [ $koko1 = $koko2 ]; then (( $((16#$desi1)) $desimaalienvertailuoperaattori $((16#$desi2)) )) && { echo $1; return ;} ||  { echo $2; return ;}; fi
(( $((16#$koko1)) < $((16#$koko2)) )) && { echo $1; return ;} ||  { echo $2; return ;} ;}
minimi -fffffffffffffff.ffffffffffffffd fffffffffffffff.fffffffffffffff

- kyllä tällä voi verrata kymmenjärjestelmässäkin.
**
Mutta tosiaan awk-sovellus on helpoin ja nopein myös maximin/minimin etsimisessä:
Koodia: [Valitse]
echo '.ffffffffffffffd .fffffffffffffff' | awk '{ if ($1<$2) print $1 ;else print $2}'
Pilkunviilaus osoitti että siinäkin on virhetoiminto:
Koodia: [Valitse]
echo 'f d' | awk '{ if ($1<$2) print $1 ;else print $2}'    # tulostaa d
echo '-f -d' | awk '{ if ($1<$2) print $1 ;else print $2}'  # tulostaa -d vaikka pitäisi tulostaa -f
- desimaaliluvuilla toiminta on moitteetonta. Hexadesimaaliluvuilla täytyy käyttää käskyä:
Koodia: [Valitse]
echo 'dddd ffff' | awk '{ if ($1>0 && $2>0) if ($1<$2) print $1 ;else print $2; else if ($1>$2) print $1 ;else print $2 ;}'
- siis siinä vaihdetaan vertailumerkki arvosta: "<" arvoon: ">" mikäli molemmat luvut ovat negatiivisia.
**

**
Koneesi ipv6-osoitteet saat käskyllä:
Koodia: [Valitse]
ip -6 addr
   
Kun saat käsiisi jotain jonka pitäisi olla ipv6-osoite voit testata sen käskyllä:
Koodia: [Valitse]
function ipv6testi () { [[ $(echo $1|tr -d :/[[:xdigit:]]) = '' && $(echo $1|tr -dc :|wc -c)<7 && $(echo $1|grep -o '[[:xdigit:]]*'| \
wc -L)<5 ]] && echo osoite on kelvollinen || echo osoite on kelvoton ;}; ipv6testi 1::1111:1/128 # esimerkkiosoite

Kyllä ipv6:lle on regex-kin ja sitä kannattaa käyttää, sillä se on nopea ja ilmeisesti virheetönkin; kunpa se vain ei olisi noin pitkä:
echo 1:1:1:1:1:1:1:1111 | grep -P "^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*$"


 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 07.02.16 - klo:14.16
Kyllä ipv6:lle on regex-kin ja sitä kannattaa käyttää, sillä se on nopea ja ilmeisesti virheetönkin; kunpa se vain ei olisi noin pitkä:
echo 1:1:1:1:1:1:1:1111 | grep -P "^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*$"

grep -P:n kanssa toimii tällainen viritys:

Koodia: [Valitse]
^\s*(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$)){8,})((?1)(?>:(?1)){0,6})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}:|(?!(?:.*[a-f0-9]:){6,})(?3)?::(?>((?1)(?>:(?1)){0,4}):)?)?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?4)){3}))\s*$
Lähde: http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses/1934546#1934546
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.02.16 - klo:08.57
grep -P:n kanssa toimii tällainen viritys:

Koodia: [Valitse]
^\s*(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$)){8,})((?1)(?>:(?1)){0,6})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}:|(?!(?:.*[a-f0-9]:){6,})(?3)?::(?>((?1)(?>:(?1)){0,4}):)?)?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?4)){3}))\s*$
Lähde: http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses/1934546#1934546

Toimii loistavasti, tattista.
**
Halusin tietää onko satunnaislukujen muodostaminen edelleenkin huonoa. Helppo tapa tulostaa satunnaisluku väliltä: 0-joku_pienehkö_numero  on käsky:
Koodia: [Valitse]
echo $(( RANDOM*joku_pienehkö_numero/32768 ))
- satunnaislukujen muodostamisessa 16-bittisyys ei tunnu olevan pahin heikkous. Jokatapauksessa annetaan sitten 31-bittinen versio; se on aina hitaampi mutta ehkäpä se on joskus myös parempi:
Koodia: [Valitse]
echo $((((RANDOM<<15)|RANDOM)*40/1073741824))

Muodostettujen satunnaislukujen laatua voi tarkastella käskyllä : 
Koodia: [Valitse]
unset apu; declare -A apu; for n in {1..100000}; do let apu[$(( RANDOM*40/32768 ))]++ ; done ; echo ${apu[*]} | awk 'BEGIN {RS=" "}{sum+=$1; sumsq+=$1*$1}END {printf "%s\n", "keskipoikkeama="sqrt(sumsq/NR - (sum/NR)^2)"    keskiarvo="sum/NR}'
- tässä joku_pienehkö_numero on 40 ja muodostettavien satunnaislukujen määrä on  100000. Tarkoituksena on seurata kuinka niiden muuttaminen vaikuttaa kun käskyä jatkuvasti toistetaan.


Koska epäilin että maximi- ja minimiarvot saaattavat olla liian usein samoja niin tein niiden tarkistusta varten skriptin joka tuloste on seuraavankaltainen: 
assosiatiivisen matriisin keskipoikkeama=172.567    keskiarvo=25000
assosiatiivisen matriisin minimi        =24632 ja sitä vastaava osoite:14
assosiatiivisen matriisin maximi        =25352 ja sitä vastaava osoite:15

skriptistä tuli seuraavanlainen:
Koodia: [Valitse]
#!/bin/bash
# petteriIII 10.2.2016
unset asso; declare -A asso
function MatrixMin () { eval echo \${$1[*]} | awk 'BEGIN { min="";RS=" "}{ if (min=="") min=$1; if ($1<min) min=$1 }END{ print min}' ;} # toimii tavallisille sekä yksi- että "moniulotteisille" assosiatiivisille matriiseille.
function MatrixMax () { eval echo \${$1[*]} | awk 'BEGIN { max="";RS=" "}{ if (max=="") max=$1; if ($1>max) max=$1 }END{ print max}' ;} # toimii tavallisille sekä yksi- että "moniulotteisille" assosiatiivisille matriiseille.
for n in {1..100000}; do let asso[$(( RANDOM*40/32768 ))]++ ; done ; echo ${asso[*]} | awk 'BEGIN {RS=" "}{sum+=$1; sumsq+=$1*$1}END {printf "%s\n", "assosiatiivisen matriisin keskipoikkeama="sqrt(sumsq/NR - (sum/NR)^2)"    keskiarvo="sum/NR}'
min=$( MatrixMin asso)
echo -n 'assosiatiivisen matriisin minimi        ='$min' ja sitä vastaava osoite:';for (( n=1; n<=${#asso[@]}; n++ )); do [[ ${asso[$n]} = $min ]] && echo $n && break; done
max=$( MatrixMax asso)
echo -n 'assosiatiivisen matriisin maximi        ='$max' ja sitä vastaava osoite:';for (( n=1; n<=${#asso[@]}; n++ )); do [[ ${asso[$n]} = $max ]] && echo $n && break; done

- epäilys osoittautui vääräksi. Tosin laskin vain kymmenkunta sadanmiljoonan ryhmää koska homma on niin hidasta.
- syy skriptin esittämiseen on se että nuo käskyt: MatrixMin ja: MatrixMax rikkovat  vanhoja luuloja taas hieman lisää.
- jonkun_pienen_luvun ollessa yli 15000 tuloksissa alkaa esiintymään kummallisuuksia. Kerrostumista?
---------
Mutta kaikkien tämänkaltaisten tehtävien tulosten tarkistamisessa plottaaminen on niitä parhaita keinoja. Plottasinkin seuraavan skriptin mukaani:
Koodia: [Valitse]
#!/bin/bash
unset matriisi
for (( n=1; n<=1000000000; n++ )); do let matriisi[$(( RANDOM*40/32768 ))]++ ; done # 1000 000 000 eli miljardi vastaa aikana yli 8 tuntia.
minimi=$( echo ${matriisi[*]} | awk 'BEGIN {minimi=9e99} { if ($1<minimi) minimi=$1 } END { print minimi }' )
for x in {1..39}; do echo $x" "$( echo "100*(${matriisi[$x]}-$minimi)/$minimi" | bc -l ); done | gnuplot -p -e 'set terminal pngcairo size 350,262 enhanced font "Verdana,10"; set output "satunnaisluvut.png"; set ylabel "poikkeama %"; set xlabel "satunnasluku"; plot "/dev/stdin" with lines'
- 16-bittisellä satunnaisluvun kehittämistavalla ovat kehitetyissä satunnaisluvuissa havaittavaa toistoa joka selviää jo miljardilla mittauksella. 31-bittisellä satunnaisluvun kehittämismenetelmällä toistoa ei voi havaita.
- 16-bittinen satunnaisluku -> $(( RANDOM*40/32768 ))
- 31-bittinen satunnaisluku -> $((((RANDOM<<15)|RANDOM)*40/1073741824)) . 31-bittinen on vain mitättömän vähän hitaampi kehittää.

- satunnaislukuja tehdään miljardi, joten yksi mittaus kestää noin 8 tuntia.
- tulokset liitteenä tämän postauksen lopussa
- sekin selvisi, että konetta voi täysin hyvin käyttää samanaikaisesti muihin hommiin, myös ajaa toisia skriptejä.
---------
Skriptien toimintaa tutkittaessa verrataan usein niiden tekstimuotoisia tulosteita. Tällöin tulosteet täytyy suunnata näytön lisäksi myös tiedostoon. Skriptiin ei lisätä mitään sillä aina ei skriptiin voi kirjoittaa mitään tai se voi olla työlästä ja pitäisi perua myöhemmin. Skriptin tulostuksen voi suunnata näytön lisäksi myös tiedostoon lisäämällä skriptin käynnistyskäskyn perään > >(tee tuloste) . Siis esimerkiksi:
Koodia: [Valitse]
. ~/mun_skriptini > >(tee tuloste)      tai: bash mun_skriptini > >(tee tuloste)
jolloin tulostus löytyy myös kotikansion tiedostosta: tuloste josta se kannattaa heti kopioida muiden tulosteiden perään tai jotakin. Tulosteessa on joskus kirjoituskelvottomia merkkejä jolloin se on näennäisesti lukukelvoton mutta kyllä gedit silti osaa sen muut merkit lukea.
- tulosteet kannattaa lisätä heti tulostusten yhteenvetoon käskyllä:
Koodia: [Valitse]
cat <(echo -e '\nseuraava tuloste '$(date)) tuloste >> yhteenveto
**
Aloinpa taas kerran pähkäillä kuinka todeta kahden tiedoston ero. Käsky diff toimii toki ihan hyvin, mutta sen tuloksia joutuu miettimään eikä sen tuloksia ole helppo jatko-käsitellä joten käytän awk:ia.
Samantien voi käyttää awk-käskyä jonka tuloksia ei voi käsittää väärin sillä se kirjoittaa tulosteeseen mitä on verrattu:
Koodia: [Valitse]
clear;awk '{apu=FILENAME; if (!FILENAME++) nimi=nimi apu " "}END{printf "\nmitä sellaista on " substr(nimi,1,index(nimi, " ")-1)":ssä \
mitä ei ole"substr(nimi,index(nimi, " "),99)"\b:ssa\n"} NR == FNR {file1[$0]++; next} !($0 in file1) ' tiedosto2 tiedosto1
- vaihda perästä: tiedosto2 tiedosto1  muotoon:tiedosto1 tiedosto2 jos ei vertaa oikeinpäin.
- tiedostojen nimet saavat olla mitävain, vaikka: mutterivarasto lista_nyt_tarvittavista_muttereista

Siihen kuinka katsotaan "mitä samanlaista tiedostoissa on" sopii käsky:
Koodia: [Valitse]
clear;awk '{apu=FILENAME; if (!FILENAME++) nimi=nimi apu " "}END{printf "\nmitä samanlaista on tiedostoissa: " substr(nimi,1,index(nimi, " ")-1)"  ja: \
"substr(nimi,index(nimi, " "),99)"\n"} NR == FNR {file1[$0]++; next} ($0 in file1) ' tiedosto2 tiedosto1

--
Ehkäpä kannattaisi lähteä ratkaisemaan tätä tiedostojen vertailua toista kautta. Alku olisi seuraava:
Koodia: [Valitse]
grep -Fxvf tiedosto1 tiedosto2
- nimittäin grepistä on myös repoista löytyvä versio agrep joka hyväksyy määrättävän kokoisia eroja.
--
Matriisejakin voi "vertailla"
- soveltaen myös joukkoja, onhan bash:issa matriisi=(joukko) 
- nämä skriptit on viisainta esittää täydellisinä esimerkkeinä sillä kun nämä alkaa sotkeutua niin loppua ei tule
- jäsenet voivat olla numeroita, tekstiä tai heittomerkkien ympäröimiä lauseita

1. mitä ensimmäisessä matriisissa on sellaista mitä ei toisessa ole
Koodia: [Valitse]
#!/bin/bash
matriisi2=( a1 a2 "nämä ovat heittomerkeissä" x a2 a4 a5 a6 a\nb a7 a8 x2 ) # a2 on kaksi kertaa
matriisi1=( x2 a3 a4 b5 a6 "nämä ovat heittomerkeissä" "rivi jossa on välilyöntejä täytyy laittaa heittomerkkien väliin")
grep -Fxvf <(echo -e ${matriisi1[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) <(echo -e ${matriisi2[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort)
2. mitä toisessa matriisissa on sellaista mitä ei ensimmäisessä ole
Koodia: [Valitse]
#!/bin/bash
matriisi1=( a1 "nämä ovat heittomerkeissä" x a2 a4 a5 a6 a\nb a7 a8 x2 ) # a2 on kaksi kertaa
matriisi2=( x2 a3 a4 b5 a6 "nämä ovat heittomerkeissä" "rivi jossa on välilyöntejä täytyy laittaa heittomerkkien väliin")
grep -Fxvf <(echo -e ${matriisi1[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) <(echo -e ${matriisi2[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort)
3. mitä yhteistä kahdella matriisilla on
Koodia: [Valitse]
#!/bin/bash
matriisi1=( a1 "nämä ovat heittomerkeissä" x a2 a4 a5 a6 a\nb a7 a8 x2 ) # a2 on kaksi kertaa
matriisi2=( x2 a3 a4 b5 a6 "nämä ovat heittomerkeissä" "rivi jossa on välilyöntejä täytyy laittaa heittomerkkien väliin")
grep -Fxf <(echo -e ${matriisi1[@]/%/\\n} | sort) <(echo -e ${matriisi2[@]/%/\\n} | sort)
4. mitä jäseniä kahdella matriisilla yhteensä on: (se ei aina ole sama kuin matriisien summa)
Koodia: [Valitse]
#!/bin/bash
matriisi1=( a1 "nämä ovat heittomerkeissä" x a2 a4 a5 a6 a\nb a7 a8 x2 ) # a2 on kaksi kertaa
matriisi2=( x2 a3 a4 b5 a6 "nämä ovat heittomerkeissä" "rivi jossa on välilyöntejä täytyy laittaa heittomerkkien väliin" )
matriisi3=("${matriisi1[@]}" "${matriisi2[@]}")
echo -e ${matriisi3[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort
5. mitä jäseniä kuuluu vain jompaankumpaan matriisiin muttei kumpaankin
Koodia: [Valitse]
#!/bin/bash
matriisi1=( a1 a2 "nämä ovat heittomerkeissä" x a2 a4 a5 a6 a\nb a7 a8 a2 x2 ) # a2 on kaksi kertaa
matriisi2=( x2 a3 a4 b5 a6 "nämä ovat heittomerkeissä" "rivi jossa on välilyöntejä täytyy laittaa heittomerkkien väliin")
comm -3 <(echo -e ${matriisi1[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) <(echo -e ${matriisi2[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) | awk '{$1=$1}1'
# tässä tehtävässä grep olettaisi että suurempi matriisi on ensimmäisenä. Ei käy sillä tässä vaatimus johtaisi vain väärinkäsityksiin.
# tätä voi käyttää myös matriisien yhtäsuuruuden tarkistamiseen - tai pikemminkin sen tarkistamiseen onko matriiseissa samat jäsenet sillä jäsenien järjestyksellä ei ole väliä.
**
Skriptin osia voidaan erottaa omaksi kokonaisuudekseen suluilla - esimerkiksi kun haluaa painottaa missä järjestyksessä suluissa olevat tehtävät tulee suorittaa - konekin tarvitsee niitä joskus mutta ohjelmoija voi määrätä niitä selventääkseen toimintaa itselleen. Siis ihan niinkuin matematiikassakin. Mahdolliset sulkutyypit ovat: ( tehtävä; uusi tehtävä; uusi tehtävä ... ) ja:{ tehtävä; uusi tehtävä; uusi tehtävä ... }. Niiden erot:

1. { ... } suoritetaan tehtävät samassa prosessissa kuin missä ollaan joten se on paljon nopeampi. Globaali-alue on yhteinen, joten parametrit siirtyvät kumpaankin suuntaan. Myös toiminnan sivuvaikutukset jäävät rasittamaan omaa prosessia.

2. ( ... ) ensin luodaan uusi prosessi ja ajetaan käskyt siinä. Onhan se on paljon hitaampaa, mutta sivuvaikutukset nollautuvat kun palataan omaan prosessiin. Koska globaali-alue on uuden prosessin oma niin parametrien palautus ei toimi - funktioon päin parametrit kyllä siirtyvät ihan hyvin.

- käytännössä funktio luodaan aina seuraavasti: functio funktion-nimi () { ... }. Mutta tuo äsköinen pätee funktioihinkin. Siten:
apu=0; function nimi() { apu=1 ;} ; nimi; echo $apu  # tulostaa 1
mutta:
apu=0; function nimi() ( apu=1 ;) ; nimi; echo $apu  # tulostaa 0 . Yksi keino saada apu:n arvo palautettua on palauttaa se kovalevy-tiedoston kautta:
apu=0; function nimi() ( apu=1; echo "$apu">/tmp/delme ;) ; nimi; apu=$(cat /tmp/delme); echo $apu
- onhan tämä hidasta ja inhan näköistä, mutta kutsuva prosessi ei kärsi vaikka kutsuttu hölmöilisi.


Esimerkiksi seuraavassa funktio asettaa muuttujan arvon globaali-alueella. Funktion ja pääohjelman globaali-alueiden tulee siis olla samat jos halutaan niiden välistä tiedonsiirtoa; elikä on pakko käyttää rakennetta { ... } :
Koodia: [Valitse]
#!/bin/bash
function lue_näppäimistöltä () { echo "annapa $1"; read $1 ;}

# samassa paikassa luetaan kaksi muuttujaa jotta olisi selvää että palautetaan oikean muuttujan arvo. 
lue_näppäimistöltä muuttuja1
lue_näppäimistöltä muuttuja2

echo -e "\n\nmuuttujalle1 annettu arvo: "$muuttuja1
echo "muuttujalle2 annettu arvo: "$muuttuja2
 
- lopputulos on siis sama kuin palautettaisiin parametri.

Rakennetta ( ... ) käytetään esimerkki matematiikassa:
Koodia: [Valitse]
echo $((1+2*3))  tai: echo $(((1+2)*3))


Kumpaa hyvänsä sulutusta tulee käyttää monimutkaisemmissa logiikkalauseissa sillä muuten helposti toimitaan toisin kuin luullaan ja selventäähän se tapahtumia jokatapauksessa. Mutta päänsärkyä niistä monimutkaisuuksista vaan saa ja kannattaa mieluummin jakaa monimutkainen moneksi yksinkertaiseksi, esimerkiksi:
Koodia: [Valitse]
a=0; b=1; \
[[ $a == 1 && $b == 1 ]] && echo sekä a että b ovat ykkösiä;  \
[[ $a == 0 && $b == 1 ]] && echo a on nolla ja b on yksi;  \
[[ $a == 1 && $b == 0 ]] && echo a on yksi ja b on nolla;  \
[[ $a == 0 && $b == 0 ]] && echo sekä a että b ovat nollia


Siitä päänsärkyä aiheuttavasta ja monimutkaisesta ratkaisusta esimerkki:
Koodia: [Valitse]
a=0; b=1; [[ $a == 1 ]] && { [[ $b == 1 ]] && echo sekä a että b ovat ykkösiä ; : ; } || [[ $b == 0 ]] && echo sekä a että b ovat nollia
- muuten selvitys kohdasta: { [[ $b == 1 ]] && echo $c ; : ; } -> tuo : johtuu bash:in vaatimuksesta että sulkujen sisällä olevan lauseke ei saa olla pelkkä ehdollinen teko, vaan siinä täytyy tehdä jotain todellistakin, vaikka suorittaa nollakomento. Tuo perässä oleva ; taas johtuu siitä että ennen sulkevaa sulkua pitää olla joko ; tai rivinsiirto.
- tässävaiheessa kuvannollisesti päätä särki jo niin paljon että piti lopettaa.


 


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 20.02.16 - klo:07.00
Bash kehittyy pikkuhiljaa. Esimerkiksi aikaisemmin tiedostoon kirjoitetun 2D-matriisin sarake- ja rivikeskiarvojen laskemiseen oli pakko käyttää awk:ia sillä bash-totetus oli tuhat kertaa hitaampi ja suurien matriisien laskeminen kesti kymmeniä minuutteja. Mutta awk:in desimaalien lukumäärä on riittämätön, joten on pakko käyttää bc-laskentaohjelmaa. Bc:n ohjaamisessa tulee helposti tehtyä skriptistä etana.
- nämä skriptit toimivat pienillekin matriiseille hyvin, mutta vasta suurten matriisien laskennassa ne loistavat.
- bc:n tuloon voi syöttää lähes rajoittamattoman pituisen laskukaavan; gigantti-kokoisen matriisin laskennassa dataa saattaa olla sata a4-arkillista.

Nämä toteutukset ovat parhaimmillaankin 3-kertaa hitaampia kuin vastaavat awk-sovellukset. Mutta tuloksessa on nyt 66 desimaalia ja haluttaessa kuinka monta vaan.

Skriptien nopeus johtuu siitä ettei niissä ole käytetty bash:in looppi-käskyjä, vaan bash:in käskyihin sisäänrakennettuja looppeja. Myös sed:issä ja awk:issa on sisäänrakennettu looppi. Nämä sisäänrakennetut loopit ovat nopeita. Seuraavat skriptit ovatkin tosinopeita bash-skripteiksi: 
Koodia: [Valitse]
function sarakekeskiarvo() { lkm=$(cut -c -1 $1 | wc -w); echo $(echo 'scale=66;('$(cut -d ' ' -f$2 $1 | tr '\n' + | sed 's/+*$//')')/'$lkm | bc -l);}; sarakekeskiarvo tiedosto1 2
# lkm=$(cut -c -1 tiedosto | wc -w)    # viimeisen sellaisen rivin rivino jolla on jotakin välilyöntiä suurempaa
# cut -d ' ' -f$2 $1                   # erotetaan haluttu sarake
# | tr '\n' +                          # vaihdetaan rivinsiirrot + merkeiksi
# sed 's/+*$//                         # poistetaan perästä turhat + merkit
- sarakekeskiarvo lasketaan sarakkeesta 2

Koodia: [Valitse]
function rivikeskiarvo() { lkm=$(sed -n $2p $1 | wc -w); echo $(echo 'scale=66;('$(sed -n $2p $1 | tr ' ' + | sed 's/+*$//')')/'$lkm | bc -l);}; rivikeskiarvo tiedosto1 2
- rivikekeskiarvo lasketaan riviltä 2
------
Linux on levykäyttöjärjestelmä ja teoriassa matriisit ovat levyllä. Mutta toki on mahdollista suorittaa käsittely RAM:mista. Siihen soveltuu esimerkiksi seuraava:
Koodia: [Valitse]
function rivikeskiarvo() { readarray apu < $1; lkm=$( echo "${apu[@]}" | wc -l ); echo '('$(echo ' '"${apu[@]}" | cut -d ' ' -f3 | tr '\n' + | sed 's/+*$//g')')/'$(($lkm-1)) | bc -l  ;}; time rivikeskiarvo tiedosto1 2
- tuota alussa olevaa: "readarray apu < $1" tarvitaan vain jotta esimerkki toimisi ensimmäiselläkin kerralla ja sitä ei tarvita mikäli matriisi on jo muistissa.
- toiminta RAM:missa on kymmenkunta kertaa hitaampaa kuin toiminta levyllä. Piti nähdä ennenkuin uskoin. Ettei vaan jossain olisi matoja?
- osoittautukin ettei käsky: "readarray" tee normaalia matriisia vaan lisää tekemäänsä matriisiin jotakin.
- mutta se ei muutu että RAM:missa olevaa matriisia voi käsitellä - luultavasti lukemalla matriisi oikein ja tekemällä mutoksen aiheuttamat lisämuutokset nopeuskin nousisi mutta skriptistä tulisi tosipitkä.
- linuxin normaalit käskyt toimivat siis myös luettaessa RAM:mista.
**
Tuosta readarrayn omintakeisesta toiminnasta:
kirjoita levytiedostoon nimeltä koetiedosto:
1 2
3 4
Lue se sitten muistiin vanhalla menetelmällä ja tulosta heti:
Koodia: [Valitse]
i=0; while IFS=$'\n' read -r rivi; do matriisi[i]="${rivi}"; ((++i)); done < koetiedosto; echo ${matriisi[@]}
se tulostaa: 1 2 3 4
käske sitten:
Koodia: [Valitse]
i=0; while IFS=$'\n' read -r rivi; do matriisi[i]="${rivi}"; ((++i)); done < koetiedosto; echo "${matriisi[@]}"
se tulostaa: 1 2 3 4
käske sitten:
Koodia: [Valitse]
readarray matriisi < koetiedosto; echo ${matriisi[@]}
se tulostaa: 1 2 3 4
Käske sitten:
Koodia: [Valitse]
readarray matriisi < koetiedosto; echo " ${matriisi[@]}"
Se tulostaakin:
 1 2
 3 4
- siis ero tulee kun krjoitetaan tulostettava lainausmerkkeihin. Oikeastaan aika näppärääkin - mutta miksei siitä kerrota missään?
------
matriisin lukeminen tiedostosta vanhalla luotettavalla menetelmällä:
Koodia: [Valitse]
i=0; while IFS=$'\n' read -r rivi; do matriisi[i]="${rivi}"; ((++i)); done < tiedosto
tai:
i=0; cat tiedosto | while read rivi; do matriisi[i]="${rivi}"; ((++i)); done
matriisin lukeminen tiedostosta uudemmalla menetelmällä. Mutta tuo readarray tekee siis vähän muutakin:
Koodia: [Valitse]
readarray matriisi < tiedosto
matriisin lukeminen tekstijonosta:
Koodia: [Valitse]
matriisi=(1 2 3 4 5) 
matriisin lukeminen toisesta matriisista:
Koodia: [Valitse]
matriisi=("${apu[@]}")
matriisin lukeminen toisesta matriisista jättäen ne rivit pois joilla on valittava teksti jossainkohtaa (tässä esimerkissä 3.3):
Koodia: [Valitse]
matriisi=("${apu[@]/*3.3*/}")
matriisin lukeminen toisesta matriisista vaihtaen millähyvänsä rivillä määrättävän tekstin:
Koodia: [Valitse]
matriisi=("${apu[@]//menuentry/menuhih}")
joskus koodista tulee sangen sottaista:
matriisi2=("${matriisi[@]//*jotakin*/'rivi poistettu\n'}"); echo -e " ${matriisi2[@]}"
tai kun yritetään "negatiivista jokeria" elikä:
matriisi2=("${matriisi[@]//!(*jotakin*)/'rivi poistettu\n'}"); echo -e " ${matriisi2[@]}"
niin tulos ei ole ihan sitä mitä odotetaan - mutta tämähän onkin vain tutkijan apuväline eikä fakta
matriisien liittäminen: 
Koodia: [Valitse]
matriisi=("${apu[@]}" "${apu2[@]}")
tai:
matriisi+=("${apu[@]}")
 
**
Aloitinpa taas kertaalleen tutkia kuinka matemaattiset laskut suoritetaan "myrkyttämättä" ympäristöä. Ei siitä oikeastaan mitään oleellisesti uutta ilmennyt, mutta taas kertaalleen laskut nopeutuivat, kofor n in $(seq 1 $(cat tiedosto2 | awk NF | awk 'END{print NF}')); do  echo -n sarakkeen:$n' summa on:'; echo $(cat tiedosto2 | awk -v n=$n '{printf "%s",$n"+"}' | sed 's/sin[0-9]*/s(&)/g;s/sin//g;s/[Ee]/*10^/g;s/+*$//g' ) | bc -l; doneodi väheni romahtamalla ja uusien laskutapojen lisäämisestä tuli oleellisesti helpompaa. Kyllä tämä silti kaipaisi mahdottoman paljon tarkennuksia koodiin. Mutta kunhan tätä ideaa saa vielä parannettua niin mihinkähän päätyy:
Koodia: [Valitse]
#!/bin/bash
function laske () {
apu=$(echo $1 | sed 's/e/'$(echo "e(1)" | bc -l)'/g') #  grep -Po '(?<=\+)\+|-|\*|\/|\^|\%') tr -d
luku=$(echo $apu | tr -d '![[:alpha:]]' | sed 's/^+//g;s/++/+/g;s/--/+/g;s/-+/-/g;s,\/+,\/,g;s/\*+/\*/g;s/\^+/\^/g') # laskumerkit bc:n sääntöjen mukaisiksi
case $(echo $apu | tr -d '[0-9].+-') in 
 sin) echo "s($luku)" | bc -l;;
 cos) echo "c($luku)" | bc -l;;
 tan) echo $(echo "s($luku)/c($luku)") | bc -l;;
 sqrt) echo "sqrt($luku)" | bc -l;;
 \!)   echo $(seq -s* $luku | bc | tr -d '\\' | tr -d "\n");; 
 *)  [[ $(echo $luku | grep '\^.*\.') ]] && echo $(echo "e($(echo $luku | awk -F^ '{print $2}')*l($(echo $luku | awk -F^ '{print $1}')))" | bc -l) || echo "$(echo $luku)" | bc -l;;           
esac
}

# kokeita, niitä on paljon ja ne muuttuvat kokoajan
laske +2--2 # automaatti-järjestelmistä tällaisia laskuja tulee usein niin hölmö kuin lasku ihmisen mielestä onkin
laske !99
laske 2^2   # tuloksen pitää olla tasan 4 mikäli lasku kulkee oikeaa tietä - siis ei samaa kuin desimaali-exponenttiset             
laske 2.000001^2
laske 2^2.000001
toisaalta onhan tuolla parempikin viritelmä:  http://forum.ubuntu-fi.org/index.php?topic=47408.msg364003#top
**
- muuten bc:lle voi syöttää matriisinkin. Esimerkiksi:
Koodia: [Valitse]
a=10; b=0; for n in $(seq $a -1 $b); do echo "obase=2; $n" | bc; done

**
Yksinkertaisilla BASH-skripteillä voidaan suorittaa nopeasti kaikenmoisia tehtäviä. Otetaanpa esimerkiksi matriisin sarakesummat: miksi edes yrittää ratkaista jonkun rivin summaa kun voi ratkaista ne kaikki ja valita sitten lopputuloksesta oikean? Eipähän käytä vahingossa väärän sarakkeen summaa.
- muuten BASH ei  tehtävien määrän kasvaessa hidastu paljoakaan mikäli käyttää käskyjen sisäisiä looppeja sillä nehän ovat C-kielisiä - ja oikein koodattuja liäksi.
Koodia: [Valitse]
for n in $(seq 1 $(cat tiedosto2 | awk NF | awk 'END{print NF}')); do  echo -n sarakkeen:$n' summa on:'; echo $(cat tiedosto2 | awk -v n=$n '{printf "%s",$n"+"}' | sed 's/sin[0-9]*/s(&)/g;s/sin//g;s/[Ee]/*10^/g;s/+*$//g' ) | bc -l; done
Kovalevylle kirjoitetun matriisin sarakesummat lasketaan riippumatta siitä mitä matriisissa on: desimaalilukuja tavallisessa tai tieteellisessä esitysmuodossa, ratkaisemattomia sinejä tai mitä halutaankin, desimaaleja voi olla sisäänmenossa ja ulostulossa niin monta kuin haluaa ...
Matriisissa voi olla mielivaltaisen monia sarakkeita tai rivejä ja se otetaan automaattisesti huomioon. Ja toiminta on lisäksi nopeaa.
- tuloste on tähän tyyliin:
sarakkeen:1 summa on:2351232
sarakkeen:2 summa on:39168.0
sarakkeen:3 summa on:40320


Vastaava sarakesummien awk-skripti on kyllä kertaluokkaa nopeampi, mutta sen desimaalien määrä on maksimissaan vain noin 19:
Koodia: [Valitse]
awk NF tiedosto2 | awk '{for(i=1;i<=NF;i++) s[i]+=$i} END {for(i=1;i<=NF;i++) print "sarakkkeen: "i" summa=" s[i]}'
ja rivisummien:
Koodia: [Valitse]
awk '{for(i=1;i<=NF;i++) t+=$i; print "rivisumma riviltä:"NR,t; t=0}' tiedosto2

Pienin muutoksin skriptin tulosteesta saa  tämäntyyppisen:
sarakkkeen:1  summa=2041  keskiarvo=291.571   keskipoikkeama=697.466
sarakkkeen:2  summa=34      keskiarvo=4.85714   keskipoikkeama=2.70547
sarakkkeen:3  summa=35      keskiarvo=5              keskipoikkeama=2
 
skripti on tällainen:
Koodia: [Valitse]
awk NF tiedosto2 | awk '{for(i=1;i<=NF;i++) {sum[i]+=$i;sumsq[i]+=$i*$i}} END {for(i=1;i<=NF;i++) print "sarakkkeen:"i" summa=" sum[i]" keskiarvo="sum[i]/NR" keskipoikkeama="sqrt(sumsq[i]/NR - (sum[i]/NR)^2)}' | column -t
- tuo: "column -t" skriptin perässä uudelleen-formatoi niin hyvin että formatointi ei ihmiseltä onnistu koskaan yhtähyvin.
- hidashan tämä on: jos otat lämpötila- ja tuuli lukemia sekunnin välein vuodenverran niin huonolla koneella käsittely kestää noin 20sekuntia. Niin kestää muuten luku- ja talletuskin.

Mennyttä aikaa ? Eivät ongelmat nykyään tuolla tasolla ole? Kyllä ne ongelmien osatehtävät ovat, ainoastaan ongelmien lukumäärä on huomattavasti suurempi; eikä näin omaan käyttöön esiintulevissa ongelmissa kasvuakaan ole tapahtunut merkittävästi. Mikäli jokaisen pienen osa-onghelman ratkaisun löytäisi heti niin jopa BASH pystyisi ratkaisemaan sen isommankin ongelman..
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.03.16 - klo:07.04
- ihan ensiksi lataa gnuplot-ohjelma:
Koodia: [Valitse]
sudo apt get install gnuplot

Yksi kouriintuntuva menetelmä määrätynlaisen tiedon kuvaamiseksi on piirtää se. Kuvataanpa hommaa esimerkillä:
- jokainen esimerkki toimii ihan sellaisenaan ja niistä voi ottaa mallin omaan skriptiinsä: ensin datan muodostaminen ja sitten sen plottaaminen. Kumpikin vain yhdellä rivillä. Dataa kannattaa tutkia editorilla että saisi kuvan siitä kuinka oma data on muodostettava.
- ettet heti heittäisi kirvestä heti järveen niin annapa käsky:
Koodia: [Valitse]
gnuplot -e "plot sin(x)"
# tai peräti:
gnuplot -p -e 'set terminal postscript eps color enhanced; set output "sini.eps"; plot [-5:5] [-1.5:1.5] sin(x),  cos(x)' # kirjoittaa tiedoston kotikansioon
# ehkäpä 3D kiinnostaa:
gnuplot -p -e 'splot [0:3] [1:4] [-1:5] x*y-2'


- sitten muodostetaan koe-tiedosto (noin yksi jakso sini-käyrää):
Koodia: [Valitse]
for x in {1..628}; do echo $x" "$(awk -v x=$x 'BEGIN{print sin(x/100)}') >> /tmp/tiedosto; done
saadaksesi ensin varman kuvan siitä millainen tiedosto muodostui niin tulosta se ensin tekstinä:
Koodia: [Valitse]
cat /tmp/tiedosto
seuraavaksi plotataan se:
Koodia: [Valitse]
cat /tmp/tiedosto | gnuplot -p -e 'plot "/dev/stdin"'
- x-arvona on /tmp/tiedosto:n sarake1  ja y-arvona sarake 2 . Oletuksena on että x- ja y-arvojen välissä on välilyönti Jos muita sarakkeita on jätetään ne huomioimatta.
- ilmaantuvan piirroksen vasemmassa alakulmassa päivitetään hiiriosoittimen koordinaatteja
- tällä esitystavalla on helppo plotata matriisejakin ; siis levyltä ei lueta mitään:
Koodia: [Valitse]
for x in {1..628}; do mat[x]=$(echo $x" "$(awk -v x=$x 'BEGIN{print sin(x/100)}')) ; done; echo -e ${mat[@]/%/\\n} | gnuplot -p -e 'plot "/dev/stdin"'

- voi myöskin tehdä funktion plot_matrix. Siinä passataan vain matriisin nimi joten sitä voi käyttää mille hyvänsä matriisille:
Koodia: [Valitse]
function plot_matrix () { eval echo \${$1[*]} | tr ' ' '\n' | gnuplot -p -e 'plot "/dev/stdin" with lines' ;}; \    # tällä rivillä on itse funktio
for x in {1..628}; do apu[$x]=$(awk -v x=$x 'BEGIN{print sin(x/100)}'); done; plot_matrix apu                       # tällä rivillä on esimerkki matriisin muodostuksesta

Onhan tuolla gnuplotilla noita krumeluurejakin, esimerkiksi:
Koodia: [Valitse]
cat /tmp/tiedosto | gnuplot -p -e 'set title "sinikäyrä"; set ylabel "y"; set xlabel "x";plot "/dev/stdin"'
tai kirjoittaa piirros kovalevylle:
Koodia: [Valitse]
cat /tmp/tiedosto | gnuplot -p -e 'set terminal postscript eps color enhanced; set output "sini.eps"; plot "/dev/stdin"'
tai UbuntuSuomen foorumille kelpaavana:
gnuplot -p -e 'set terminal pngcairo size 350,262 enhanced font "Verdana,10"; set output "koe.png";set pointsize 1.0; set title "osumat"; set ylabel "y"; set xlabel "x";plot "~/fooruminstatistiikka" using 6 with lines'
- noita set-käskyjä voi laittaa niin monta kuin haluaa

ja tulosta voi tarkastella esimerkiksi käskyllä:
Koodia: [Valitse]
evince sini.eps

Mikäli x- ja y-pisteet ovat toisissa sarakkeissa kuin 1 ja 2 niin silloin käsketään:
Koodia: [Valitse]
cat /tmp/tiedosto | gnuplot -p -e 'set pointsize 1.0; set title "sinikäyrä"; set ylabel "y"; set xlabel "x";plot "/dev/stdin" using 2:3'



Kun halutaan plotata samaan kuvaan kaksi käyrää niin siihen on useampiakin menetelmiä, tässä kaksi:
Koodia: [Valitse]
# Käyrien datat ovat omissa tiedostoissaan:
rm -f /tmp/tiedosto1; for x in {1..628}; do echo $x" "$(awk -v x=$x 'BEGIN{print sin(x/100)}') >> /tmp/tiedosto1; done
rm -f /tmp/tiedosto3; for x in {1..628}; do echo $x" "$(awk -v x=$x 'BEGIN{print cos(x/100)}') >> /tmp/tiedosto3; done
cat /tmp/tiedosto1 <(echo -e '\n') /tmp/tiedosto3 > /tmp/tiedosto4
gnuplot -p -e 'plot "/tmp/tiedosto4" using 1:2 index 0, "" using 1:2 index 1'

# tai kun käyrien datat ovat samassa tiedostossa:
rm -f /tmp/tiedosto1; for x in {1..628}; do echo $x" "$(awk -v x=$x 'BEGIN{print sin(x/100)}')" "$(awk -v x=$x 'BEGIN{print cos(x/100)}') >> /tmp/tiedosto1; done
gnuplot -p -e 'set pointsize 1.0; set title "sini- kosini-käyrät"; set ylabel "y"; set xlabel "x";plot "/tmp/tiedosto1" using 2 title "Sini" with lines, "/tmp/tiedosto1" using 3 title "Kosini" with lines'
# - kun on tuontapainen määritelmä: "using 2" eikä "using 1:2" niin se tarkoittaa että x-arvona käytetään pisteen järjestysnumeroa.
# - koodi koostuu hieman eritavalla kuin aikaisemmissa esimerkeissä ettei siitä tulisi kamalan pitkä.
# - käyrien värit tulevat automaattisesti.Piirroksen oikeassa yläkulmassa kerrotaan mikä väri vastaa mitäkin käyrää. Värejä voi olla 8?
# - tämän käyrän pirros esitetty sivun alaosassa

-----
Edellisessä esimerkissä plotattavat arvot sopivat erinomaisesti automaatti-asetuksia varten. Mutta joskus plotattavat arvot ovat sellaisia, että niiden perusteella tapahtuvat automaatti-asetukset olisivat huonoja ja silloin voi kyseisen asetuksen määrätä. Muutama yleisin:
Koodia: [Valitse]
 
set xrange [-2*pi:2*pi]                                   # säätää x-alueen
set yrange [-1.5:1.5]                                     # säätää y-alueen

set xtics ('-2π' -2*pi, '-π' -pi, 0, 'π' pi, '2π' 2*pi)   # säätää akselimerkintöjä
set ytics 1                                               # säätää akselimerkintöjä
set tics scale 0.75                                       # säätää akselimerkintöjä
set xtics axis                                            # samassa esityksessä voi olla käyriä joilla on eri x. Mikä x-akseleista näkyy
set ytics axis                                            # samassa esityksessä voi olla käyriä joilla on eri y. Mikä y-akseleista näkyy

noborder                                                  # määrää ettei käyrän ympärille piirretä rajoja 

set format x "%2.0f"                                      # x-akselin merkintöjen esitysmuoto
set format y "%2.0f"                                      # y-akselin merkintöjen esitysmuoto
- tämä kaivannee esimerkkiä:
datan teko: rm -f /tmp/tiedosto1; for x in {1..628}; do echo $(awk -v x=$x 'BEGIN{print (x/314)}')" "$(awk -v x=$x 'BEGIN{print sin(x/100)}')" "$(awk -v x=$x 'BEGIN{print cos(x/100)}') >> /tmp/tiedosto1; done
datan plottaus: gnuplot -p -e 'set format x "%2.1f'pi'"; set pointsize 1.0; set title "sini- kosini-käyrät"; set ylabel "y"; set xlabel "x";plot "/tmp/tiedosto1" using 1:2 title "Sini" with lines, "/tmp/tiedosto1" using 1:3 title "Kosini" with lines'

- joskus täytyy muuttaa muutakin kuin set-lauseita: esimerkiksi joskus haluat kaksi käyrää samaan piirrokseen jotta saisit käsityksen siitä kuinka toisen käyrän arvot korreloivat toisen käyrän arvojen kanssa, mutta toisen käyrän esimerkiksi y-arvot ovat niin pienellä alueella ettei sen käyrän muutoksia näe samasta piirroksesta ihmis-silmin tai peräti toisen offset sekoittaa koko piirroksen. Tällaista tilannettta kuvaa esimerkki:
Koodia: [Valitse]
datan muodostus: rm -f /tmp/tiedosto1; for x in {1..628}; do echo $(awk -v x=$x 'BEGIN{print (x/314)}')" "$(awk -v x=$x 'BEGIN{print sin(x/100)}')" "$(awk -v x=$x 'BEGIN{print cos(x/100)/100+40}') >> /tmp/tiedosto1; done
datan plottaus: gnuplot -p -e 'set format x "%2.1f'pi'"; set pointsize 1.0; set title "sini- kosini-käyrät"; set ylabel "y"; set xlabel "x";plot "/tmp/tiedosto1" using 1:2 title "Sini" with lines, "/tmp/tiedosto1" using 1:3 title "Kosini" with lines axes x1y2'
- siis eron tekee tuo lopussa oleva: axes x1y2. Jos jätät sen pois et saa arvoista mitään selvää.

-------------------
Kuinka gnuplotilta kysytään neuvoa siitä kuinka käskyt rakennetaan:
- ensin mennään päätteeseen ja käsketään: gnuplot
- sitten annetaa käsky: help . Sieltä tulee niin paljon asiaa että sekoittaahan se vaan alkuunsa. Mutta annapa käsky: help plot. sieltä tulee taas paljon asiaa, mutta muun muassa:

gnuplotin syntaksista:
       plot {<ranges>}
            {<iteration>}
            {<function> | {"<datafile>" {datafile-modifiers}}}
            {axes <axes>} {<title-spec>} {with <style>}
            {, {definitions{,}} <function> ...}
- sitten vain antamaan käskyjä tyyppiä: help plot ranges
- siis vikana on ihan sama kuin Linuxissa kaikkialla: saat niin paljon apua että melkein tukehdut siihen. Mutta pari vuotta sitkeää yrittämistä niin kyllä se siitä.
----
gnuplot:illa on myös kyky interpolointiin. Muodostaakseesi koetiedoston anna ensin käsky:
Koodia: [Valitse]
echo -e '-111 -0.07\n-24 0.09\n62 0.12\n69 0.2\n86 0.7\n99 0.74\n101 0.69\n105 0.2\n120.403 -0.5848\n170 -0.353\n247.891 -0.105295\n321 0.0925' > /tmp/delme
ja sitten anna käsky:
Koodia: [Valitse]
gnuplot -p -e 'plot "/tmp/delme" u 1:2 smooth cspline, "/tmp/delme" with points'
- interpoloitu käyrä on punainen jatkuva kuvaaja. Vihreät pisteet ovat annettuja pisteitä.
- csplinen lisäksi tunnetaan:  unique, frequency, cumulative, cnormal, kdensity, acspline, bezier ja sbezier.
---
gnuplotilla on enemmän ominaisuuksia kuin jaksat edes lukea. Kyse onkin lähinnä siitä onko gnuplot ajantasalla.
gnuplotin versio 5.02 on tehty tammikuussa 2016. Ubuntun versio on 4.6.4 sillä Canonical ei suosi sitä että ihmiset tekevät itse.



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.03.16 - klo:09.18
Nyt en malta olla kertomatta commandlinefu.com:mista löytyneestä fibonac-sarjan jäsenien laskumenetelmästä:
Koodia: [Valitse]
echo 'n=92;m=(n+1)/2;a=0;b=1;i=0;while(m){e[i++]=m%2;m/=2};while(i--){c=a*a;a=c+2*a*b;b=c+b*b;if(e[i]){t=a;a+=b;b=t}};if(n%2)a*a+b*b;if(!n%2)a*(a+2*b)' | bc | tr -d '\\\n'
- tuon f(92):n laskeminen kestää 5 millisekuntia minun kelvottomalla läppärilläni - siis se on nopeampi kuin awk-skripti mikäli awk-skriptissä edes riittäisivät numerot.
- sillä voi laskea mielivaltaisen suuren fibonac:cin - kunhan sen numerot vain mahtuvat RAM:miin.  Tuo raja 92 on naurettavan pieni ja 92000 on parempi.
- se tuntuu laskevan oikein - tosin äkkiseltään löysin netistä fibonacit vain 1000:n asti. Ja oikeellisuushan on ehdottomasti tärkeintä.
- sen toimintaa ei käsitä, Mutta sama vikahan on jopa C-koodillakin: et tiedä mitä mielettömyyksiä se milloinkin tekee riehuessaan kirjastoissaan. Eikä kirjastot tiedä mitä raudan firm-ware ohjaa raudan tekemään.

Tässähä ei varsinainen matematiikka olekaan BASH:ia, vaan skripti-kieli bc:tä. Tämä on ensimmäinen varsinainen bc-skripti minkä olen kohdannut.

tai kuinka sen ottaa - täytyy myöntää etten ole mikään menestystarina tässä skriptaamisessa. Varmaankin tie tämmöisen ymmärtämiseen kuulkee niiden define-lauseiden kautta joista kyllä toitotetaan mutta joita en ole koskaan saanut toimimaan; yksi toimiva esimerkki riittäisi.

Lopultakin sain aikaan toimivan esimerkin tuosta define:stä:
Koodia: [Valitse]
#!/bin/bash
function eksponentiaatio () { echo "kantaluku=$1; eksponentti=$2

define int(kantaluku) { auto oldscale; oldscale = scale; scale = 0; kantaluku /= 1; scale = oldscale; return kantaluku ;}
 
# eksponentin ollessa desimaaliluku lasketaan eksponentiaatio kaavalla:  e^(eksponentti*log(kantaluku))
define power(kantaluku,eksponentti) { if (eksponentti == int(eksponentti)) return kantaluku^int(eksponentti) else return e(eksponentti*l(kantaluku)) ;}

power (kantaluku,eksponentti)" | bc -l ;} # bc:n funktiokutsut laitetaan tänne

eksponentiaatio 4 2.000001
**
Bc:lle voikin syöttää ohjelmakoodia. Olisikohan syntaksi C:n kaltainen ? Sillä:
Koodia: [Valitse]
echo 'n=93;n=(n+1)/2;n+1' | bc
tulostaa 48 niinkuin olettaa voikin
- ja voi se ohjelmakoodi tulostaa itsekin, kuten tässä jonka tarkoitus on muuttaa tulosteet muodosta: .1 muotoon 0.1 ja muodosta: -.1 muotoon -0.1
Koodia: [Valitse]
echo "x=-.1; if(x==0) print \"0.0\" else if(x>0 && x<1) print 0,x else if(x>-1 && x<0) print \"-0\",-x else print x" | bc

Myös tarkistus siitä onko luku tosiaan luku on parasta suorittaa kysymällä se bc:ltä:
Koodia: [Valitse]
[[ $(echo kirjoita_luku_tähän | bc 2>/dev/null) ]] && echo kyseessä on numero || echo kyseessä ei ole numero
- tarkistus bc:llä on aina selvä eikä tarvitse pähkäillä minkäsorttista lukua tarkistaa. Regexillä täytyy sillä:
Koodia: [Valitse]
luku=-9999; [[ $(echo $luku | grep -E '^[-+0-9][0-9]+$') ]] && echo "kokonaisluku"
luku=-1.9999; [[ $(echo $luku | grep -E '^[-+0-9]*\.[0-9]*$') ]] && echo "desimaaliluku"
luku=-0.9e-999; [[ $(echo $luku | grep -Po  '[-+]?[0-9]*\.?[0-9]+[eE][-+]?\d+') ]] && echo "tieteelisen esitysmuodon luku"


tai toisen asteen yhtälön juuret laskettuna bc:llä:
Koodia: [Valitse]
echo 'a=1; b=2; c=1; x1=(-b+(sqrt(b^2)-4*a*c))/(2*a) ; x2=(-b-(sqrt(b^2)-4*a*c))/(2*a); print "ensimmäinen juuri=";x1; "ja toinen juuri=";x2' | bc
- matikkani on unohtunut ja sitä en tiedä onko oikein; mutta korjaamallahan se lutviutuu. Mutta oikeastaan tässä vain selvennetään minkätyyppisien ongelmien ratkaisuun bc kykenee.
- siis tässä ei ole kyse toisen asteen funktion ratkaisemisesta vaan siitä että bc kykenee tulostamaan tekstiäkin.

kertoma laskettaisiin define määritelmin näin:
Koodia: [Valitse]
#!/bin/bash
function kertoman_laskeminen () { echo " x=$1; scale=0
define kertoma (x) { if (x <= 1) return (1);
                     return (kertoma(x-1) * x);}
kertoma (x)" | bc -l | tr -d '\\\n' ;}   
time kertoman_laskeminen 1000
- kertoman laskemisessa defineä ei kuitenkaan kannata tehdä, sillä ihan yhtä nopeaa on laskea näin: echo $(seq -s* x | bc | tr -d '\\\n')
- tosi tässä esitetty define on rekursiivinen. mutta muoto:
Koodia: [Valitse]
define kertoma (x) { m=1; for(i=1;i<x;i++) m=m*i; return (m);}
ei nopeuta yhtään.
- kertomat luvuista alueella 1-20 saa nopeimmin laskettua käskyllä: echo $(($(seq -s* luku)))

Tämmöisiähän 30-vuotta sitten oli kädessäpidettävissä funktiolaskimissa:
- kun annetaan rajat x1 ja x2 ja määritellään funktio niin integroidaan niiden ja x-akselin rajoittama pinta-ala ja piirretään määrityksistä myös käyrä:
Koodia: [Valitse]
#!/bin/bash
function tee_kippura () { echo "
define f(x) { return (e(-(x^2)));}
for(i=0;i<1;i=i+0.01) {print i;574372;f(i)}" | bc -l > /tmp/delme ; sed -i ':a;N;$!ba;s/574372\n/ /g' /tmp/delme ;}

function itegroi_pa () { echo "
define simpson(a,b,n) {
auto h,sum_even,sum_odd;
h=(b-a)/(2*n);
sum_even=0;
sum_odd=0;
for (i=1;i<=n;i++) sum_odd=sum_odd+f(a+(2*i-1)*h);
for(i=1;i<n;i++) sum_even=sum_even+f(a+2*i*h);
return ((f(a)+f(b)+4*sum_odd+2*sum_even)*h/3);}

define f(x) { return (e(-(x^2)));}

simpson(0,1,10)" | bc -l ;} # integroi PA:n väliltä 0-1. Ja 10:n palasta? arvo 100=> lasku kestää hieman kauemmin mutta arvo on oikeampi?

tee_kippura
itegroi_pa
gnuplot -p -e 'set pointsize 1.0; set title "PintaAla"; set ylabel "y"; set xlabel "x";plot "/tmp/delme" using 1:2 title "Raja" with lines'
- hankalakäyttöinenhän tuo vielä on eikä kannata kehittääkään sitä sujuvaksi sillä periaatehan tässä kiinnostaa.
**
Totuusarvot (Boolean) ovat yhtä vanhat kuin tietokonekin ja kyllä ne BASH:iinkin kuuluvat. Mutta nimitykset true ja false ovat tehty vasta senjälkeen kun BASH oli jo tehty ja niinpä BASH puhuu totuusarvoista numeroarvoina 1 ja 0. 
- itseasiassa mikähyvänsä nollasta eroavan totuusarvo on false (arvo on "nurinkurinen" koska käskyn ollessa toimiva sen virhekoodi on 0. Mikrosoft alkaessaan tehdä ohjelmisto-ympäristöä harkitsi kuinka parhaiten heittää kapuloita Linuxin rattaisiin ja BOOLEAN osastollakin kaikki käännettiin ympäri. Onnistuikin sabotaasissaan, ryökäle.)

Ja tämä tuntuu myös BOOLEAN matematiikassa: BASH:issa toimitaan numeroarvojen kanssa. Käytännössä tämä tarkoittaa sitä, että BASH:issa totuusarvot lasketaan kaarisuluissa ja hakasuluissa toimitaan tekstijonojen kanssa. Esimerkiksi: 
(( 0 )) && echo true || echo false  # tulostaa false: koska (( 0 )) on false niin suoritetaan || perässä oleva käsky.
[[ 0 ]] && echo true || echo false  # tulostaa true, koska katsottiin onko verrattavassa mitään tekstiä ja se ei merkitse mitään että teksti sattuu olemaa 0.

Esimerkkejä boolean-operaattorien käytöstä:
#!/bin/bash
a=1; b=1
(( $a & $b )) && echo "true" || echo "false"  # Muuttujien ja  (=and) . Tuo yksinäinen & kirjoitetaan usein &&, mutta siinä taidetaan sotkea kaksi asiaa vaikka se toimiikin)
(( $a | $b )) && echo "true" || echo "false"  # Muuttujien tai (=or)
(( $a ^ $b )) && echo "true" || echo "false"  # Muuttujien poissulkeva tai (=xor; tosi vain jos muuttujien totuusarvot ovat erilaisia)(siis BASH:issa ^=XOR ja **=potenssi)
- muuuttujan boolean-arvo muuttuu päinvastaiseksi kun sen eteen kirjoitetaan merkki: !
**
BASH:issa on myös ihan hyvät bitti-operaatiot. Eipä niitä juurikaan käytetä, mutta asianharrastajat kyllä tekevät niillä ihmeitä. Muutama esimerkki:

var=1; echo $((var <<= 2))     # tulostaa:4 . Siis desimaaliluvun 1 binääriesitystä 1 siirretään kaksi pykälää vasemmalle ja oikealta tulee nollia. Lopputulos esitetään desimaalisena.
tai:
var=4; echo $((var >>= 2))     # tulostaa: 1 . Pitää pitää mielessä ettei BASH:issa ole desimaalilukuja - ja BASH ei pyöristä vaan leikkaa. Samat desimaali/binääri/desimaali muunnokset kuin edellisessäkin.

var=5; ((var |= 7)); echo $var # tulostaa 7, sillä tuo yhtäkuinmerkki myös muuttaa var:n arvon.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 11.03.16 - klo:17.34
Lämpökameran datan plottaaminen gnuplot-ohjelmalla:
- syy siihen miksi laitan tämän tänne kun suurinpiirtein sama on verkkisivulla: http://gnuplot.sourceforge.net/demo/heatmaps.html
on se, että poikkeuksetta kaikilla verkkosivuilla olevat ohjeet ovat virtuoosien tekemiä toisia virtuooseja varten ja ne eivät koskaan toimi meille taviksille ihan noinvain, sillä niissä jätetään selvityksen "ensimmäinen pylväänväli" pois; ja sitäpaitsi ne eivät muutenkaan aina toimi Ubuntulla ihan semmoisenaan.

Koodia: [Valitse]
#!/bin/bash
# lämpökameran datan tulee olla seuraavassa muodossa: x-pixeli_no     y-pixeli_no    lämpöä_kuvaava_numero 
ja juuri niinkuin seuraavassa on ettei seuraisi vaikeuksia. Seuraavassa esimerkissä arvot kirjoitetaan tiedostoon

echo -e "
0 0 5
0 1 4
0 2 3
0 3 1
0 4 0

1 0 2
1 1 2
1 2 0
1 3 0
1 4 1

2 0 0
2 1 0
2 2 0
2 3 1
2 4 0

3 0 0
3 1 0
3 2 0
3 3 2
3 4 3

4 0 0
4 1 1
4 2 2
4 3 4
4 4 3" > /tmp/delme

# mitenkä tiedosto onkin muodostettu niin se plotataan seuraavalla käskyllä:
gnuplot -p -e 'plot "/tmp/delme" using 2:1:3 with image'

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.03.16 - klo:09.57
Kyllä BASH on verraton prototyypin teossa: piti määritellä onko luvun neliöjuuri kokonaisluku. Sen koodiksi tuli:
Koodia: [Valitse]
[[ $(echo $(echo "sqrt (kirjoita_luku_tähän)") | bc -l | grep -o '\.0*$')  ]] && echo luvun neliöjuuri on kokonaisluku || echo luvun neliöjuuri ei ole kokonaisluku
- siis tehdään raa'asti juuri niin kuin ongelma esitetään. Tuommoisissa ratkaisuissa on se hyvä puoli ettei salahautoja juurikaan ole.
- siis tuo grepin regex:  '\.0*$'    tarkoittaa: desimaalipisteen jälkeen välittömästi tulee 0 ja siitä eteenpäin kaikki merkit ovat nollia. 
- pilkunviilausta: kun kuulemma täytyy erottaa luvut: 152415789666209426002111556165263283035677489 ja: 152415789666209426002111556165263283035677490 niin koodiksi tulee
Koodia: [Valitse]
[[ $(echo $(echo "scale=66;sqrt (kirjoita_luku_tähän)") | bc -l | tr -d '\\\n' | grep -o '\.0*$')  ]] && echo luvun neliöjuuri on kokonaisluku || echo luvun neliöjuuri ei ole kokonaisluku
- tai itseasiassa luvussa voi olla neljä a4-arkillista numeroita - scale:a joutuu vain kasvattamaan.
**
Teoriassa Linux pitää kirjaa kaikesta mitä PC:ssä tapahtuu. Siis on aivan yksinkertaista tehdä ohjelma: "Kuka vieras riehuu koneessani ja kerropa vieraan jokainen teko ja tekemisen yritys"?

Vaan eipä ole. Kirjanpito löytyy ja sen on pakko olla kunnollinen jotta Linux toimisi. Mutta jollekulle ei sovi se että ensinnäkin vieraan tekemiset paljastetaan ja todella kauheaa olisi jos PC:n omistajat voisivat itsekin selvittää sen. Siten ohjelmia löytyy kyllä - versiota 0.02 vuodelta 1927 tai sentapaista.

Mutta yrittää voi - vaikka työkalut ovat alkujaankin tylsiä ja nykyään vielä ruostuneitakin. 

Kirjanpito löytyy suureltaosin kansiosta /proc. Siellä olevissa tiedostoissa on teoriassa selväkielisenä esimerkiksi tapahtumien lukumäärät. Mutta se on tehty silloin kun kovalevytilakin oli kallista ja toiminta hidasta, joten sen tiedoista on vaikeaa saada tolkkua. Mutta käytännössä tuo kansio onkin viisainta jättää rauhaan ja käyttää ohjelmia jotka poimivat sieltä tietoja. Niinkuin esimerkiksi ohjelma: hardinfo joka selvittää jo paljon. Esimerkiksi kun olet äskettäin bootannut PC:si ja et ole vielä avannut internet-selainta niin silti koneessasi on jo monia tyyppejä. Suurin osa ihan omasta määräyksestäsi sillä esimerkiksi kello hakee netistä sillointällöin tarkan ajan. Tai kun PC:si verkko on päällä mutta selain ei ole avattuna niin internet-tarjoajasi käy silti kurkkimassa ja tuopa mukanaan siipiveikkojakin.

Tai verkon tapahtumista keräät tietoa käskyllä nstat ja sillä voit todeta homman itsekin: boottaa PC:si mutta älä avaa internet-selainta vaan anna heti käsky:
Koodia: [Valitse]
while true;do nstat | grep IpInReceives | awk '{print $2}' ; done
ja seuraa kun verkosta tulee paketteja.
Siis liikenne on merkityksettömän pientä? Mutta pankkitunnus ja salasana eivät paljoa vaadikaan.
- tietenkin Järjestelmänvalvonta kertoo tämän myös. Mutta Järjestelmän valvonnalla on vaikea tehdä pitkänajan-yhteenvetoja.
------------
Ne konessasi kullakin hetkellä olevat siipiveikot saat listattua käskyllä:
Koodia: [Valitse]
apu2=''; while true; do apu1=$(sudo netstat -p | grep -v ^unix | grep -v ^Active | grep -v ^Proto | awk '{print $5}' ); [[ $apu1 != $apu2 ]] && date && echo -e $apu1 | tr " " '\n' && echo && apu2=$apu1; done
- aina kun koneessasi olevien siipiveikot muuttuvat annetaan uusi tuloste muotoa: tyhjä rivi, päiväys, luettelo siipiveikoista yksi rivillään. Tulostetta kylläkin tulee alkuunsa niin paljon ettei kuvaputki riitä.

Boottaa ja anna tuo käsky välittömästi: se ei saisi tulostaa mitään niin kauan kuin jaksatkin päätettä tuijottaa. Avaa sitten internet-selain ja lataa esimerkiksi Iltasanomat ja anna senjälkeen päätteessä tuo käsky. Tulostetta tulee paljon ja jokainen rivi edustaa yhtä iltasanomien siipiveikkoa. Internet tarjoajallasi on taas omat siipiveikkonsa. Parin minuutin kuluessa näytön pitäisi taas vähitellen tyhjentyä - mutta se johtuu siipiveikoista ja teoriassa huonosti käyttäytyvä siipiveikko voi jäädä asumaan päästyään koneellesi.

Sulje sitten internet-selain ja mene päätteeseen seuraamaan nstat:in tulostetta. Kyllä ne siipiveikot vielä siellä ovat - internet:hän on yhteydetön joten syyllinen tähän on selain. Siipiveikot häipyvät sitten parissa minuutissa. Toivottavasti. Melkein kaikki.

Kaikki nämä ovat tulostetta vain niistä jotka eivät kätkeydy; siis hyviksistä.

Linuxissa on vielä korkeammankin tason kirjanpito: lokitiedostot. Teoriassa niistä saa listan kaikesta koneen tapahtumista. Ja kyllähän virtuoosit sieltä löytävätkin melkeinpä mitävain. Mutta käytäntö tökkii; mikäli näin olisi ei kaikenmaailman netstatit kehittyisi. Mutta päinvastoin: lokit eivät kehity mutta netstat juhlii.
**
Täytyi tulostaa tiedoston viimeinen kappale - siis kappaleiden välissä on tyhjä rivi. Tehtävään on kyllä yksinkertaisia awk-skriptejä mutta ne ovat aina jotenkin omituisia. Hommaan voi kasata funktionkin:
Koodia: [Valitse]
function tulosta_vikakappale () { vika=$(cat "$1" | grep -Pn ^$  | cut -d: -f1 | tail -1); awk "NR>$vika" "$1" ;}  # kutsu: vika_kappale tiedosto
**
Minua on aina pänninyt että sleep-komento on niin virheellinen pienillä sleep-arvoilla: esimerkiksi "time sleep 1" näyttää 1.004 - ja niin kauan se muuten käytännössäkin suurinpiirtein kestää. Luultavasti koneessasi on kylläkin super-sleepin binääri, mutta herra yksin tietää missä se on ja jokatapauksessa sen käyttöönottaminen edellyttäisi kääntämistä. Mutta itseasiassa käytettävissäsi on parikymmentä-kertaa parempi ajoitus-käsky kuin sleep: se on yksinkertaisesti:
Koodia: [Valitse]
read -t aika-sekunneissa_0.001sekunnista_alkaen
- siis käsky: time read -t 1 näyttää: 1.000
- minun koneessani se kestää tarkemmissa mittauksissa 1.00022 sekuntia. Muuten käsky on aivan laillinen vaikka ei määrää muuttujaa - muuttujan nimi on silloin:  RESULT
- käytännön mittauksissa on usein pieniä odotusaikoja ja niiden virheellisyys tunkee kaikkeen. BASH on kelvoton ennenkaikkea siksi, että tämän on jätetty kelvottomaksi. Parikymmen-kertainen parannus ei vielä paljoakaan auta mutta hyvä alku se on. Millisekunnin pituisen viiveen höpertäminen miljoonassa mittauksessa:
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 15.04.16 - klo:13.49
Tulipa mieleen, että päivämäärän tarkistamiseen tarkoitettu regex joutuu käyttämään grepin hidasta kytkintä -P, se on suurikokoinen ja vaikeasti ymmärrettävä mikäli tarkoitus on ottaa huomioon se kuinka monta päivää kussakin kuussa on. Mikäli on tarkoitus ottaa huomioon sekin että onko karkausvuosi niin meneekin vaikeaksi. Mutta hommaan on myös nopea  ja helppo ratkaisu. Siinä päivämäärä "esitarkastetaan" helpolla regex:llä ja mikäli luku saattaa olla päivämäärä niin kysytään date-komennolta onko kyseessä tosiaankin päivämäärä. Tulos lasketaan nopeammin kuin monimutkainen regex sen laskisi, tulos on käsittääkseeni ehdottomasti oikea ja tapahtumien kulku helppo ymmärtää: 
Koodia: [Valitse]
pvm=$( echo 2016-02-29 | grep  '[0-9][0-9][0-9][0-9][.-][0-1][0-9][.-][0-3][0-9]'); date -d$pvm > /dev/null 2>&1 ; [[ $( echo $?) = 0 ]] && echo $pvm
- tarkisatettava päivämäärä on 2016-02-29 mikä sattuu olemaan karkauspäivä. Muutapa vuotta ei-karkausvuodeksi ja ei tulosta mitään
- päiväyksen muoto on vvvv.kk.pp mutta eiköhän sen saisi muutettua myös "Suomalaiseen" muotoon: vvvv.pp.kk
- siis kun päivä on kelvollinen niin se tulostetaan mutta kelvotonta päivää ei tulosteta
- käsky taipuu tarkistamaan kaikki tiedostossa olevat päivämäärät ihan niinkuin normaali regex:kin.

- ei se tiedostosovellus ihan helppo ollutkaan ja hidaskin se on; pari millisekuntia päivä. Mutta vuoden 2000 karkauspäivä tuli oikein koska sekä 100:n että 400:n vuoden poikkeukset otettiin huomioon:
Koodia: [Valitse]
rm /tmp/ tiedosto; for n in {1999..2001}; do for o in {00..13}; do for m in {00..32}; do echo $n-$o-$m >> /tmp/tiedosto; done; done; done
pvm=($( grep '[0-9][0-9][0-9][0-9][.-][0-1][0-9][.-][0-3][0-9]' /tmp/tiedosto)); for (( n=1; n<=${#pvm[*]}; n++ )); do date -d${pvm[$n]} > /dev/null 2>&1 ; [[ $( echo $?) = 0 ]] && echo ${pvm[$n]}; done
read -p "paina enter lopettakseesi"
rm /tmptiedosto
**
Myös satunnaislukujen kehittäminen BASH:issa on huonoa, sillä se perustuu 16-bittiseen koodiin. Ja 16-bittiseltä ei paljoa voi odottaakaan. Mutta BASH:in koodi sallii bittimäärän kasvattamisen 15-bitin välein. Esimerkiksi haluttaessa satunnaislukuja väliltä 0-40 on käsky kun kehittämisessä halutaan käyttää apuna 31-bittistä satunnaislukua:
Koodia: [Valitse]
echo $((((RANDOM<<15)|RANDOM)*40/1073741824))   
ja 46- bittisen satunnaisluvun perusteella kehitetty olisi:
Koodia: [Valitse]
echo $(((((((RANDOM<<30)|(RANDOM<<15)))|RANDOM))*40/35184372088832))

Käskyt ovat  inhottavan näköisiä, mutta ne ovat nopeita;  ja 31-bittinen hidastaa noin 25% ja 46 bittinen hidastaa noin 28% verrattuna 16-bittiseen. 16-bittisellä voi pitkällä mittauksella havaita säännönmukaisuutta, mutta 31- ja varsinkin 46-bittisistä ei pitkälläkään mittauksella voi havaita omituisuuksia.
**
Kuka on esittänyt että BASH-koodissa voi olla looppeja?  Vain laiskuus on hyvä syy käyttää BASH:in looppeja, sillä esimerkiksi bc:llä looppaaminen on joskus niin sotkuista puuhaa että siinä aivot nyrjähtää. Mutta koodi jossa looppaaminen suoritetaan BASH-koodilla on kammottavan hidasta.

Miettimällä saa loopit usein pois kokonaankin ja onhan noita nopeitakin looppauskeinoja niinkuin esimerkiksi awk tai bc. Esimerkiksi looppi bc:llä:
Koodia: [Valitse]
echo "for (i = 4.00; i < 5.42; i += 0.02)  i" | bc   # looppausehdoiksi sopivat kyllä muuttujatkin. Nopeutus voi olla jopa luokkaa 100* .
Mutta bc-looppi on vaikea soveltaa ja koodista tulee sotkuista. Esimerkiksi:
Koodia: [Valitse]
for n in $(echo "for (i = 1; i < 1000000000; i += 1)  i" | bc) ; do  toiminta; done 
- siis itse loopinmuodostus nopeutuu "satakertaisesti" mutta koko tehtävä nopeutuu paljonkin vähemmän sillä toimintahan se on jonka suorittamiseen ajasta suurin osa kuluu ja sehän ei nopeudu ollenkaan. Nopeuden kaksinkertaistuminenkin tapahtuu vain niissä harvoissa tapauksissa kun toiminta vie ajasta vain pienen osan.

Nopein tavallinen looppi on malliltaan (mutta tämä on siis paljonkin hitaampi kuin tuo sotkuinen bc-looppi, se ei kykene muuttujiin eikä varsinkaan desimaalilukuihin ja muistisyöppökin se on):
Koodia: [Valitse]
for n in {1..1000}; do toiminta; done
Melkein yhtänopea on:
Koodia: [Valitse]
for n in $(seq alku steppi loppu); do toiminta; done   # muuten desimaaliset arvot soveltuvat myös kunhan desimaalipilkku muutetaan pisteeksi
- erikoistapauksissa seq-looppi on nopeinkin. Esimerkiksi kertoman laskeminen on nopeinta ja siisteintä muodostaa: seq -s* luku_josta_kertoma_lasketaan  | bc -l
- esimerkiksi nopein tapa laskea kertoma 20:ntä pienemmistä luvuista:
Koodia: [Valitse]
echo $(($(seq -s* luku_josta_kertoma_lasketaan)))

Käskyllä for n in {1..joku_tosisuuri_luku} on looppi jaettava osiin sillä brace-expansion on muistisyöppö:
Koodia: [Valitse]
for n in {1..10000} ; do for m in {1..10000} ; do toiminta; done; done
Ulkonäöltääm siistimpi käsky olisi:
Koodia: [Valitse]
for (( o=1; o<=1000000; o++ )); do toiminta ; done
mutta se on 20% hitaampi. 
**
Bash:in satunnaislukugeneraattorin 46-bittisen version tulosten poikkeamat kun ulostulo on jaettu 512:sta astiaan. Näillä asetuksilla skripti kestää noin 15 sekuntia
-  tarkoituksena on selvittää esiintyykö satunnaisluvuissa jotain omituista kuten esimerkiksi jaksollisuutta. Ainakaan otoksen koon ollessa neljäsataamiljoonaa ja mittausajan ollessa 72 min. ei näy vielä mitään.
- 16 bittinen (pelkkä RANDOM) osoittaa jaksollisuutta jo kymmenellä miljoonalla.
- satunnaislukujen jakauma on gaussiaaninen; mutta pientä kummallisuutta on
Koodia: [Valitse]
#!/bin/bash
# laskenta.
unset matriisi; for n in $(seq 1000000) ; do (( matriisi[$(((((((RANDOM<<30)|(RANDOM<<15)))|RANDOM))/68719476736)) ]++ )); done
# graafinen esitys:
minimi=$( echo ${matriisi[*]} | awk 'BEGIN {minimi=9e99} { if ($1<minimi) minimi=$1 } END { print minimi }' )
for x in {1..511}; do echo $x" "$( echo "100*(${matriisi[$x]}-$minimi)/$minimi" | bc -l ); done | gnuplot -p -e 'set terminal wxt size 350,262 enhanced font "Verdana,10"; set xrange [0:511]; set ylabel "poikkeama %"; set xlabel "satunnaisluku"; plot "/dev/stdin" with lines'
echo -e ${matriisi[@]/%/\\n} | awk '{sum+=$1; sumsq+=$1*$1} END {print "keskipoikkeama=" sqrt(sumsq/NR - (sum/NR)^2)}'
**
Koetin taas kertaalleen saada nopeutta satunnaisluvun muodostuksen tarkistukseen. Esiin tulikin että käyttämäni: let a=1 tyyppisen lauseen voi korvata (( a=1 )) tyyppisellä laiseella, jota käyttäen nopeus nousee yli kaksinkertaiseksi. Kyllä tulos on ihan kiva muutaman tunnin saldoksi.

Muuten näiden nopeutuksien hakemisen tarkoituksena ei ole etsiä BASH:ista hyvää, vaan saada jonkinlainen käsitys siitä miksi Perl ja Python on kehitetty elikä pystyykö käytännön ongelmat ratkaisemaan myös BASH:illa. Tämänhetkinen tilanne on se sekä Perl että Python ovat melkein jokasuhteessa kertaluokkia parempia sillä onhan niitä kehitetty runsaasti jo vuosikymmeniä ja BASH:ia varsin vähän. En silti vertailisi niitä keskenään sillä BASH:ista löytyy potkua mikäli sen kieroutunutta sielunelämää hallitsee.
- muistatteko kuinka OS2:lle kävi Windowsin kanssa: hävisi huonommalleen.
**
Nämä satunnaisluku-mittaukset ovat erinomaisia rasitustestejä. Sillointällöin koneen tuuletin alkaa kiljumaan. Kun järjestelmän valvonnasta katsoo ytimien rasitusta niin yksi ydin kerrallaan on kuormitettu 100%:iin ja toisissa se normaali pariprosenttia. Mutta tuulettimen kiljuminen tuntuu ajoittuvan niihin epämääräisesti ajoittuviin hetkiin jolloin käyttöjärjestelmä vaihtaa rasitettavaksi toisen ytimen. Siitä tulee muuten mieleen että käyttöjärjestelmä osaisi käyttää kaikkiakin ytimiä mutta jostain syystä ei käytä. Yksi melkein hyvä syy olisi että jos kaikkia ytimiä käytettäisiin niin prosessorit alkaisivat saada lämpöhalvauksia ja powerien liian pienen tehon takia prosessorit alkaisivat virheillä.
**
Aloinpa tutkia kuinka muodostetaan alkulukuja. Ensialkuun tutustuin skripteihin joista jopa minä ymmärsin mitä tehdään - mutta ne olivat surkean hitaita; miljoonan ensimmäisen alkuluvun etsimiseen kului useita kymmeniä minuutteja. Siitä sitten tutkin nopeampia, mutta nopeutuessaan niiden logiikka alkoi tulla minulle hämäräksi. Kunnes kohtasin skriptin joka haki 1.3 miljoonaa ensimmäistä alkulukua viidessä sekunnissa. Sen täytynee laskea oikein ja sen toiminta on helppo käsittää mutta sen toimintatapa ei helposti tule mieleenkään. Skripti:
Koodia: [Valitse]
seq 3 2 10000000 | factor | sed '/ .* / d ; s/:.*//'
Olenkin saanut BASH:ista käsityksen että näin on aina: jokaisesta skriptistä löytyy varmuudella super-nopea versio mutta maksaa hintansa löytää se.  Ja mahdollisesti tuon nopeamman koodia ei joko ole koskaan nettiin vietykään tai se ei vain löydy ja sen joutuu "keksimään" uudestaan.
**
BASH:illa ei ole vakiota MAXINT, mutta sen arvo on 64-bittisellä: 9223372036854775807 minkä voi tarkistaa käskyllä: cat /usr/include/limits.h | grep ' LONG_MAX'.
Rajan ylittäminen ei aiheuta virhettä vaan arvo kerrostuu: echo $((9223372036854775807+1)) on: -9223372036854775808
Eihän BASH:in matematiikkakäskyjä käytetä juuri ollenkaan, mutta jos tarvitsee niin voi varmistaa kuuluuko luku kerrokseen yksi käskyllä:
positiivisella puolella: [[ $(echo $numero '<=' 9223372036854775807  | bc) -eq 0 ]] && echo liian suuri numero
negatiivisella puolella: [[ $(echo $numero '>=' -9223372036854775808  | bc) -eq 0 ]] && echo liian pieni numero
**
BASH on täynnänsä pieniä ärsyttäviä yksityiskohtia. Niiden ärsyttävyyttä lisää tieto että niistä useimmat on ratkaistu - monien selitykset löytyvät kyllä man-sivuilta mutta kenelläkään ei ole aikaa kahlata kaikkia läpi. Mutta osa on vain katoavaa BASH-perinnetietoutta.

Esimerkiksi jo vuosia olen luullut että bc tulostaa aina kuten seuraava kun tuloksessa on numeroita paljon:
seq -s* 999 | bc
mutta kyllä siitä saa kunnollisenkin tulosteen:
seq -s* 999 | BC_LINE_LENGTH=0 bc
Kun näin tämän ensikerran niin tuntuipa hölmöltä ratkaisulta. Mutta matematiikkaohjelma jossa kutsutaan bc:tä usein on ihan toinen juttu: kyseessä on ympäristömuuttuja jolle annetaan arvo vain kerran ja sen nopeuttava vaikutus tuntuu samassa ohjelmassa kaikkialla; sen voi vaikka laittaa ohjelman aloituslauseisiin.
- voi tuon määreen kirjoittaa skriptin alkuunkin ja niin että jokainen bc tunnistaa sen; kuitenkin se tarvitsee hieman lisää:
Koodia: [Valitse]
EXPORT BC_LINE_LENGTH=0

BC:ssä ärsyttävistä ominaisuuksista toinen on se, että bc esittää tuloksissaan 0 desimaalia ellei toisin määrätä. Tämän kiertäminen:
echo "scale= kuinka_monta_desimaalia_haluat_tuloksissa_olevan" > /tmp/delme; BC_ENV_ARGS=/tmp/delme
- siis määräys annetaan vain kerran alussa sillä se on voimassa kunnes pääte sammutetaan.
**
Joskus haluaa tiedostolistauksen perään tulostuvan montako riviä siinä on. Sen saa aikaiseksi esimerkiksi seuraavankaltaisella käskyllä:
Koodia: [Valitse]
awk ' {print $0} END {print "tiedostoja yhteensä: " NR}' /boot/grub/grub.cfg
tai kyllä BASH itsekin tähän kykenee:
Koodia: [Valitse]
cat /boot/grub/grub.cfg | tee /dev/stderr | echo tiedostoja yhteensä: $(wc -l)
**
BASH:in goto-käsky ei se tarvitse goto-sanaa. Tai pikemminkin se on gosub-käsky mutta palatessa ei tarvitse kirjoittaa return. Sillä on kyllä sellainen rajoite että sillä voi kutsua ainoastaan funktioita => kauheaa, joutuu tekemään jäsennettyä koodia. Sen käyttö:
Koodia: [Valitse]
$muuttuja
- jos rivillä on jotakin muutakin, esimerkiksi ehto niin $muuttuja täytyy olla viimeisenä, esimerkiksi:
Koodia: [Valitse]
etsi_kenen_synttärit_on && $järjestä_pippalot
- toiminta on mielekästä kun muuttujassa järjestä_pippalot on synttärisankarin nimi.
- siis mainitsemalla muuttujan nimen voi hypätä miljoonaan eri paikkaan.
**
find käskyllä saa helposti tulostettua koko tiedostojärjestelmän tai sen palasia melkein niinkuin haluaa. Huonoina puolina on se että se tulostaa piilotiedostotkin ja sekoittaa tulostuksiinsa asiaankuulumattomuuksia kuten esimerkiksi: "Lupa evätty" - ja täysin kelvollista konstia niiden poistamiseksi en ole löytänyt. Tässä pikkuisen kelvoton:
Koodia: [Valitse]
find ~  -not -path '*/\.*' 2>&1 | grep -v 'Lupa evätty'

**
Bash:ista tuli uusi versio: bash 4.4 ; ei se repoissa vielä ole vaan käytössä on edelleen 4.3 . Sen muutos-listalla on kokoa niin paljon, ettei sitä "kukaan lue" . Tietysti se lisää bash:iin vain 0.001%.
**
Yksi syy BASH:in huonoon maineesen on käskyjen tulosteiden lukeminen muuttujiin sillä se johtaa usein skripteihin jotka toiminta riippuu siitä millaista dataa ne silläkertaa saa. Yleensä muuttujaan lukeminen on ihan järkevä toimenpide, mutta varsinkin find- ja ls-käskyn tulosteet tulee aina lukea matriisiin. Ja koska matriisiin lukeminenkin tehdään yleensä väärin niin satunnaisuus senkun kasvaa. Ja kun tulosteissa vielä usein on välilyöntejä niin tästä saadaan kehitettyä kunnon soppa.

Find-käskyn tuloste luetaan matriisiin seuraavalla käskyllä:
Koodia: [Valitse]
IFS=$'\n'; matriisin_nimi=($(find  kansio_mistä_alkaen_luetaan <haun rajoitukset> )); unset IFS
- siis sulkuja pitää olla kaksi; yksi sulku lukee muuttujaan jolloin kaikki näyttää päällisinpuolin tarkasteltuna olevan kunnossa mutta "ei tää nyt toimi".
- tuo IFS on varmistamassa että jokainen rivi luetaan matriisin samaan jäseneen silloinkin kun luettavassa on välilyöntejä.
- tällöin yksittäiset polku-tiedostonimet päätyvät matriisiin kukin omaan jäseneensä. Jäsenet voi hakea matriisista jäsenen järjestysnumerolla. Siis esimerkiksi tällätavoin:
Koodia: [Valitse]
echo ${matriisin_nimi[jäsenen järjestysnumero]}
tai lukemisen voi tehdä näinkin:
Koodia: [Valitse]
mapfile -t matriisin_nimi < <(find kansio_mistä_alkaen_luetaan <haun rajoitukset> )

- mikäli haluat varmistaa minkä kanssa pelaat käske: echo ${matriisin_nimi[0]} - jos käsky tulostaa monta riviä on lukeminen todennäköisesti tapahtunut väärin.

- muuten annapa käsky:
Koodia: [Valitse]
a[0]=höh; echo $a
- toimii toisinkinpäin: muuttujan asettaminen asettaa samalla samannimisen matriisin jäsenen 0 .
Onko matriisi helppo erottaa muuttujasta? Konekaan ei osaa. Mutta tämä ei ole ongelma kun asian tietää.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 26.09.16 - klo:18.36
BASH:ille on maailmalla monia päteviä kirjastoja, mutta ei ole mitään helppoa tapaa saada niitä käyttöönsä. Mutta ilman kirjastoja BASH:illa tehdyt skriptit ovat kymmeniä kertoja turhan hitaita ja toimivat miten sattuu. BASH on surkea - koska siltä kehitetään olemattoman vähän, se on tulkattu kieli ja ennenkaikkia koska sillä ei ole kirjastoja. Ja vaikuttaa siltä että jossakin pelätään BASH:ia ja kirjastojen käyttöä pyritään estämään.

Nimenomaan matriisien käsittely kaipaa kirjastoja sillä matriisien käsittelemistavat ovat käytännölle vieraita. BASH:in matriiseilla on monia erikois-ominaisuuksia, mutta niitä erikoisominaisuuksia ei nykyään osata hyödyntää joten missään niitä ei käytetä. Itseasiassa ne ovat kuitenkin käyttökelpoisia, joten ne kannattaa aina huomioida - ja "kirjasto-skriptit" ottavatkin yleensä ne huomioon mutta itse ohjelmoidut eivät.

Otetaanpa esimerkki: skripti joka hakee matriisin jäsenen arvoa vastaavan osoitteen: BASH:issa matriisin jäsenten osoitteet voivat pomppia sinnetänne jättäen osoitteita väliinkin, samaa arvoa voi löytyä montakin eri osoitteissa ja ensimmäinen osoite voi olla mikähyvänsä positiivinen luku - ja assosiatiivisessa matriisissa osoite voi olla tekstiä. Näiden ominaisuuksien vuoksi eivät "normaalit" skriptit toimi hyvin etsittäessä jäsenen arvoa vastaavaa osoitetta - tai voihan se etsitty arvohan voi löytyä monestakin osoitteesta. Tässä yksi tiivis ja nopea ratkaisu jolla ei ole pahoja varjopuolia:
- mukana on testaamiseen sopivia matriisin-määrittelyjä
Koodia: [Valitse]
#!/bin/bash
function arvoa_vastaava_osoite () { apu=$( eval echo -e \${$1[@]} | sed 's/ /\n/g' | awk -v apu2=$2 '{if ($0==apu2) print NR" "}'); for n in $apu; do eval echo -e \${!$1[@]} | awk -v apu=$n '{print $apu}'; done ;}

declare -A matriisi2

matriisi1[7]=2
matriisi1[7777]=55
matriisi1[77]=12
matriisi1[888888]=eka
matriisi1[54]=12
matriisi1[17]=55
matriisi1=({1..32001}) # kokeillessa tämä lause voi olla kommentoitu tai ei

matriisi2[ykkönen]=eka
matriisi2[kakkonen]=toka
matriisi2[kolmonen]=kolmas
matriisi2[nelonen]=neljäs
matriisi2[tuntematon]=eka


time arvoa_vastaava_osoite matriisi1 1200 # kokeiltaessa on joko matriisi1 tai matriisi2. Etsittävää arvoa muutellaan myös
- BASH-loopit hidastavat aina paljon ja siksi tässä on vain yksi BASH-looppi, ja sekin käydään yleensä läpi vain kerran-muutaman kerran.
- 32000 rivisen matriisin käsittelyyn kuluu noin 85ms;  puhdasta BASH:ia olevalta skriptiltä kestää noin 220ms mutta sehän ei toimisikaan aina.
- ratkaisu, jossa on puhdasta BASH:ia sisältää viitisen kriittistä vikaa sen lisäksi että se on todella hidas.
- jos tämmöistä ei löydy kirjastosta niin ratkaisu on siirtyä käyttämään sellaista kieltä jonka kirjastosta löytyy.
**
Valtaosa muistakin tehokkaista matriisinkäsittely-menetelmistä ovat täysin käsittämättömiä. Niin on muissakin kielissä, mutta niissä ohjelmoinnin nämä osat ovat kirjastoissa. Esimerkiksi:
Koodia: [Valitse]
function matriisien_ero () { awk 'BEGIN{RS=ORS=" "} {NR==FNR?a[$0]++:a[$0]--}END{for(k in a)if(a[k])print k}' <(echo -n "${!1}") <(echo -n "${!2}") ;}
- awk on matriisien käsittelyyn ehdottomasti sopivampi kuin BASH - tosin awk:in voi ajatella kuuluvan BASH:iin. Se huono puoli awk:illa on että sen "numeromäärä" on vain noin 19 numeroa - gawk:issa tosin on rajoittamattoman tarkkuuden kytkin -M.
- tällekin voi antaa verrattaviksi assosiatiiviset matriisit.
**
Sleep-käsky on prantunut; esimerkiksi ennen yhden sekunnin viive oli ennen luokkaa 1.004 ja nykyään 1.0012. Mutta "read -t 1" on kymmenen kertaa parempi: 1.00014. Lyhyet viiveet olivat ennen varsin käyttökelvottomia, mutta nyt sata mikrosekuntia on 108 mikrosekuntia ja 10 mikrosekunnin viive on sillä 17 mikrosekuntia. Osoituksena tästä:
Koodia: [Valitse]
for n in $(seq -s " " 100000); do read -t 0.00001; done
kestää noin 1.83 sekuntia kun pitäisi kestää 1 sekuntia. Toinen virhelähde on tuo looppi joka myös aiheuttaa virhettä; luultavasti noin 0.2  sekuntia.

Jos tuommoisia 10-100 mikrosekunnin viiveitä tekee niin mihin niitä käytetään? Herra yksin tietää, mutta kyllä niille käyttöä aikanaan tulee kunhan päähän uppoaa se että homma on mahdollista.

Koska noiden pienten viiveiden kuluttama aika lasketaan karkeasti kuvattuna suorittamalla viive miljoonakertaa ja jakamalla kulunut aika miljoonalla niin ei olekaan varmuutta ettei kääntäjä oikaise ja kasvata ensin viivettä ja tee sitten yhtä mittausta tuolla lopullisella viiveen arvolla. Todetakseni ettei noin tapahdu tein skriptin - siinä pyörii yhtaikaa kaksi toisistaan riippumatonta prosessia:
Koodia: [Valitse]
#!/bin/bash
# tässä skriptissä on kaksi prosessia pyörimässä yhtäaikaisesti. Ensin omia aikojaan tiedoston lukuarvoa kasvattavava prosessi taustalla.
for (( n=0; n<=999999; n++ )); do echo $n > file1;done &

# ja sitten perään toinen looppi ottamaan tiedostosta näytteitä. Lukujen tulisi useimmiten kasvaa, mutta koska BASH on muutenkin ajallisesti horjahteleva niin eihän niin aina ole. Yleensä tämä ei edes tulosta kaikkia 30:tä; esimerkiksi
# jos ensimmäinen prosessi kirjoittaa juuri kun toinen haluaa lukea niin BASH lopettaa
apu1=0; apu2=0; for m in {0001..0030}; do read -t 0.$m apu; apu1=$apu2;apu2=$(cat file1); [[ $apu1 && $apu2 ]] && echo -n $(($apu2-$apu1))' ' ; done
kill $! # tapa viimeiseksi määrätty taustaprosessi - siis tuo jonka perässä on merkki &

tulostusrivi esimerkiksi: 14 15 24 31 45 49 50 59 52 59 61 63 65
- skripti ei yleensä tulosta näinkään montaa 30:stä puhumattakaan - esimerkiksi jos toinen prosessi kirjoittaa samaan aikaan kuin toinen haluaa lukea niin BASH lopettaa koko homman. Mutta jo vähäinenkin tulostus osoittaa sen että viive tosiaan muuttuu sillä yleensä arvot kasvavat kokoajan.
- Jos haluaa aloittaa 10mikrosekunnista niin toinen looppi muutetaan muotoon for m in {00001..00030}
**
Taas kertaalleen piti laittaa toiminnan suorittamiselle ehdoksi se että erään muuttujan arvo on nolla. Siis ihan samaa kuin jo aikoinaan piti oppia; mutta nyt vasta tuli mieleen että se tehdään aina hieman liian työläästi. Varmaankin useimmat muutkin tekevät sen väärin, sillä nopein ja yksinkertaisin tapa on:
Koodia: [Valitse]
(( $muuttuja )) || toiminta

Testi:
Koodia: [Valitse]
[[ $muuttuja ]] || toiminta
sitten puolestaan ei testaa onko muuttujan arvo 0 , vaan onko muuttuja aritmeettiselta arvoltaan määrittelemätön tai 'teksti-jono arvoltaan' tyhjä-joukko (elikä '')
**
Useissa BASH:in käskyissä on sisäänrakennettu erittäin nopea looppi. Esimerkiksi jokaisen a:n muuttaminen b:ksi tiedostossa nimeltään ohje sen jokaisella rivillä:
Koodia: [Valitse]
cat ohje | tr a b
- tr-käsky hyväksyy kuitenkin vain yksittäisten merkkien muuttamisen. Hankalinta on, että käsky:
Koodia: [Valitse]
cat ohje | tr ab cd
onnistuu, mutta se tarkoittaa: vaihda kaikki a:t c:ksi ja b:t d:ksi. Mikäli tiedostossa oleva tekstijono täytyy korvata toisella tekstijonolla niin seuraavantyyppinen pelkkää BASH:ia oleva käsky käy:
Koodia: [Valitse]
readarray matriisi < tiedoston_nimi ; echo "${matriisi[*]//mikä/miksi}"
- sed:issä, awk:issa ... ja monessa muussakin on vielä hieman nopeampi sisäinen automaattilooppi. Varsinkin awk:iin voi kyllä lisätäkin looppeja eikä se paljoakaan hidasta.
- myös regex-moottorissa on sisäiset nopeat automaattiloopit.
- erittäin harvoin BASH:iin kannattaa tehdä loopeja BASH-koodilla, ne ovat kymmeniä-satoja kertoja hitaampia kuin nuo käskyjen sisäiset loopit.

Skriptejä joissa ei ole BASH-koodisia looppeja löytyy melkeinpä mihinhyvänsä tilanteeseen, esimerkiksi: 
Alussa kirjoitettiin data-tiedosto nimeltään: 0
Sitten piti kirjoittaa uusi 0 - kuitenkin piti ensin uudelleen-nimetä olemassa-oleva 0 1:ksi.
Sitten piti taas kirjoittaa uusi 0 - kuitenkin piti ensin uudelleen-nimetä olemassaolevat 1:ksi ja 2:ksi.
Sitten piti taas kirjoittaa uusi 0 - kuitenkin piti ensin uudelleen-nimetä olemassaolevat 1:ksi, 2:ksi ja 3:ksi.
ja niin edelleen. "Normaaliskriptissä" on vilisemälla looppeja ja väliaikaistiedostoja ja se on tautisen hidas. Sellainen käsky uudelleen-nimeämiseen jossa ei ole BASH-kielisiä looppeja:
Koodia: [Valitse]
mikäli ollaan valmiiksi oikeassa kansiossa: rename 's/(\d+)/$1+1/ge' $(ls | sort -rg)
mikäli saatetaan olla väärässä kansiossa  : ( cd kansio_jossa_uudelleen_nimettävät_ovat; rename 's/(\d+)/$1+1/ge' $(ls | sort -rg ))
- 10.000 tiedoston uudelleen nimeäminen kesti 170ms. Myös awk/BASH-toteutus jossa awk tekee loopit ja BASH toteuttaa kestää 5600ms ja lisäksi "start, stop ja step" täytyy asetella.
- tätä kannattaa käyttää jos vain mahdollista sillä nuo muut ovat kovalevylle todella rasittavia ja yksikin erehdys saa koko koneen vaikeaan tilaan.
Koodia: [Valitse]
awk -v a=$(ls | tail -1) 'BEGIN { while (a-->-1) string=string "mv " a+1" " a+2"\n"; print string }' | bash
ja vastaava silkkaa BASHia oleva käsky kestää noin 10 sekuntia, mutta toisaalta se on helpoiten räätälöitävissä: esimerkiksi seuraavassa tiedostonimien edessä on tekstiä, siis tiedostonimet ovat tässä seuraavanlaisia: data>numero<
Koodia: [Valitse]
touch data10001; for n in {10000..2}; do  mv data$n data$(($n+1)); done
Mutta kyse ei ole yksinomaan hitaudesta vaan siitä että esimerkiksi prosessorin kuormitus kasvaa liialliseksi kun kestoaika kasvaa kohtuuttomasti.

Mutta BASH on paitsi helpompi räätälöidä niin sillä voi myös laittaa tehtävän suoritukseen taustalle; siis voi jatkaa muiden hommien tekemistä melkein välittömästi. Mutta vaikka yhden tiedoston arvoja voi käyttää 10ms kuluttua niin kaikki tiedostot on käsitelty vain hieman nopeammin kuin tavallisesti.
- huomaa että tiedostojen nimet ovat: data1  data2 ... data10000 eikä pelkkiä numeroita. Kokelin toimintaa antamalla käskyt:
Koodia: [Valitse]
mkdir ~/koe; cd ~/koe
for n in $(seq -s " " 10000); do echo $n > data$n; done                                                                                                         # tämä lause muodostaa uudelleen-nimettävien tiedostojen joukon
time ( touch data10001; for n in {10000..1}; do  mv data$n data$(($n+1)) ; done & ) ; sleep 7.5; cat data10001; cat data2 # uudelleen-nimeäminen ja tuloksen tarkistus   
tulostaa: 10000 ja 1 niinkuin pitääkin ja nautiluksella katsoenkin kaikki on kunnossa. Tuo vähintään 7.5sekunnin sleep tuntuu olevan pakollinen jotta kumpikin lukema tulisi oikein - koska niitä uudelleen-nimeämisiä tehdään taustalla kokoajan.

Ensimmäinen tiedosto on kylläkin selvä jo 10ms kuluttua ja yksi lisää valmistuu aina vajaassa millisekunnissa mutta koko joukko siis vasta 7.5 sekunnin kuluttua.
- skriptiajurissa tiedostoja on noin paljon editoitaessa hankalimpia skriptejä. 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 20.10.16 - klo:15.57
boottauskyvynpalautus käyttäen ainoastaan live-Ubuntua

Teoriassa verkkosivuilla olevat skriptit voi leikata-liimata live-Ubuntun pääteeseen sellaisenaan - tällöin skriptille ei tarvitse myöntää suoritusoikeuttakaan. Käytännössä skriptejä ei saa toimimaan tällätavoin esimerkiksi:
1. terminaalin skriptikielenä on Dash ellei toisin määrätä. Dash eroaa Bash:ista niin vähän että useimmat Bash-skriptit toimivat Dash:issa ihan hyvin mutta jotkut skriptit eivät. Terminaali määrätään käyttämään Bash:ia käskyllä: #!/bin/bash. Määräyksen: #!/bin/bash eteen täytyy kirjoittaa välilyönti.
2. skriptit toimivat sittenkin vain useimmiten. Sillä jonkun tulkin bugin vuoksi myös Bash vaatii että joskus muidenkin lauseiden eteen täytyy lisätä välilyönti - ilmeisesti kuitenkin vain pääohjelmassa ja siellä voi lisätä kaikkien lauseiden eteen välilyönnin. Mutta funktioihin ei tarvitse koskea. Saavathan ne välilyönnit siellä rivien alussa olla valmiiksikin sillä eivät ne toimintaan vaikuta, mutta skriptin voi aina leikata-liimata mikäli lauseiden edessä on välilyönti. Ja mikäli rivien alussa ei ole välilyöntejä niin skripti ei aina toimi ennenkuin rivien eteen lisää välilyönnin käyttäen esimerkiksi gedit-editoria antamalla käsky: gedit. Liimaa teksti sinne. editoi sitä ja leikkaa koko teksti uudestaan ja liimaa päätteeseen. Mutta geditissäkin ainoastaan editointi toimii mutta talletus ei, joten kun boottaat niin menetät kaiken.
- suomenkielisen näppiksen saa näin: näpäytä yläpalkista neliötä jossa lukee EN ja avautuvasta valikosta: TextEntrySettings... ja avautuvan tekstineliön vasemmasta alakulmasta + . Avautuvaa valikkoa selataan kunnes esiin tulee: Finland jota näpäytetään ja sitten näpäytetään ADD. Valikot suljetaan ja näpäytetään taas yläpalkissa neliötä EN ja avautuvasta valikosta näpäytetään FI .
- verkkosivulla olevasta koodista ei näe onko sen lauseiden edessä välilyönnit vai ei.
3. live-Ubuntussa ei ole kaikkia niitä sovellus-ohjelmia jotka normaali-koneessa on. Sovellus-ohjelmien lisääminen live-Ubuntuun on mahdollista mutta siinä on melkoinen työ.
4. interaktiivisuus katoaa - esimerkiksi kun skripti kysyy jotakin niin se odottaa vastausta väärästä paikasta joten skripti jää ikuisesti odottamaan vastausta.


Todetaksesi että tämä toimii voit ajaa boottauskyvynpalautus-skriptin - sillä on hyvä kokeilla monipuolisuutensa takia. Siis koko koodin voi liimata-leikata yhtenä palana live-Ubuntun päätteeseen - suoritusoikeutta ei tarvitse antaa. Boottauskyvynpalautus-skriptin hakeminen UbuntuFoorumin sivuilta ja sen:
1. Boottaa live-Ubuntulla ja avaa sen verkkoyhteys (verkkoyhteys avataan ihan samallatavoin kuin normaalistikin, elikä napauttamalla yläpalkista verkkokuvaketta ja .... )
2. Avaa selain, kirjoita selaimen hakukenttään: ubuntu
3. avautuvasta valikosta valitse ubuntu suomi ja siellä näpäytä UbuntuYhteisö - keskustelualueet.
4. Kirjoita UbuntuSuomen hakukenttään: boottauskyvynpalautus ja paina enter. Hetkenkuluttua avautuvasta ikkunasta näpäytä: Ohjeita shell-skriptaukseen
-  hakukenttiä on kaksi. Se oikea kenttä on se alempi.
5. Paina Ctrl-f ja kirjoita: boott
-  maalaa alempana oleva harmaa koodikenttä.
-  maalaaminen tapahtuu näin: kohdista hiiriosoitin kopioitavan alkuun ja paina hiiren vasen nappi alas. Pitäen nappia alhaalla siirrä hiiriosoitin kopioitavan loppuun jossa voit päästää hiirinapin ylös.
6. Paina ensin Ctrl-c , sitten paina Ctrl-ALT-T ja pääteikkunan auettua paina: SHIFT-Ctrl-v . Heti alkaa tapahtua joten pistä näppisi taskuun.
-  siis esimerkiksi Ctrl-ALT-t tehdään näin: ensin nappulat Ctrl ja ALT alas ja pitäen niitä alhaalla painetaan kirjainnappulaa t
Koodia: [Valitse]
#!/bin/bash
# 1.1.2016 petteriIII.
# Tämä päivitys-skripti paivittää kovalevyltä sen kaikki Ubuntut (ja monet muutkin Linuxit). 105
# bootata voidaan mistä halutaan, eikä merkitse mitään jos jommassakummassa on EFI tai vaikka molemmissa.
# päivitys-skripti voi myös sijaita joko päivitettävässä tai päivittävässä. Päivitys-skripti kannattaa ajaa boottaus-välineen nautiluksella käyttäjätilassa.
# Jos salasanaa kysytään on se boottaus-välineen salasana; jos on bootattu live-versioon ei salasanaa kysytä ollenkaan.
# Btrfs-tiedostomuoto vaatii päivitys-skriptiin muutoksia.
# Kun on bootattu live-versiolta täytyy verkkoyhteys luoda ensin; live-versiossa verkkoyhteys luodaan samallatavoin kuin kovalevyllä olevassa.
# Kun on bootattu live-versiolta korjataan kovalevyn kaikkien liittämättömien osioiden levyvirheet ennen päivitystä.
# Ennen päivitystä yritetään poistaa kaikki päivityksen esteet ja turhista roskista pyritään eroon.
# Jos koneessa on lukemattomia turhia imageja saattaa kestää pitkäänkin poistaa ne, mutta muuten päivitys toimii lähes yhtä nopeasti kuin tavallinenkin päivitys.
function KirjoitaOhje { echo '- ennen päivittämistä kannaattaa sulkea muut ohjelmat.
- aluksi kysytään salasanaa ettei sitä kysyttäisi sitten toiminnan aikana keskenkaiken. Salasana on kovalevyllä toimittaessa oma
  salasanasi ja muistitikulla toimittaessa se on ubuntu jos sitä edes kysytään.
- toimiva internet-yhteys on välttämätön; live-versioiden verkkoyhteys asennetaan aivan samoin kuin normaalistikin - muistitikun asetukset säilyvät mutta cd:n ei.
- kovalevyosiot tarkistetaan ja yritetään korjata lukuunottamatta sitä osiota jolla on bootattu. Windows on kylläkin kantona kaskessa.
- roskikset tyhjennetään, vain viimeinen image jätetään ja päivityksen esteitä poistetaan.'; }
function tarkistaKovalevyjenKaikkiOsiot () {
read -t 5 -n 1 -p 'jos haluat tarkistaa kovalevyn perusteellisesti paina jotain kirjainta; silloin tarkistus kestää jonkinverran kauemmin mutta tarkistaa paremmin.' apu
echo
for osio in $(sudo blkid | grep -v swap | awk '{print $1}' | sed 's/\://g'); do
  [[ $(echo $osio | grep loop) ]] && continue
  sudo fsck -MVCaf $osio ||   sudo fsck -MVCac $osio
  echo ""
done
# live-tikku saattaa aiheuttaa virheviestin:There are differences between boot sector and its backup.
}
function unmountmnt { # if grep -qs '/mnt/foo' /proc/mounts; then
cd / # umount ei onnistu, jos joku umountattavan kansio on valittuna
sudo echo ""
for mountattu in $(sudo mount | grep /mnt | awk '{print $3}')
do
  if grep -qs $mountattu /proc/mounts; then sudo umount -l $mountattu; fi
done
echo '/mnt:n mountit on poistettu'
}
function Levy {  echo -n $(ls -l /dev/disk/by-id | grep -m 1 '/'${Chrootosio##*/} | awk '{print $9}' | sed "s/-part*//g") ;}
function paivitaKaikkiOsiot () { # itseensä chroottaus onnistuu ihan kivasti joten tällätavoin voi ihan hyvin päivittää itsensä
unmountmnt
for Chrootosio in $(sudo blkid | grep -v swap | grep -v vfat | awk '{print $1}' | sed 's/\://g'); do
  # [[ $( mount | grep $Chrootosio) ]] && echo && echo $Chrootosio'ohitettu' && echo && continue # hypätään seuraanaan loopissa
  osiontyyppi=$(sudo blkid -o value -s TYPE $Chrootosio)
  sudo mount -t $osiontyyppi $Chrootosio /mnt
  [[ -d /mnt/boot/grub ]] && [[ $(cat /mnt/boot/grub/grub.cfg | grep menuentry.*Ubuntu ) ]] &&  {
  sudo cp -L /etc/resolv.conf /mnt/etc/ ######################################### koe 21.10.16
  sudo mount -t sysfs none /mnt/sys &&  sudo mount -t proc proc /mnt/proc && sudo mount --bind /dev/ /mnt/dev &&  sudo mount --bind /dev/pts /mnt/dev/pts && sudo mount -o bind /etc/resolv.conf /mnt/etc/resolv.conf && sudo mount -o bind /dev/shm /mnt/dev/shm && sudo mount --bind /proc /mnt/proc
  echo; echo -n '********** päivitetään: '$Chrootosio '   levyllä: '; Levy; echo '     tiedostomuoto: '$osiontyyppi; echo
  [[ -d /mnt/home/$(env | grep USER)/.local/share/Trash ]] && sudo rm -rf /mnt/home/$(env | grep USER)/.local/share/Trash # poistaa roskikset kaikilta osioilta joiden käyttäjä on sama kuin se jonka tililtä päivitä-skriptiä ajetaan eikä eriliskodin roskisia tyhjennetä koskaanfunction  käskynlähde() ( set -o pipefail;
[[ $(type $1  2> /dev/null | grep sisäänrakennettu ) ]] && echo sisäänrakennettu ||
which "$@" | xargs -r readlink -f | xargs -r dpkg -S ;
(( $? )) && echo -n käskyä ei ole asennettu mutta se löytyy paketista: &&
apt-cache search $1 | awk '{print $1}' | grep -w ^$1 ;); käskynlähde agrep
:
  # Kohdekoneen muistin vapauttaminen ei helpota tätä ohjelmaa. Kohdekoneen siistimisen syy onkin se, että kohdekone pärjäisi tulevaisuudessa itsekseen.
  sudo chroot /mnt tune2fs -m 1 $Chrootosio # rajoittaa pääkäyttäjälle varattua kovalevytilaa yhteen prosenttiin.
   for n in $(ls /mnt/boot | grep config | grep -v $(ls /mnt/boot | grep config | sort | tail -1) | sed 's/config-//g'); do
    sudo chroot /mnt apt-get --yes  purge 'linux-image-'$n
    sudo chroot /mnt apt-get --yes  purge 'linux-image-extra-'$n # turha lause, mutta muistutuksena siitä mitä tapahtuu.
    sudo chroot /mnt apt-get --yes  purge 'linux-headers-'$n
  done # Mikäli et halua poistaa imageja kommentoi nämä 5 riviä
  # seuraavia kommentointeja poistetaan kun haetaan vikaa jos päivitys-skripti toimisi joskus virheellisesti; ei toistaiseksi ole tarvittu
  #sudo fuser -cuk /var/lib/dpkg/lock; sudo rm -f /var/lib/dpkg/lock   
  #sudo fuser -cuk /var/cache/apt/archives/lock; sudo rm -f /var/cache/apt/archives/lock
  sudo chroot /mnt rm -f /var/cache/apt/archives/lock /var/lib/aptitude/lock /var/lib/dpkg/lock /var/lib/apt/lists/lock
  #sudo chroot /mnt rm /var/lib/apt/lists/*$; sudo rm /var/lib/dpkg/lock$; sudo rm /var/cache/apt/archives/lock # pitäiskö siirtää alkuun?
  sudo chroot /mnt apt-get check # korjaa pakettivaraston riippuvuudet
  sudo chroot /mnt dpkg --force-confnew --configure -a
  sudo chroot /mnt apt-get --yes  --fix-broken install
  sudo chroot /mnt apt-get --yes  autoclean
  #sudo chroot /mnt apt-get --yes  clean # poistaa myös osittaiset_ja_vahingoittuneet paketit ja tekee toiminnan varmemmaksi, mutta hidastaa ellei verkkoyhteys ole tosinopea.
  sudo chroot /mnt apt-get --yes  autoremove
  # jos jossain vaiheessa kun vahingoittueen paketin nimi selviää saa sen poistettua: sudo dpkg --remove -force --force-remove-reinstreq paketin_nimi
  sudo chroot /mnt rm -f /var/crash/* # poistaa tehdyt raportit etteivät ne jatkuvasti kiusaisi. Jos ne kiusaa sittenkin: sudo gedit /etc/default/apport ja kirjoitta 0
  sudo chroot /mnt apt-get update
  [[ $(env | grep USER.*=ubuntu) ]] && sudo chroot /mnt apt-get -y upgrade || sudo chroot /mnt apt-get -y dist-upgrade
  # $($Chrootosio | sed -r 's/[0-9]+$//g')
   sudo chroot /mnt update-grub && echo -n 'grub.cfg sijoitettiin levylle: '; Levy
} # vastaava avaava sulkumerkki on rivillä 38
unmountmnt
done
}
# Pääohjelma
# echo ${0%} | tr "/" " " | awk '{print $(NF-2)}';read
#[[ $(lsb_release -i | grep Ubntu$ ) ]] && echo tämä on standardi Ubuntu joten jatketaan || echo tämä ei ole stardardi Ubuntu ja jatkaminen saattaa olla vaarallista. Vain vastaamalla: kyllä jatketaan. Haluatko välttämättä jatkaa? && read apu && [[ $apu != kyllä ]] && exit || echo jatketaan kun lupa saatiin
 KirjoitaOhje
 echo ''; sudo echo
 hommaalkoi="$(date +%s)"
 tarkistaKovalevyjenKaikkiOsiot # sitä osiota jolta on bootattu ei tarkisteta.
 paivitaKaikkiOsiot
 echo -e 'päivitetty: '$(date +"%d-%m-%y %k:%M")'\t ja päivittäminen kesti sekunneissa: '$(($(echo $(date +%s)-$hommaalkoi)))
:
**
Koska BASH:ilta puuttuu kirjastot tulee sen alkeellisistakin käskyistä usein todella hankalia eikä niiden laatu päätä huimaa. Esimerkiksi: Efi_osio voi sijaita millä osiolla hyvänsä, ei sen levyn ensimmäinen osio tarvitse olla. Kun halutaan tietää mikä laitenimi on sillä efi-osiolla jota osio /dev/sda6 käyttää niin se saadaan tietää käskyllä:
Koodia: [Valitse]
function laitenimeä_vastaava_levy () { echo ${1##*/} | sed -r 's/[0-9]+$//g' ;}
function levyn_efi_osio () { sudo fdisk -l | grep EFI | grep $(laitenimeä_vastaava_levy $1) | awk '{print $1}';}   # funktiosta voidaan kutsua toista funktiota
levyn_efi_osio  osion_laitenimi    # siis esimerkiksi: levyn_efi_osio /dev/sda6
- muuten se keno-viritelmä jota usein käytetään jotta saataisiin käskyn esitys selväpiirteiseksi onkin yleensä turha ja hankalan-näköinen sillä esimerkiksi tässä nuo kolme lausetta voi kopioida päätteeseen yhtenä palana - perään joutuu kylläkin painamaan return. Voit kopioida päätteeseen vaikka tuhatrivisen skriptin ja perään painaa return yhden kerran.
**
Siitäkin tämä BASH on kiva että kaikessa on ikuisesti parannettavaa ja parantaminen on yksinkertaista. Jokatapauksessa seuraavassa on tärkeää funktion muodostustapa. Muodosta: funktio {}  siirrytty muotoon: funktio () .
- kaarisulut muodostavat funktiolle oman prosessin joten funktion asetukset eivät saastuta muuta skriptiä:
Koodia: [Valitse]
function  käskynlähde() ( set -o pipefail; [[ $(type $1 | grep sisäänrakennettu ) ]] && echo sisäänrakennettu || which $1 | xargs -r readlink -f | xargs -r dpkg -S ; (( $? )) && echo käskyä ei ole ;); käskynlähde käskyn_nimi
- itseasiassa sekin on täysin selvää että testi on: (( $? )) eikä [[ $? ]] sillä tottakai vertailun pitää olla aritmeettinen. Kestipä pikään tajuta.

Nyt alkaa parantelu mennä alueelle jossa en osaa kunnolla testata, mutta toiminto olisi todella tarpeellinen jos sen vaan saisi toimimaan virheettömästi (saattaa toimiakin mutta en ole läheskään varma):
- selvitystä: käsky ei välttämättä löydy käskyn nimen mukaisesta paketista niinkuin esimerkiksi käsky: ls . Paketin nimi täytyy selvittää kulman takaa.
Koodia: [Valitse]
function  käskynlähde() ( set -o pipefail;
[[ $(type $1  2> /dev/null | grep sisäänrakennettu ) ]] && echo sisäänrakennettu ||
which "$@" | xargs -r readlink -f | xargs -r dpkg -S ;
(( $? )) && echo -n käskyä ei ole asennettu mutta se löytyy paketista: &&
apt-cache search $1 | awk '{print $1}' | grep -w ^$1 ;); käskynlähde agrep
:

- leikka-liimaa kaikki käskyn rivit kerralla - ei koodiin tarvitse laittaa kenoja vaan toimii se ilmankin
- kun käsky on kerran toiminut niin painamalla nappia nuoli ylös saat koko käskyn historiasta yhtenä rivinä ja voit kirjoittaa agrep:in tilalle ls tai echo tai xargs tai mitämielitkin.
- agrep on muuten käyttökelpoinen: se etsii samankaltaisia kuin mitä määrätään.
**
Taas kertaalleen aloin kehittää menetelmää jolla saisi tulostettua päätteessä käskyn antamishetkellä määriteltyjen muuttujien nimet ja arvot:
Koodia: [Valitse]
apu=$(echo ' '${!a*}' '${!b*}' '${!c*}' '${!d*}' '${!e*}' '${!f*}' '${!g*}' '${!h*}' '${!i*}' '${!j*}' '${!k*}' '${!l*}' '${!m*}' '${!n*}' '${!o*}' '${!u*}' '${!v*}' '${!w*}' '${!x*}' '${!z*}\
' '${!y*}' '${!A*}' '${!B*}' '${!C*}' ' ${!D*}' '${!E*}' '${!F*}' '${!G*}' '${!H*}' '${!I*}' '${!J*}' '${!K*}' '${!L*}' '${!M*}' '${!N*}' '${!O*}' '${!P*}' '${!Q*}' '${!R*}' '${!S*}' '${!T*}\
' '${!U*}' '${!V*}' '${!W*}' '${!X*}' '${!Z*}' '${!Y*}  | sed 's/ /\n$/g' | sed '/$\B/d' | sed '/$apu/d' | grep [[:lower:]] | tr [[:space:]] ¤) && (echo $apu | tr ¤ '\n';\
 eval echo $apu | tr ¤ '\n') > /tmp/delme && awk '{a[NR]=$0; i=NR}END{i=i+i%2;for(m=1;m<=i/2;m++) print a[m]"    "a[m+i/2]}' /tmp/delme | column -t
- tämän käskyn voi liittää skriptiinsä tai tehdä esimerkiksi näin: ensiksi tyhjennät päätteessä määritellyt muuttujat käskyllä: "exec bash"  ja sitten esimerkiksi: a=1 ja: b=2 jonka jälkeen annat edelläkuvatun käskyn. Se tulostaa:
$a   1
$b   2
- alle 10ms nopeus on BASH-skriptille erittäin suuri sillä tavanomaisin menetelmin ei saa 200ms nopeampaa aikaiseksi.
- nopeus ei myöskään riipu muuttujien lukumäärästä mikä on BASH-skriptille erikoista.     
- muuttujissa saa olla välilyöntejäkin eikä muuttujia silti tarvitse laittaa sulkuihin - mutta kyllä sulutkin saa olla.
- mikäli jokin muuttuja on määritelty mutta sillä ei ole arvoa näkyy tämä listauksesta.
- matriisista näytetään vain jäsen 0.
- awk on BASH:iakin monipuolisempi ja vaikeampi erittäin nopea skripti-kieli. Tuossa edellisessä se hoitaa tulosteen muotoilun mikä on se hidas ja vaikea osa. Tuo awk-osa on niin nopea ettei sen lisääminen tunnu edes vaikuttavan - Perl ja Python eivät missään nimessä pääse edes samaan. Awk kärsii aivan sama/media/petteri/tikku/OMATSKRIPTITsta kuin BASH:kin ja ne molemmat kärsivät samasta kuin assembler: ihan liian työläitä - vaikka ovatkin tarvittaessa tosi- nopeita.
Muuten: skriptien muuttujien nimissä pitää olla ainakin yksi pieni kirjan - yksinomaan suurilla kirjaimilla kirjoitetut ovat BASH:in omia muuttujia.
**
Assosiatiivisen matriisin kirjoittaminen levylle, sen lukeminen levyltä ja sen tulostaminen.
- matriisin nimi ja koko voivat olla mitä hyvänsä.
- muuten matriisit sorttaavat itsensä automaattisesti, joten lukujen järjestys muuttuu. Mutta osoitteet ovat aina samassa järjestyksessä kuin arvotkin.
- esitän tämän sillä se kertoo miksi bash:ia mollataan: esimerkiksi tämä matriisin tallettaminen: jos meitä pyydetään tekemään  skripti matriisin tallettamiseksi niin luultavasti tehtävä yritetään ratkaista  tekemällä skripti joka tallettaa matriisin? Totaalisen idioottimaista, vain opin saanut voi sortua moiseen.  Sensijaan tietämätön saattaa ajatella niinkuin tässä: matriisi on jo bash:in muistissa ja sen kuva muistissa on yksi tekstijono. Mikäli talletetaan tämä tekstijono niin homma on tehty ehdottomasti moitteetta - se normaalitapa sensijaan on buginen ja hidas kuin .... Näin on bash:issa aina: paljon puhuvan suupielet ovat ruskeat, niin minun kuin myös professorien.

Bash:issa assosiatiivisen matriisin voi siirtää funktioon globaalialueella mutta palautus ei globaalialueen kautta assosiatiivisena matriisina toistaiseksi onnistu. Palauttamisen voi kuitenkin tehdä "normaalina" matriisina. Sillä sekä "normaalin" että assosiatiivisen matriisin muistikuvat ovat samanlaisia ja kun siirretään muistikuva niin on bash:ista kiinni tulkitseeko se matriisin muistikuvan "tavallisena" vai assosiatiivisena.
Koodia: [Valitse]
#!/bin/bash
declare -A matriisi # assosiatiivinen matriisi siirtyy globaalialueella funktioon päin mutta palauttaminen ei onnistu noinvain. 181

function talleta_matriisi () { echo $(declare -p | grep "declare -A".*$1  | cut -d= -f 2-) > /tmp/delme ;} # assosiatiivisen matriisin muistikuva RAM:missa talletetaan levylle.

function tulosta_matriisi () { ( echo -n 'jäsenten arvot   : '; eval echo \${$1[*]}; echo -n 'jäsenten osoitteet : '; eval echo \${!$1[@]} ) | column -t ;}

# muodostetaan koematriisi:
matriisi[ykkönen]=yksi
matriisi[555]=2
matriisi[17]=3
matriisi[2]=4

talleta_matriisi matriisi
unset matriisi; declare -A matriisi # unset nollaa matriisin joten voi olla varma että matriisi on luettu levyltä. Mutta unset vie samalla assosiative-statuksen joten se täytyy palauttaa. Lauseen voi kommentoida
eval matriisi=$(cat /tmp/delme)     # assosiatiivinen matriisi muodostetaan uudelleen levyltä luetun muistikuvan mukaan. Erillistä funktiota levyltä lukemiseen ei edes tarvita
tulosta_matriisi matriisi
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 11.12.16 - klo:10.50
 Loopit ovat sellaisia kieliä varten jotka ratkaisevat ongelmansa väkivoimin ja bash:issahan ei ole voimaa. Mutta bash on ammattilaisten tekemä ja poikkeuksetta kaikkeen löytyy keinonsa. Kunhan ei käytetä looppeja; sana: for ... kuuluu kelvottomiin skripteihin; varmaankin jonkun Perlin kehittäjät ujuttivat loopit bashiin jotta kilpailijasta tulisi entistäkin kehnompi.
- toki laiskuus on ihan kelpo syy tehdä bashissakin looppeja mikäli esimerkiksi hitaus ei merkitse.
- minua on aina kiinnostanut ilmiöt nimiltään: "hävitä huonommalleen" ja "pitää parempaansa alistettuna". Niinkuin OS/2 hävisi Windowsille niin on myös bash hävinnyt huonommalleen. Tottakai kun huonompaa on kehitetty päivittäin jo kolmenkymmenetä vuotta on se kehittynytkin paremmaksi kun bashia ei kehitetä juuri ollenkaan. Ja kun lisäksi opetetaan käyttämään bash:ia tehottomasti esimerkiksi jättämällä kertomatta että jos bashissa käyttää looppeja niin tehot laskevat; joskus vähän mutta joskus ne tekevät skriptistä käyttökelvottoman .

"Bash-loopittomia" ratkaisuja löytyy kaikkeen. Esimerkiksi muutama erikoistapaus - ensiksi matriisista etsiminen:
Koodia: [Valitse]
koe=(kaksiyksi kaksi kolme kaksikolmatta); echo -e ${koe[@]/%/\\n} | grep -nw kaksi | grep -o ^[0-9]*
tulostaa 2 niinkuin pitääkin ja nopeus kasvaa mielettömästi.

Vielä kammottavampi esimerkki on matriisien vertaaminen; totunnaisin keinoin hommaan sotketaan looppien lisäksi tiedostot. Mutta totunnaisia keinoja käyttäen matriisien vertaaminen onnistuu muistissakin:
Koodia: [Valitse]
mat1=(1 2 3); mat2=(1 2 3); [[ ! $(diff <(echo $(echo -e ${mat1[@]/%/\\n})) <(echo $(echo -e ${mat2[@]/%/\\n}))) ]] && echo matriisit ovat samanlaiset || echo matriisit ovat erilaiset
RAM:missa olevia muistikuvia vertaamalla toiminta on nopeampaa ja varmempaa ... ja vielä paljon epä-totunnaisempaa, esimerkiksi:
Koodia: [Valitse]
mat1=(1 2 3); mat2=(1 2 3); apu1="$(declare -p | grep "declare.*mat1"  | cut -d= -f 2- | tr -d [])"; apu2="$(declare -p | grep "declare.*mat2"  | cut -d= -f 2- | tr -d [])"; [[ $apu1 = $apu2 ]] && echo matriisit ovat samanlaiset || echo matriisit eivät ole samanlaisia
käsky ei välitä ovatko matriisit "tavallisia" tai assositiivisia eikä paljoa matriisien koostakaan. Ja osoitteiden vastaavuus tarkistetaan myös. Ja kaikkien matriisien arvoissa sekä assosiatiivisten matriisien arvoissa että osoitteissa saa olla välilyöntejä mikäli ne ovat heittomerkkien välissä - tai osoitteissa ei kavaita heittomerkkejäkään. Toiminta on moitteetonta, mutta häviää "nopeuskunkulle" hieman (mutta jotenkin varmemman oloinen).

Lopuksi vielä "nopeuskunkku" (Tämä tarkistaa osoitteetkin ja kelpaa assosiatiivisillekin. Tässäkään ei ole looppia):
Koodia: [Valitse]
mat1=({1..10000}); mat2=({1..10000}); [[ $(echo "${mat1[*]}") = $(echo "${mat2[*]}") && $(echo "${!mat1[*]}") = $(echo "${!mat2[*]}") ]] && echo matriisit ovat samanlaiset || echo matriisit ovat erilaiset
- luultavasti lisäksi melkein aina oikeellinen, mutta varmuutta siitä ei ole - aika näyttää.

-vertailun vuoksi se "normaali" menetelmä joka käyttää looppia mutta tarkistaa osoitteetkin:
Koodia: [Valitse]
mat1=({1..10000}); mat2=({1..10000});  for n in {1..10000}; do [[ "${mat1[$n]}" != "${mat2[$n]}" && "${!mat1[$n]}" != "${!mat2[$n]}" ]] && echo matriisit ovat erilaiset && break ; done; echo matriisit ovat samanlaiset
tämä on 10-200 kertaa hitaampi mutta toimii kyllä assosiatiivisillekin ja nimissä ja osoitteissa saa olla välilyöntejä. Mutta tuo hitaus: valinta ei tuota vaikeuksia kun pitää valita 0.3 sekuntia kestävän ja 60 sekuntia kestävän välillä.

- huomautus:  bash-matriisien osoite-alueella voi olla aukkoja - ominaisuus josta ei nykyään osata käyttää hyödyksi mutta kyllä sillä käyttönsä on. Mutta se tekee sen ettei pelkkä arvojen tarkistus riitä vaan osoitteet täytyy tarkistaa myös. 

Tottakai looppaaminen on välttämätöntä bash:issakin, mutta niin että bash käyttää käskyjä joissa on looppi sisällä: silloin tuo looppaaminen suoritetaan kun käsky on tulkattu C-kieliseksi. Ja joissakin käskyissä looppaaaminen on tarkoituksenakin, esimerkiksi:
Koodia: [Valitse]
seq -s* luku_josta_kertoma_lasketaan | bc
on nopein tapa laskea kertoma. Mutta siinäkään ei ole rakennetta: for ... ja looppaaminen tapahtuu C-koodisena. Mutta siiretään looppi BChen, ne ovat melkein yhtänopeita:
Koodia: [Valitse]
echo 'b=1;n=10000;while(n--)b=(b*(n+1));b' | bc
tai:
echo  "for (i = 1; i < 10000; i += 1)  i" | bc | sed ':a;{N;s/\n/*/};ba' | bc
**
Samoin etsintä matriisista:
Koodia: [Valitse]
function maksimi (){ eval echo \${$1[*]} | tr ' ' '\n' | sort -n | tail -1 ;}; mat=({999..1}); maksimi mat
function minimi (){ eval echo \${$1[*]} | tr ' ' '\n' | sort -nr | tail -1 ;}; mat=({999..1}); minimi mat

ei niissä looppeja tarvita ja niinpä ne ovatkin tosinopeita. Lisäksi niille passataan vain matriisin nimi. Niihinkin saa ujutettua bc:n jotta matriisin jäsenet voisivat olla myös ratkaisemattomassa muodossa:
Koodia: [Valitse]
function minimi (){ eval echo \${$1[*]} | tr '[]' '()' | tr ' ' '\n' | bc -l | sort -n | head -1 ;}; mat[1]='1+1'; mat[2]=s[.01]/c[.01]; minimi mat
jos bc: numeromäärä ei ole miellytä niin vahda sitä (numeromäärä saa olla mikähyvänsä positiivinen kokonaisluku) ja samalla kannattaa vaihtaa bc:n tulostustapaa:
Koodia: [Valitse]
function minimi (){ export BC_LINE_LENGTH=0; echo 'scale=566'>/tmp/delme; export BC_ENV_ARGS=/tmp/delme; eval echo \${$1[*]} | tr '[]' '()' | tr ' ' '\n' | bc -l | sort -n | head -1 ;}; mat[1]='1+1'; mat[2]=s[.01]/c[.01]; minimi mat
matriisin summan laskeminen:
Koodia: [Valitse]
function summa (){ summa=$(echo "${mat[@]/%/+}" | sed 's/+$//' | bc); echo 'summa='$summa ;}; mat=({999..1}); summa mat
matriisin summan ja keskiarvon laskeminen:
Koodia: [Valitse]
function summaJaKeskiarvo (){ echo "${mat[@]/%/+}" | sed 's/+$//' > /tmp/delme; summa=$( cat /tmp/delme | bc); lkm=$(cat /tmp/delme | tr -dc + | wc -m); echo 'matriisinsumma='$summa' keskiarvo='$(echo $summa/$lkm | bc) ;}; mat=({999..1}); summaJaKeskiarvo mat
**
Bashin käskykantaa voi lisätä funktioilla. Itseasiassa koneessasi on hirveä läjä jo määriteltyjä funktioita: annapa käsky: declare . Tosin noissa funktioissa ei ole sanaa: function )
- sitä olen aina ihmetellyt miksi ne ei edes vahingossa lisää joskus jotain hyödyllistä ?  Ja sitäpaitsi niille kaikille on käyttöä muttei kylläkään tässä ulottuvuudessa.
- funktiokutsuun kuluva aika on 45mikrosekuntia.
- muuttujat ovat yhteisiä kutsujan kanssa.

Aloinpa tehdä funktiota, joka etsii luetellusta joukosta minimin. Sen vaatimuksia:
- joukossa voi olla mielivaltainen määrä jäseniä eikä jäsenen pituutta rajoiteta
- jäsenissä voi olla etumerkki tai sitten ei
- desimaalilukujen tulee kelvata
- joukon jäsenissä voi olla tieteellistä esitystapaa (=exponentteja tyyliin: 1e3)
- eksponenttimerkki voi olla iso tai pieni e.
- jäsenet voi olla laskutoimituksia.
- myös sinit ja muut sellaiset tulee voida ratkaista. 
Tämmönen siitä tuli:
Koodia: [Valitse]
function minimi () { for n in $(seq $((${#@}+1))); do eval echo \$$n; done | tr '[]' '()' | sed 's/[eE]/*10^/g' | sed 's/^+//' | bc -l | tr -d '\\' | sort -nr | tail -1 ;}; minimi 1.1e3 s[.01]/c[.01] 1.2E3
**
Bash:issa kohtaa kummallisuuksia jatkuvasti, nyt tuli vastaan käsky:
Koodia: [Valitse]
function permutaatiot (){ eval echo $(printf "{%s,}" "$@"); }; permutaatiot {a..z}
käskynsuoritus kestää noin 2,5 minuuttia josta puolet näytölle kirjoittamista. Muodostuva datamäärä on noin 850 gigaa ja RAM:miin siitä ilmeisesti sijoitetaan 14G joten eipä ajankulu ole ihme - muodostettavien permutaatioiden lukumäärä on kyllä oikea elikä 67108863. En usko että muut kielet toimivat näinkään nopeasti. Muistitila jää varatuksi kunnes annetaan käsky: exec bash.
- lisäsinpä kirjaimen. Datamäärä nousi 1700 gigaan. RAM:mia kului 15,5G ja swap:pia 2.2G. Linux on kova peli ja bash osaa käskeä sitä.
**
Fifo:n ylläpitäminen levyllä on yksinkertaista:

Rivin lisääminen tiedoston huipulle: echo 'tämä on uusi rivi1' >> .fifo
Rivin lukeminen tiedoston pohjalta : apu=$(head -n 1 .fifo); echo $apu; sed -i '1d' .fifo
- kun rivin lukee niin se pitää poistaa, tämäntakia lukemisen perään tuo: sed -i '1d' .fifo
- tallettaa voi mitähyvänsä.
- pinoa ylläpidetään levyllä. Onhan se hidasta, mutta mitään ei häviä vaikka sammuttaisi koneensa.
- fyysisiä virheitä ei voi tulla ja loogisilta virheiltähän mikään ei suojaa.


Fifo voidaan toteuttaa matriisissakin - ja matriisi-toteutus on nopea. Siitä lienee viisainta esittää käyttökin:
Koodia: [Valitse]
#!/bin/bash
function lisää_pohjalle () { mat=("$@"); mat=($(echo -e ${mat[@]/%/\\n} | awk '{print NR" "$0}' | sort -k1 -nr  | sed 's/^[^ ]* //g')) ;}
function lue_pinnalta  () { eka=${mat[${#mat[*]}-1]}; unset mat[${#mat[*]}-1] ;} # kun jäsen luetaan täytyy se poistaa pinosta
unset mat; lisää_pinnalle {1..10000};  # lisätä voidaan yksi kerrallaan tai monta kerrallaan. Lisättävä voi olla mitä tyyppiä hyvänsä.
for n in {1..10000}; do lue_pinnalta; echo -n $eka' '; done
- tässävaiheessa ei ole tarkoitus tehdä jotain hyödyllistä, vaan tarkistella väitettä että bash on hidas ja kyvytön.  Eihän se 70mikrosekuntia per alkio ole yleensä nopeaa, mutta bash:ille kyllä on.



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.12.16 - klo:10.39
Kielen saattamiseksi rappiolle sen kehittäjille täytyy uskotella että kielen parhaat ominaisuudet ovat kelvottomia. Esimerkiksi esitetään että goto käsky on iljettävä. Bash:in kehittäjät ja sen parhaat "tukijatkin" ovat uskoneet sen ja esittäävätkin bash:in muuttujat ainoastaan luku-arvoja säilyttäviksi. Mutta bash:in muuttujat pystyvät säilyttämään myös ohjaus-rakenteita. Esimerkiksi:
a=ls; $a 
kutsuu funktiota ls joka listaa ihan samallatavoin kuin käsky ls olisi kirjoitettu suoraan. Parametritkin sallitaan, esimerkiksi a="cat /etc/fstab"; $a
joka toimii ihan samoin kuin käsky esitettäisiin suoraan: cat /etc/fstab
Tai käsky: teksturi=nano; $teksturi teksti
toimii ihan samoin kuin käsky: nano teksti
tai peräti: trappi="trap LopetaSkriptiJaPalaaSkriptiajuriin SIGINT"; $trappi
toimii ihan samoin kuin kirjoitettaisiin suoraan:  trap LopetaSkriptiJaPalaaSkriptiajuriin SIGINT
joka painettaessa ctrl-c siirtää ohjelman suorituksen funktioon nimeltä: LopetaSkriptiJaPalaaSkriptiajuriin

Kutsuttavat funktiot voivat olla myös itsemääriteltyjä; esimerkiksi
function hupi () { echo vitsi ;}; a=hupi; $a
tulostaa sanan vitsi. Suurissa skripteissä tämä on erittäin käyttökelpoista; muuttujan arvoa muuttamalla voidaan määrätä minne ohjelmansuoritus hyppää; vanhat muistavat basicin - tämä on sama kuin basic:in "GOSUB X OF ..." - kuitenkin osoitteet käsitetään funktionnimiksi eikä numero-osoitteiksi. Ja hyppy-osoitteita voidaan muuttaa ohjelmallisesti.
- funktiokutsu on ilmeisesti yhtä nopea näinkin - jokatapauksessa erot ovat mikrosekunteja.
- monet eturivin ohjelmoijat sanovat että goto on ihan käyttökelpoinen monessa paikassa. Niinkuin esimerkiksi Torvalds.
- toinen keino laittaa bash aisoihin on uskotella että käskyä eval ei saa käyttää turvallisuussyistä. En luultavasti ymmärtäisi vaikka selitettäisiinkin kuinka se riski syntyy mutta ihmetyttää miksei kukaan ole yrittänytkään selittää - ilmeisesti kukaan muukaan ei ymmärrä ja kaikki vain apinoivat jonkun irvileuan aloittamaa huhua. Sillä on aivan varmaa että se on turvallisuusriski - ihan niinkuin kaikki muukin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: matsukan - 24.12.16 - klo:12.23
>Kielen saattamiseksi rappiolle sen kehittäjille täytyy uskotella että kielen parhaat ominaisuudet ovat kelvottomia. Esimerkiksi esitetään että goto käsky on iljettävä. Bash:in kehittäjät ja sen parhaat >"tukijatkin" ovat uskoneet sen ja esittäävätkin bash:in muuttujat ainoastaan luku-arvoja säilyttäviksi. Mutta bash:in muuttujat pystyvät säilyttämään myös ohjaus-rakenteita. Esimerkiksi:

Tämä viha goto käsky juontuu basic kieleen ja sen puutteisiin. goto käskyä käyttäen voi tulla tapauksia jossa goto käsky joutuu väärään paikkaan esim versionhallinan takia. Esim parin kolmen vuoden taakaa muistan Applen koodeissa  ssl bugin johtuneen juurikin silloin kun käytettiin goto käskyä eikä esim aaltosulkeita.

Voihan olla että esim kernelin syvimmissä c kielen rakenteissa on hyödyllistä joskus käyttää goto käskyä.  Tai joissakin bash scripteissä. Mutta esim c++ koodeissa, goto käskyä ei tulisi käyttää.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.12.16 - klo:20.36
Tämä viha goto käsky juontuu basic kieleen ja sen puutteisiin. goto käskyä käyttäen voi tulla tapauksia jossa goto käsky joutuu väärään paikkaan esim versionhallinan takia. Esim parin kolmen vuoden taakaa muistan Applen koodeissa  ssl bugin johtuneen juurikin silloin kun käytettiin goto käskyä eikä esim aaltosulkeita.

Voihan olla että esim kernelin syvimmissä c kielen rakenteissa on hyödyllistä joskus käyttää goto käskyä.  Tai joissakin bash scripteissä. Mutta esim c++ koodeissa, goto käskyä ei tulisi käyttää.

Enpä tiennytkään ja kiva että kerroit. Sillä aina uudet tiedot oikaisee käsityksiä.
**
Edellinen oli tarinaa muuttujista. Puhutaanpa muuttujista toisesta näkökulmasta:

Bash tuntee vain tekstijonot. Kuitenkin tulkille voi antaa ohjeita kuinka sen pitää tekstijono tulkita silloin kun se pitäisi tunnistaa numeroksi. Bash osaa tulkita tekstijonon ainoastaan etumerkillisenä kokonaislukuna alueelta -9223372036854775808-9223372036854775807; mikäli arvo on näiden alueiden ulkopuolella niin tulkinta antaa väärän tuloksen eikä asiasta edes urputeta. Mikäli luvussa  on desimaalipiste niin välittämättä declare:sta muiodostetaan tekstijono - vasta kun tekstijonoa yritetään käyttää bash:in omissa matemaattisissa laskuissa se aiheuttaa virheen samoinkuin mikätahansa muukin aakkonen. Sanotaan ettei bashin muuttujilla ole tyyppiä - no sanotaan niin, mutta jokatapauksessa bash:in voi määrätä seuraaviin tulkintoihin kirjoittamalla skriptin alkuun:
 
declare a ;    määrittelee että a on etumerkillinen kokonaisluku väliltä: -9223372036854775808-9223372036854775807
declare -i a ; määrittelee että a on kokonaisluku samalta lukualueelta: näennäisesti ihan sama.
                                    - kuitenkin:"declare -i a; a=1+2; echo $a"  tulostaa 3. Kuntaas: "declare a; a=1+2; echo $a" tulostaa 1+2
declare -r a                ; määrittelee että muuttujan a voi ainoastaan lukea. Siis käytännössä määrittely on: "declare -r a=1" ja jos myöhemmin määritelee arvon uudestaan aiheuttaa se virheen.
declare -a muuttuja  ; luo muuttujan joka tulkitaan matriisiksi
declare -A muuttuja  ; luo muuttujan joka tulkitaan assosiatiiviseksi matriisiksi. Nimenomaan pitää huomioida että tulkitaan - itse matriisi on aivan samanlainen.
declare -f muuttuja  ; luo muuttujan joka tulkitaan funktioksi
declare -x muuttuja  ; luo muuttujan joka siirtyy ilmanmuuta myös "lapsi-prosessien" käyttöön.
declare a=373         ; muuttujalle voidan antaa arvo samalla kun se määritellään
declare -ira a           ; samallakertaa voidaan antaa useampiakin määreitä.
- mitään määrittelyjä ei tarvitse tehdä, sillä bash on kova olettamaan.
- jos määritetään a=0.001 niin a ei ole numero vaan tekstijono.
- funktioissa voi määritellä että muuttuja tunnetaan vain funktion sisällä, määrittely on: local muuttuja
- kaikki bashin muuttujat ovat globaalialueella elikä ne tunnetaan kaikkialla. Tämä saa naurettavaan valoon sen huomautuksen jonka useimmat esittävät: bash:in funktiot eivät osaa palauttaa arvoja. Eihän niiden tarvitsekaan palauttaa sillä arvothan ovat muuttuneet muutenkin - puute se on sittenkin mutta sen kanssa voi elää. Globaalialueella toimiminen ei aiheuta ristiriita-tilanteita massiivisissakaan skripteissä mikäli skriptaaja osaa nimetä muuttujat oikein.function summaa { echo summa=$( eval echo \$\{$1\[\*\]\} | tr ' ' + | bc -l) ;}; mat=({1..100000}); time summaa mat
- bash-skripteissä voi laskea myös desimaaliluvuilla, mutta itse bash ei laskemiseen sovellu vaan laskut on tarkoitus suorittaa funktiossa nimeltään bc; esimerkiksi: echo 0.001+0.002 | bc    tai toisella tavalla: bc <<< 0.001+0.002
- ei merkitse mitää ovatko bc:n syöttöluvut numeroita tai tekstijonoja kunhan niissä on vain numeroita, mahdollisesti molemmilla luvuilla etumerkit ja mahdollisesti yksi desimaalipiste.   
- muuten bc:ssä numeroiden määrää ei mitenkään rajoiteta vaan muuttujassa saa kokonaisosassa olla numeroita vaikka ziljoona, sitten desimaalipiste ja taas ziljoona numeroa desimaaliosassa. Samoin käytettävissä on funktiokirjasto, joten bash:issa matematiikka on helppoa. Ei niin että bc olisi hyvä matematiikkaohjelma, mutta helppo se on noissa yksinkertaisissa laskuissa.

- sekä tavallisen että assosiatiivisen matriisin sisäinen esitys on samanlainen; kyse on siis kuinka matriisit tulkitaan:
- tavallinen matriisi:     
määrittely: declare -a mat1; mat1[1]=1; mat1[2]=2; mat1[3]=3   koneen muistissa on: declare -a mat1=([1]="1" [2]="2" [3]="3" )
- assosiatiivinen matriisi:
määrittely: declare -A mat2; mat2[1]=1; mat2[2]=2; mat2[3]=3   koneen muistissa on: declare -A mat2=([1]="1" [2]="2" [3]="3" )
- siis koneen muistissa kumpikin määrittely on melkein samanlainen
- assosiatiivisen matriisin osoitteet voivat kyllä olla tekstiäkin ja silloinhan tavallinen matriisi ei edes toimisi, mutta esitystapa olisi silloinkin melkein samanlainen.

- hexa-luvuille  ei ole declarea. Hexa-alueen laskutoimitukset esimerkiksi  : echo $((0xF + 0xF))  joka tulostaa 30 - ilmeisesti kääntää esin hexat desimaalialueelle, suorittaa laskutoimitukset ja tulostaa aina desimaalisena.
- oktaaliluville ei ole declarea. Oktaalialueen laskutoimitukset esimerkiksi: echo $((8#100)) joka tulostaa 64     - ilmeisesti kääntää esin hexat desimaalialueelle, suorittaa laskutoimitukset ja tulostaa aina desimaalisena.
- bc voi suorittaa laskut mielivaltaisessa lukujärjestelmässä ja tulostaa myös mielivaltaisessa lukujärjestelmässä: echo "ibase=8; obase=2;11" | bc
**
Koska bash on niin tekstijono-suuntautunut ovat siinä useimmat datan-käsittely-käskytkin tarkoitettu tekstijonoille - mutta toisaalta numeroitakin voi käsitellä tekstijonoina ja joskus siinä on järkeäkin:
muuttuja=apua; echo ${muuttuja^}  # muuta ensimmäinen suuri kirjain suureksi
muuttuja=apua; echo ${muuttuja^^} # muuta kaikki kirjaimet suuriksi
muuttuja=APUA; echo ${muuttuja,}  # muuta ensimmäinen kirjain pieneksi
muuttuja=APUA; echo ${muuttuja,,} # muuta kaikki kirjaimet pieneksi
muuttuja=APUA; echo ${muuttuja~}  # muuta ensimmäinen kirjan toisentyyppiseksi kuin se oli
muuttuja=APUA; echo ${muuttuja~~} # muuta kaikki kirjaimet toisentyyppiseksi kuin ne olivat

muuttuja=appuva; echo ${muuttuja/p/k}  # muuta ensimmäinen p-kirjan k-kirjaimeksi - toimii kirjainryhmillekin ja mikäli on määrätty ryhmä niin yksi ei kelpaa - ryhmät voivat olla erisuuria eli tämä on super-tr
muuttuja=appuva; echo ${muuttuja//p/k} # muuta kaikki p-kirjaimet k-kirjaimiksi   - toimii kirjainryhmillekin ja mikäli on määrätty ryhmä niin yksi ei kelpaa - ryhmät voivat olla erisuuria eli tämä on super-tr

- ja vastaavia loppumattomiin, väsyin jo katsellessani
**
Bash:in matematiikka toimii vain kokonaisluvuille ja on muutenkin niin vikaherkkää että sitä kannattaa käyttää hyvin harkiten. Salahaudat poislukien: on se ainakin nopeaa - bash:iksi tarkoitan:
echo $((1+1))  tulostaa 2 elikä summan. - * ja / toimivat myös.
echo $((5%2))  tulostaa 1 eli jakojäännöksen
echo $((5**2)) tulostaa 25 elikä potenssiin korotuksen
echo $((5^2))  tulostaa 7 elikä binääri-lukujen 101 ja 10 bitwise-XOR:in; siis kyseessä ei ole potenssiin korotus
echo $((2|4))  tulostaa 6 elikä binääri-lukujen 10 ja 100 bitwise-OR:in. Muutkin loogiset operaattorit tunnetaan.
a=1; echo $((~a))  tulostaa 2. Negaatiostahan tässä kyse on mutta tulokseen lisätään jostasin syystä aina 1. option-base hommeli vaikka bash:issa ei sitä ole?
a=1; echo $((a<<=2)) tulostaa 4 elikä a:n binääri-esityksen siirrto 2 paikkaa vasemmalle ja tulos muutettu desimaaliluvuksi
- voi laskutoimituksia laittaa samaan useampiakin: echo $((5**2-8/2)) tulostaa 21
- sulutkin otetaan huomioon: echo $(((2+2)*3)) tulostaa 12 kuntaas: echo $((2+(2*3))) tulostaa 8
- muuten nopein tapa laskea kertoma on: echo $(($(seq -s* 20))) # kylläkin vain luvuista alle 20

Bash toimii kuitenkin loistavasti desimaaliluvuilla rajoittamattoman monen desimaalin laskutarkkuuden omaavan ohjelman bc avulla jolla on kytkimellä -l käytettävissään funktiokirjasto. Esimerkiksi:
echo 0.001+0.002 | bc    tai toisin: bc <<< 0.001+0.002            # normaalit laskutoimitukset; samat kuin bash:issakin
echo "for (i = 4.00; i < 5.00; i += 0.01)  i" | bc                                # desimaalinen looppi
echo 1.0000002 '>' 1.0000001 | bc                                               # desimaaliluvut vertaillaan bc:ssä. Tai luvut aina viisainta verrata bc:llä sillä bc toimii yhtähyvin myös kokonaisluvuille.
echo $(echo "s(4*a(1)*90/180)" | bc -l)                                         # 90-asteen sini. Siksi kaava on niin inhoittavan näköinen kun bc toimii radiaaneissa ja ihmiset haluaa toimia asteissa.
**
Koska loopit ovat bash:in pahiksia niin aloin etsiä niille korvaavaa toimintaa: tässä tapauksessa oli kyse pino-rakenteen tutkiminen: inhoitti ihan esittää lopputulos jossa pinon hoitamiseen kului 20ms ja looppeihin 700ms.
Tässä esitetty kertoo jotenkin  kuinka  "loopittomia looppeja" tehdään. Menetelmä saattaa johtaa johonkin: vaikka bash on jo melkein kuollut eikä tämmöiset enää auta mitään, niin sikäli asia on mielenkiintoinen että muut tulkkaavat kielet taitaa jäädä jalkoihin.
- "looppi" on nopea: 11 mikrosekuntia per kierros ja jokaisella kierroksella suoritetaan tässä yksi funktiokutsu ja matriisi-operaatio. Liian hyvää ollakseen totta?
Koodia: [Valitse]
function lisää_perään () { mat+=("$@") ;}; unset mat; $(seq -s' 'lisää_perään' ' 10000 | sed 's/^[0-9]*//'); echo ${mat[*]//lisää_perään/}
**
Nämä loopittomat ratkaisut ovat todella nopeita, mutta meidät kaikki on opetettu tekemään bash-skriptit siten että ne mählivät kaikkialla. Ja kun on opetettu väärin niin vain harvoin pystyy heittämään yhden väärin opitun pois, ja ennenkuin skriptit toimivat nopeasti ne täytyy kaikki karsia pois.

Tämä loopiton skripti laskee summan matriisista jonka nimi mainitaan. Kun matriisin nimen vaihtaa niin mitään muuta ei tarvitse muuttaa.
- vaikka puhutaan summasta niin monet muutkin matematiikka-operaatiot ovat mahdollisia. Muodostettavan matriisin "koolla ei ole ylärajaa", se voi olla vaikka satatuhat jäseninen kuten ({1..100000}). Matriisi muodostetaan ainoastaan testausta varten - matriisi voidaan yhtähyvin määritellä myös: mat=(6*6 -1*6^2 .00000000000000000000000000000000000000000001)
siis desimaalien luvulla ei ole mitään rajaa ja jäsenet voivat olla ratkaisemattomassa muodossa - myös sinit sun logaritmit tunnetaan mutta tutustu bc-hen ennen kuin käytät niitä.
Koodia: [Valitse]
function summaa { echo summa=$( eval echo \$\{$1\[\*\]\} | tr ' ' + | bc -l) ;}; mat=({1..100000}); time summaa mat
- time:n antama suoritusaika on noin .14 sekuntia ja se vaihtelee senverran kun skripteillä yleensäkin. Mutta ajettaessa skripti päätteessä ensimmäisen kerran kestää noin 1.8 sekuntia sellaista aikaa jolloin ei tunnu tapahtuvan mitään. Aika kuluu mat-matriisin täyttämiseen. Kun matriisi on kerran täytetty sen uudelleenmuodostaminen melkein-samanlaisena on huomattavasti nopeampaa joten ajettaessa skripti myöhemmin samassa päätteessä tuo 1.8 sekuntia vähenee .2 sekuntiin. Mikäli skriptiä ajetaan kymmeniä kertoja alkaa matriisin uudelleenmuodostusaika taas kasvaa.
- "skriptin" voi kopioida foorumilta Ubuntun päätteeseen ja painaa enter.

Inhottavamman näköinen versio poistaa turhat etu-plussat, muuttaa pi:n arvoksi 3.14... ja muuttaa e:n arvoksi 2.71... silloin kun on kyse luonnollisen lukujärjestelmän kantaluvusta ja muutoin eksponenttimuotoon:
Koodia: [Valitse]
function summaa { echo summa=$( eval echo \$\{$1\[\*\]\} | sed 's/^+//;s/ /+/g;s/pi/4*a(1)/g;s/\be\b/e(1)/g;s/\Be\B/*10^/g' | bc -l) ;}; mat=(e+2*pi 1e3); time summaa mat
**
Siitä tämä BASH on mukava, että kukaan ei voi koskaan tehdä sitä parasta versiota - en minä etkä sinä. Toisin ilmaistuna: ainoastaan kun pysyy hiljaa ei puhu potaskaa. Siis ei ole mitään paineita: teet mitä vaan niin pieleen menee.

Taas uusi versio loopittomasta bash-skriptistä matriisin jäsenen arvoa vastaavan osoitteen etsimiseksi. Skripti on montakertaa nopeampi kuin aikaisemmat ja se toimii aina jokseenkin yhtänopeasti.
- matriisin nimi voi olla mikävaan ja samassa skriptissä voidaan kutsua hakua eri paikoista - ja kokonaisluvuilla, desimaaliluvuilla tai sanoilla muodostetuista matriiseista - sanoissa saa olla välilyöntejäkin.

BASH:issa matriisin jäsenten osoitteet voivat pomppia sinnetänne jättäen osoitteita väliinkin. Ja matriiseissa yleensäkin samaa arvoa voi löytyä monestakin eri osoitteesta. Lisäksi bash:issa matriisin ensimmäinen numero-osoite voi olla mikähyvänsä positiivinen kokonaisluku - ja assosiatiivisessa matriisissa osoite voi olla tekstiäkin. Näiden ominaisuuksien vuoksi eivät "normaalit" skriptit toimi hyvin etsittäessä jäsenen arvoa vastaavaa osoitetta.
Koodia: [Valitse]
#!/bin/bash
function arvoa_vastaava_osoite () { osoitelista=($(declare -p | grep dec.*$1= | grep -Po \[[[:alnum:]]*\]=\""$2"\" | grep -Po '[[:alnum:]]*(?=])')); }
unset mat # unset tuhoaa matriisin, mutta myös tiedon siitä oliko se tavallinen vaiko assosiatiivinen. Tämä siis hyväksyy myös assosiatiiviset matriisit.
unset osoitelista
mat=($(seq 10000000 | awk '{print rand()}')) # luodaan kymmenmiljoonainen matriisi noin kuusinumeroisista satunnaisluvuista.
arvoa_vastaava_osoite mat 0.500489
echo etsittävä: .500489' löytyi seuraavista osoitteista:'
echo -e ${osoitelista[*]/#/\\n} | sed 1d
echo; echo ja varmistukseksi:
for (( n=0; n<${#osoitelista[@]}; n++ )); do echo osoitteessa:${osoitelista[@]:$n:1}' on arvo:'${mat[${osoitelista[@]:$n:1}]}; done
- tuon kymmenmiljoonaisen matriisin luominen satunnaisluvuista kestää noin 12 sekuntia ja etsiminen sieltä 3.5 sekuntia elikä 0.35mikrosekuntia osoitteelta .  Osoitteitahan tulee monta sillä laskettaessa 6-numeroinen satunnaislukuluku 10 miljoonakertaa saadaan sama luku yleensä 6-14 kertaa.
- kyllä tämä toimii erinomaisesti pienienkin matriisien kanssa. Silloikin kun arvot ovat tekstijonoja ja assosiatiivisissa osoitteetkin saavat olla tekstijonoja.
**
Bash:in matriisit ovat tosiaan niin kummalliset että ainoa keino saada varmasti oikea käsitys matriisin kokoonpanosta on katsoa millaisen määrittelyn bash itse on siitä muistiin muodostanut - lisäksi se on ehdottomasti nopein keino. Esimerkiksi matriisien yhtäsuuruuden tarkistamiseksi on lukemattomia toinen toistaa etevämpiä keinoja mutta kaikki ne kompastuvat johonkin.
Mutta tämä keino ei kompastu mihinkään ja se sopii yhtähyvin yksinkertaisiin matriiseihin kuin hyper-monimutkaisiinkin, tavallisiin ja assosiatiivisiin. Ja se on bash:iksi salamannopea:
Koodia: [Valitse]
function esitysmuistissa () { echo "$(declare -p | grep -Po "(?<=declare -[aA] $1=).*")" ;}
mat1=({1..100}); mat2=({1..100}); [[ "$(esitysmuistissa mat1)" = "$(esitysmuistissa mat2)" ]] && echo matriisit ovat samanlaiset || echo matriisit ovat erilaiset


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.01.17 - klo:18.33
Bash:ista löytyy jatkuvasti uusia piirteitä: ensin nuo "loopittomat-loopit" toivat nopeutta kymmenkertaisesti ja nyt tämä tapa käsitellä matriiseita - se nopeuttaa kaikenlaisia matriisinkäsittelyjä vielä enemmän eikä valehtele koskaan.
- eikä mikään näistä ole "uusi keksintö" vaan virtuoosit ovat tunteneet nämä aina.
- enää en vähättele bash:in kykyjä luettuani yhden virtuoosin kuukausi sitten kertomaa: esimerkiksi skripti joka koostuu yhdestä grep-komennosta on joissain tilanteissa nopeampi kuin C:llä saa aikaiseksi. Joskus harvoin muutenkin teet bashilla nopeamman skriptin kuin C:llä saat aikaiseksi  -  puhumattakaan muista kielistä.

Tässä esimerkki assosiatiivisen matriisin tulostamisesta silloin kun sekä arvossa että osoitteessa saattaa olla välilyöntejä; siitä ei mikään muu tulostustapa selviä:
Koodia: [Valitse]
#!/bin/bash
function tulostamatriisi () { tabs 25; echo -e osoite'\t'arvo; echo; declare -p | grep dec.*$1= | cut -d\( -f2 | tr -d []\) | sed 's/" /"\n/g;s/=/\t/g' ;}
declare -A mat

mat[alussa]=kiitosseisoo
mat[sutta ja sekundaa]="muodostetaan tauotta"
mat[surullista]="mutta totta"
 
tulostamatriisi mat
**
Olin jo kovin varma siitä että tämä muistikuvan tulkintaan perustuva menetelmä on aina super-nopea. Joten alkaessani tehdä muistikuvan tulkintaan perustuvaa matriisin levylle talletusta ja levyltä lukua odotin erinomaisia tuloksia. Miljoona-jäsenisen matriisin kirjoittaminen kävikin nopeasti, mutta lukeminen kesti 4.5 sekuntia. Pettyneenä tuohon tulokseen melkein lakkasin kehittämästä noita levy-operaatioita. Kunnes tulin mitanneeksi kuinka kauan muilla menetelmillä kestää: loopittomalla menetelmällä lukeminen kesti 2.5 tuntia ja tavallisella menetelmällä lukee vieläkin kun vuorokausi on kulunut - eikä niillä edes saa täysin moitteetonta lopputulosta.
**
Leikkaa-liimaa seuraavat neljä koodiriviä yksi kerrallaan päätteeseesi jotta toiminta selviäisi:

Tehdään satatuhat-jäseninen matriisi kokeiluja varten:
Koodia: [Valitse]
mat=($(seq 100000))
Talletetaan se levylle:
Koodia: [Valitse]
echo "$(declare -p | grep -P "declare -[aA] mat=.*=\".*")" > ~/.matriisi
Nollataan matriisi ja tarkistetaan onko se varmasti nollattu::
Koodia: [Valitse]
unset mat; echo ${mat[*]-matriisi on tyhjätty}
Luetaan se levyltä takaisin ja tulostetaan:
Koodia: [Valitse]
apu1=$( cat ~/.matriisi) && eval "$apu1" && echo ${mat[*]}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 13.01.17 - klo:00.24
- enää en vähättele bash:in kykyjä luettuani yhden virtuoosin kuukausi sitten kertomaa: esimerkiksi skripti joka koostuu yhdestä grep-komennosta on joissain tilanteissa nopeampi kuin C:llä saa aikaiseksi. Joskus harvoin muutenkin teet bashilla nopeamman skriptin kuin C:llä saat aikaiseksi  -  puhumattakaan muista kielistä.

Jep. Tuollaiseen käyttöön shell-skriptit ovat mainioita. Ja muutenkin eri komentoriviohjelmien kutsumiseen ja orkestrointiin.

Tehdään satatuhat-jäseninen matriisi kokeiluja varten:
Koodia: [Valitse]
mat=($(seq 100000))
Talletetaan se levylle:
Koodia: [Valitse]
echo "$(declare -p | grep -P "declare -[aA] mat=.*=\".*")" > ~/.matriisi
Nollataan matriisi ja tarkistetaan onko se varmasti nollattu::
Koodia: [Valitse]
unset mat; echo ${mat[*]-matriisi on tyhjätty}
Luetaan se levyltä takaisin ja tulostetaan:
Koodia: [Valitse]
apu1=$( cat ~/.matriisi) && eval "$apu1" && echo ${mat[*]}

Tällaisia voi toki huvikseen kikkailla, mutta taulukoiden käsittely on kyllä aika tuskaista ja virhealtista Bashin tarjoamilla välineillä. Tässä tallennat taulukon tiedostoon bash-koodina ja suoritat koodin ladattaessa. Ei kovin kaunista, eikä erityisen hyvä ajatus silloin kun luettava tiedosto ei ole itse tuotettu. Siellä voisi olla mitä tahansa vihamielisen tahon lisäämiä komentoja, jotka iloisesti suoritat.

Oikeasti data kannattaisi tallentaa vaikkapa JSON-muodossa, jos halutaan että se on luettavissa selväkielisenä tekstinä. Komentorivillä JSON-datan käsittely onnistuu jq:lla (https://stedolan.github.io/jq/), mutta se ei valitettavasti kuulu minkään jakelun perusasennukseen.

Huomattavasti parempi työkalu tällaisiin hommiin olisi Python.  :)

Koodia: [Valitse]
#!/usr/bin/env python3

import json

mat = [ x for x in range(1, 1000001) ]
with open("numeroita.json", "w") as f:
    json.dump(mat, f)

with open("numeroita.json", "r") as f:
    newmat = json.load(f)
    print(newmat)

Koodistakin saa jotain selvää ilman mustaa vyötä.   ;D
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.01.17 - klo:11.20
Onneksi ei ole pelkoa siitä että juuri kukaan edes harkitsee isompien bash-skriptien tekemistä. Minä harrastan sitä siksi että bash:in parissa on mukava työskennellä koska lähes kaikki netissä oleva tieto siitä on ajalta jolloin netinkin kirjoituksissa oli paljon asiaa eikä juuri ollenkaan pahiksia - edes fork-bomb:ia en ole kohdannut.

Mutta ilmeisesti tuo eval-juttukin on saamassa korjausta: bash4.4:ssä voi säätää sen maksimin johon eval suostuu: voidaan määrätä että esimerkiksi muuttujissa oleva teksti ratkaistaan mutta siinä olevaa koodia ei suoriteta. Niin ainakin luulen:     /* Define to the maximum level of recursion you want for the eval builtin. 0 means the limit is not active. */ #define EVALNEST_MAX 0 

- korjaus: taitaakin vaatia koko bash:in uudelleenkääntämistä  ?

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 13.01.17 - klo:13.52
Onneksi ei ole pelkoa siitä että juuri kukaan edes harkitsee isompien bash-skriptien tekemistä. Minä harrastan sitä siksi että bash:in parissa on mukava työskennellä koska lähes kaikki netissä oleva tieto siitä on ajalta jolloin netinkin kirjoituksissa oli paljon asiaa eikä juuri ollenkaan pahiksia - edes fork-bomb:ia en ole kohdannut.

Kyllähän hyvää tietoa tuotetaan jatkuvasti. Yleiset ohjelmointikielet ja monet harrastajien kirjoittamat kirjastotkin on useimmiten dokumentoitu erittäin kattavasti ja kauniisti. Tietoa jaetaan asiantuntijafoorumeilla kuten Stack Overflow'ssa, jossa kokeneet koodaajat äänestävät huonot neuvot alas ja hyvät ylös. Skriptausongelmiinkin löytyy parhaiten apuja tuollaisilta foorumeilta.

Mutta ilmeisesti tuo eval-juttukin on saamassa korjausta: bash4.4:ssä voi säätää sen maksimin johon eval suostuu: voidaan määrätä että esimerkiksi muuttujissa oleva teksti ratkaistaan mutta siinä olevaa koodia ei suoriteta. Niin ainakin luulen:     /* Define to the maximum level of recursion you want for the eval builtin. 0 means the limit is not active. */ #define EVALNEST_MAX 0 

- korjaus: taitaakin vaatia koko bash:in uudelleenkääntämistä  ?

Yksikin taso on liikaa tällaisessa käytössä. :)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 16.01.17 - klo:20.18
Matriisin lukemisessa tiedostosta tai kirjoittamisessa tiedostoon voi eval-käskyn korvata käskyllä: . joka liittää määrätyn tiedoston koodiin mutta ei suorita sitä. Mutta tämä edellyttää siirtymistä tutkimaan ympäristömuuttujia elikä muistikuvan käsittelyyn.
- muistikuvan selvitys: esimerkiksi teet skriptissäsi matriisin mat=(1 2 3). Bash tekee siitä ympäristömuutujiin muistikuvan: mat=([0]="1" [1]="2" [2]="3"). Saat sen nähdäksesi käskyllä:
declare | grep ^mat= .
- muistikuvan käsittely mullistaa kaikki matriisi-operaatiot, esimerkiksi nopeus nousee isojen matriisien käsittelyssä yleensä luokkaa 1000* . Liian vähän ja liian myöhään.
- luettaessa tiedosto tarkastetaan se ensiksi erillisessä prosessissa. Jos luettavassa tiedostossa on mitään muuta kuin kelvollinen matriisimäärittely tai matriisin jäsenissä on kiellettyjä merkkejä niin tiedostoa ei edes yritetä lukea vallitsevassa prosessissa. 
- vaikka menetelmä ei kompastu mihinkään niin kaikki toteutukset kompastuvat johonkin. Siis paranneltavaa on aina ja varsinkin varmistusta voi parannella loputtomasti.
- suoritusnopeus on verrannollinen matriisin merkkimäärän neliöjuureen. Vanhoilla menetelmillä suhde oli exponentiaalinen eivätkä ne suurilla matriiseilla edes toimineet.
- bash:in matriisin jokaisella jäsenellä on sekä osoite että arvo. Mikäli osoitteesta ei huolehdita niin numeeristenkaan matriisien käsittely ei toimi aina. Muistikuvassa osoitetieto on aina mukana mutta "normaali-toteutuksilla" hyvin harvoin sillä osoitteen huomioiminen hidastaa toimintaa puoleen.
- assosiatiivisilla matriiseilla on pakko ottaa osoite huomioon.
- jokaista matriisia voi käsitellä muistikuvansa avulla mitenkä hyvänsä ja nämä käsittelytavat ovat salamannopeita verrattuna vanhoihin menetelmiin ja ne ovat myös luotettavampia. Matriisin muistikuvan käsittelyyn perustuvat käsittelytavat ovat pienillä matriiseilla vain hieman "normaali-toteutuksia" nopeampia, mutta suurilla käytännön matriisella jopa tuhatkertaa nopeampia. Esimerkiksi matriisin lukeminen tiedostosta: miljoonajäsenisen matriisin lukeminen kestää muistikuvan avulla 5 sekuntia ja "normaali-toteutuksilla" monta tuntia ja tulos saattaa silti olla väärä. Mutta muistikuvan käsittely ei voi virheillä - toki senkin voi ohjelmoida väärin mutta se on oikaistavissa.
- toiminnan tarkistamiseksi tein tuhansia koe-ajoja. Tottakai minä olen yhtä rajoittunut kuin kukahyvänsä enkä ehkä ole huomannut testata jotakin. Mutta toisaalta kaikki testit ovat onnistuneet.
- talletus ja luku toimivat oikein vaikka matriisissa olisi kontrollimerkkejä mutta tulostus nikottelisi. Voisihan ne kontrollimerkit kieltääkin.

Tein matriisin levylle-talletuksen ja levyltä-lukemisen tutkimiseksi skriptin. Sen merkitykselliset funktiot ovat: matriisintalletus ja matriisinluku. Muulla osalla skriptiä saa helposti aikaan sellaisen tilanteen kuin haluaa noiden funktioiden tarkistamiseksi. 
Koodia: [Valitse]
 
#!/bin/bash
function matriisintalletus () { declare | grep ^$1= > $2 ;}
function matriisinluku () { ( [[ $(grep -Pv ^$1=\(.+\)$ $2) || $(grep -P '( /[[:alnum:]]|\brm\b|\$\()' $2) ]] && echo $2 on kummallinen && return 1 || return 0;) && (( ! $? ))  && . $2 ;}
function matriisinkirjoitusriviin () { tabs 30; echo -e 'osoite\tarvo'; declare | grep "$1"= | sed "s/$1=//" | sed 's/\" \[/\"\n\[/g' | tr -d \(\)[] | tr = '\t' ;}
function matriisinkirjoitusjonoon () { echo osoitteet:; declare | grep ^$1  | grep -o \[[0-9]*\] | tr '\n' ' ' | tr -d []; echo
                                       echo arvot:;     declare | grep ^$1= | sed "s/$1=//" | tr -d \(\)  | sed 's/\ \[/\n/g' | sed 's/.*=\"//' | tr -d \" | tr '\n' ' ' ;}
declare -A matriisinnimi
matriisinnimi[alussa]=kiitosseisoo
matriisinnimi[sutta ja sekundaa]="muodostetaan tauotta"
matriisinnimi[surullista]="mutta totta"

alkuhetki1=$(date +%s.%N); matriisintalletus matriisinnimi tiedosto; loppuhetki1=$(date +%s.%N)
unset matriisinnimi; declare -A matriisinnimi # unset hävittää myös tiedon oliko matriisi tavallinen vai assosiatiivinen joten tarvittaessa se täytyy palauttaa
alkuhetki2=$(date +%s.%N); matriisinluku matriisinnimi tiedosto; loppuhetki2=$(date +%s.%N)
echo;  matriisinkirjoitusriviin matriisinnimi; echo -n 'talletusaika='; echo $loppuhetki1-$alkuhetki1 | bc; echo -n '    lukuaika='; echo $loppuhetki2-$alkuhetki2 | bc

read -p 'paina enter nähdäksesi toisentyyppisen matriisin' ; clear
matriisinnimi2=($(seq 1000))
unset matriisinnimi2[55]
matriisinnimi2[500]=" / 9rmkkkk" # tätä muuttelemalla näkee millaisia tiedostoja jäseniä kartetaan.
alkuhetki1=$(date +%s.%N); matriisintalletus matriisinnimi2 tiedosto2; loppuhetki1=$(date +%s.%N)
unset matriisinnimi2
alkuhetki2=$(date +%s.%N); matriisinluku matriisinnimi2 tiedosto2; loppuhetki2=$(date +%s.%N)
echo; matriisinkirjoitusjonoon matriisinnimi2; echo; echo -n 'talletusaika='; echo $loppuhetki1-$alkuhetki1 | bc; echo -n '    lukuaika='; echo $loppuhetki2-$alkuhetki2 | bc
echo; echo huomioi että osoite:55 ja arvo:56 puuttuvat ja että arvon:501 paikalla on määräämäsi arvo.; echo
read -p 'paina enter jatkaakseesi'
**
Ilmeisesti muistikuvan hyödyntämiseen perustuvan funktion voi tehdä mihin tehtävään tahansa: olen tehnyt niitä jo kymmenkuntaan erityyppiseen tehtävään ja jokakerran funktion on saanut muodostettua heti. Tässä viimeinen:
Koodia: [Valitse]
#!/bin/bash
mat=($(seq 1000000))
function matriisintunnuslukuja () { declare | grep ^$1= | tr ' ' '\n'| cut -d ] -f1 | tr -d [ |\
awk '{sum=sum+$0; lkm++}END{print "matriisin summa:"sum" keskiarvo:"sum/lkm}' ;};  matriisintunnuslukuja mat
- muuten tavallisilla muuttujilla on myös muistikuvansa.
**
Nämä bash:in skriptit ovat juuri sitä mitä vanhat aivot tarvitsevat jotta dementia vähän viivyttelisi. Lisäksi bash on kuollut jo joten mitään paineita ei ole; koska ei voi onnistua niin ei voi epäonnistuakaan. Mutta sikäli vanhat bash-skriptit ovat haastavia että ne ovat niin hitaita ja rajoittuneita  että niitä täytyy parantaa ennenkuin pääsee tositoimiin. Kummassakin suhteessa parannusten täytyy olla "kymmenkertaisia".

Tein matriisin minimin ja maximin etsivän skriptin. Skriptin pitää toimia oikein olipa matriisi om muodostettu numeroista, tekstijonoista, tai numeroiden ja tekstin sekasotkusta. Tekstijonoissa täytyy saada olla välilyöntejä silloinkin kun ne ovat assosiatiivisen matriisin osoitteissa.
 
Yksi koitos etsimisen toimimiselle on muodostaa sellainen matriisi jonka jäsenmäärä on sama kuin satunnaisluku-generaattorin mahdollisten erilaisten arvojen lukumäärä, täyttää matriisi samalla generaattorilla tehdyillä satunnaisluvuilla ja etsiä sitten matriisin minimi ja maximi. Tämä testaa samalla satunnaislukugeneraattoria, sillä mikäli satunnaislukugeneraattori on hyvä niin tuloksen pitää olla eri ajokerroilla yleensä teoreettinen minimi ja maximi, harvemin poiketen yhdellä, vielä harvemmin poiketen kahdella.... Bash:in satunnaislukugeneraattori on hyvä 16-bittiseksi ja sillä onkin näin. Awk:in satunnailukugeneraattori on näiden tuloksien mukaan noin 26-bittinen joten sen tutkiminen vaatii matriisin koon olevan yli 33 miljoonaa mikä vaatii paljon RAM:mia. Heittoja täytyy olla sillä eihän ne muuten satunnaislukuja olisi eikä minimin etsintäkään toimisi.
- bash muodostaa satunnaislukunsa kokonaisluku-alueelle välille: 0-32767 ja awk muodostaa satunnaislukunsa desimaali-alueelle välille: 0-1.
- maximin ja minimin suhde kertoo sen kuinka monibittisestä satunnaislukugeneraattorista on todennäköiseti kyse.
Koodia: [Valitse]
#!/bin/bash
mat=($(for n in $(seq 32767 ); do echo $RANDOM; done ))        # Bash:in satunnaislukugeneraattori.
#mat=($(seq 33554432 | awk '{printf "%1.9f\n", rand()}'))    # awk:in satunnaislukugeneraattori # parempi: awk -v seed=$RANDOM 'BEGIN{srand(seed)}{printf "%1.9f\n", rand()}'))
# seuraava on etsii osoitteiden minimin ja maximit:
# function minimi (){ declare | grep ^$1= | cut -d \( -f2 | sed "s/\ \[/'\n'/g" | cut -d = -f1 | tr -d \)\[\] | sort -n | sed -e 1b -e '$!d' ;}; minimi mat
# seuraava etsii arvojen minimit ja maximit:
function minimi (){ echo matriisin minimi ja maximi: ; declare | grep ^$1= | cut -d \( -f2 | sed "s/\ \[/'\n'/g" | cut -d \" -f2 | sort -n | sed -e 1b -e '$!d';}; minimi mat
**
Edelliset esimerkit käsittelivät sellaista yksiulotteista matriisia joka on jo koneessa määriteltynä. Mutta levyllä olevaa kaksiulotteista matriisia voi käsitellä vähän samantyyppisesti niinkuin esimerkiksi tällä joka etsii kaksiulotteisen levy-matriisin määrätyn sarakkeen maximin ja minimin:
Koodia: [Valitse]
#!/bin/bash
function maxmin () { echo Tiedoston:$1 sarakkeen:$2 maximi ja minimi:;\
mat=($(cat $1 | cut -d' ' -f$2)); declare | grep ^mat= | cut -d \( -f2 | sed "s/\ \[/'\n'/g" | cut -d \" -f2 | sort -n | sed -e 1b -e '$!d' ;};\
maxmin tiedosto 2
- tämäkin on nopea jne. ja toimii myös tekstijonojen kanssa jotka voitaisiin sortata tekstijonossa olevien numeroiden mukaan.
- kutsu pitää olla samanlainen kuin muissakin kielissä: maxmin tiedostonimi käsiteltävän_sarakkeen_numero - jotta kutsumuodon tietäisi vaikkei olisi koskaan kuullutkasan.
- tuo maximin ja minimin etsiminen olisi helpointa toteuttaa awk:illa, mutta awk:in avulla saatavat tulokset ovat kovin "vähänumeroisia" ja vaikka se ei yleensä merkitse mitään niin yleiskäyttöisessä funktiossa se ei käy. Gawk:issa on mukana rajoittamattoman tarkkuuden kirjasto, mutta gawk ei ole Ubuntussa ilmanmuuta.
- jäsenten osoitteet ovat levyllä olevilla kaksiulotteisilla matriiseilla samanlaisia mielikuvituksettomia kuin muiden kielien matriiseissa. Eikä assosiatiivisuudesta ole puhettakaan.
- testattaessa tiedostoksi voidaan laittaa miljoonarivinen matriisi kirjoittamalla tiedoston paikalle <((seq 1000000)) . Sen minimi on 1 ja maximi 1000000. RAM:mista toimiminen nopeuttaa tässätapauksessa noin 40%.
- levyllä olevassa matriisissa saa olla tyhjiä rivejä, eikä rivien tai sarakkeiden täydy olla yhtäpitkiä muutenkaan.

Skriptistä voi tehdä myös version levyllä olevan kaksiulotteisen matriisin rivien käsittelemiseksi muuttamalla käsky: cut -d' ' -f$2  käskyksi: sed "$2q;d". Tätä uutta funktiota kutsutaan näin: maxmin tiedostonimi käsiteltävän_rivin_numero.

Levylle kirjoitetun kaksiulotteisen matriisin voikin aina käsittellä yhtähyvin kuin jo muistissa olevan. Levyllä olevan kaksiulotteisen matriisin käsittelyssä on paras unohtaa että matriisin jäsenillä on myös osoite.
Epäilemättä myös moniulotteisia matriiseja pystyy käsittelemään, mutta funktion tekeminen saattaisi olla vaikeaa.
**
Skriptiä kokeillakseen ei kannata tehdä omaan koneeseen skriptiä, vaan leikata skripti verkkosivulta ja liimata se oman koneen päätteeseen ja painaa enter.
**
Tähän asti nopein keino muodostaa numeerinen matriisi on ollut: matriisinnimi=($(seq 10))   ja sen muistikuva (jollaisia joskus näkee):
matriisinnimi=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")

ZestyZapuksessa toimii kolme-neljäkertaa nopeampi tapa muodostaa numeerinen matriisi: readarray matriisinnimi <<< $(seq 10) mutta sen muistikuva on toistaiseksi ennennäkemättömän kummallinen:
matriisinnimi=([0]=$'1\n' [1]=$'2\n' [2]=$'3\n' [3]=$'4\n' [4]=$'5\n' [5]=$'6\n' [6]=$'7\n' [7]=$'8\n' [8]=$'9\n' [9]=$'10\n')
- toiminta tuntuu kuitenkin olevan kummallakin tavalla muodostetuilla matriiseilla sama
- readarray on toiminut kaiketi bash4:stä lähtien ja siten tämäkin. Mutta muistikuva muodostuu vasta nyt edes jotenkin. Kuinka ne aikaisemmat ovat voineet toimia kun muistikuvat ovat olleet vajaita?
- arvojen tulostaminenmuistikuvasta kummallakin tavalla muodostetulla matriisilla:
Koodia: [Valitse]
declare | grep matriisinnimi= | tr ' ' '\n' | sed 's/.*\=//g' | tr -dc '\n'[[:alnum:]]* | tr -d n$
**
Bash 4.4:ssä on mahdollistettu muistikuvien helppo lukeminen. Esimerkin muodossa esitettynä:
Koodia: [Valitse]
katti=(1 2);apu=${katti[*]@A}; echo "$apu" 
tulostaa: declare -a katti=([0]="1" [1]="2")
- siis kun tuon apu:n tallettaa niin koko matriisimäärittely menee levylle joten luettaessa myös matriisin tyyppi luetaan.
- muuten matriisin tyypin selvittämiseksi joutuisi käyttämään käskyä:
Koodia: [Valitse]
[[ "$(declare -p mat)" =~ "declare -A" ]] && echo asso matriisi; [[ "$(declare -p mat)" =~ "declare -a" ]] && echo tavallinen matriisi
**
Minkähän takia ohjekirjoissa ei ole yksinkertaisia esimerkkejä: lyhyt esimerkki kertoo koko homman mutta tuhatrivinen dokumentti vain sotkee.  Esimerkiksi matriisin tyypin ja muiden parametrien selvittäminen:
Koodia: [Valitse]
declare -Air mat=([0]="1" [1]="2" [2]="3"); echo ${mat@a}

mikä tulostaa Air . Koetapas hakea tämä "Bash-raamatusta"
**
Matriiseja sortatessa on totuttu jättämään osoite huomioimatta sillä osoitteen huomioiminen vaikeuttaa paljon ja muissa ohjelmointikielissä tieto-kato on usein merkityksetöntä.
Mutta bash:in matriisin osoitteissa on enemmän tietoa joten osoitteiden huomiottajättäminen kadottaa usein oleellista tietoa.

Assosiatiivisen matriisin sorttaamisessa osoitteet on pakko huomioida kaikissa kielissä.

Aikaisemmin ison matriisin sorttaaminen ottaen osoite huomioon on ollut niin sietämättömän hidasta että ongelma ratkaistiin yleensä olemalla sorttaamatta. Mutta sorttaaminen muistikuvan avulla on bash:iksi nopeaa, noin 2 sekuntia kuluu miljoonarivisen matriisin sorttaamisessa.

Osoitteen ottaminen huomioon luo seuraavankaltaisen ongelman: matriisin jäsenen osoite ja arvo kuuluvat yhteen ja kun toinen sortataan täytyy toisen seurata mukana. Ongelma on siis sama kuin sortatessa teksti jonkun kentän perusteella: rivit täytyy pitää yhtenäisenä.

Sorttatessa muistikuvan avulla on osoitteet helpompi ottaa huomioon. Eikä muistikuva ota kantaa siihen onko matriisi tavallinen vai assosiatiivinen, se ei välitä välilyönneistä ja funktion voi muodostaa käyttämättä eval-käskyä.

- nämä skriptini käsittelevät sellaisia asioita joilla ei ole enää käytännön merkitystä riippumatta siitä millä kielellä ne olisi tehty, mutta bash:illa tehtynä ne ovat todella surkeita. Mutta on mielenkiintoista yrittää löytää hyvää tuosta pahnanpohjimmaisesta. Sillä bash:in väitetään olevan kymmenkertaisesti huonompi kuin se todellisuudessa on. Halutaanko estää se että ihmiset hoitaisivat PC:nsä turva-ongelmat itse?
Koodia: [Valitse]
#!/bin/bash
function sorttaamatriisi () { declare | grep ^$1= | sed "s/$1=//;s/\s\[/\n/g" | tr -d \(\)[]\" | sort -n -t= -k$2 | tr = '\t' ;}

#testi1: declare -A mat; mat[a c]=3; mat[c a]="1 2"; mat[a b]=2 # testi 2: 
mat=($(seq 1000000 | awk '{print 1000000*rand()}'))

tabs 30; alkuhetki=$(date +%s.%N); sortattu=$(sorttaamatriisi mat 2); loppuhetki=$(date +%s.%N);
echo -e "osoite\tarvo"; printf "%s\n"  "$sortattu"; echo -n sorttaukseen kulunut aika sekuntia: ; echo $loppuhetki-$alkuhetki | bc
# sorttauskäsky on muotoa: sorttaamatriisi matriisinnimi sorttaus_kentän_numero 
# Sorttaus_kentän_numero -> 1=osoitteen mukaan ja 2=arvon mukaan
# sorttauksen parametreja täytyy edelleen määritellä halutuiksi; esimerkiksi -n sorttaa todellisen numeroarvon perusteella jne.


 

 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.02.17 - klo:13.31
Aina on väitetty ettei bash osaa palauttaa funktion parametria. Niin väitetään edelleenkin, vaikka parametrin palautustapoja on useitakin. Sillä bash ei toimi tässäkään asiassa niinkuin muitten kielien virtuoosit pitävät oikeaoppisena. Mutta eikö lopputulos ole se mikä ratkaisee? Jotkut parametrin palautusmenetelmät käyttävät kovalevyä ja toiset eivät; tässä yksi sellainen joka ei käytä kovalevyä, eikä se käytä eval-käskyäkään - ja tällä menetelmällä voi palauttaa halutun määrän parametreja:
Koodia: [Valitse]
#!/bin/bash
function palautaparametri () { read -p "kirjoitapa jotakin: " parametri
read $1 < <(echo "$parametri") # tämä käsky siirtää määrättävän parametrin muutetun arvon globaalialueelle. Ainoastaan tämä lause on tarpeen.
}

unset jotakin ; palautaparametri jotakin  # 'unset jotakin' vain varmistaa ettei echota jotakin joka on jäänyt edellisestä ajosta.
unset muuta  ; palautaparametri muuta    # 'unset jotakin' vain varmistaa ettei echota jotakin joka on jäänyt edellisestä ajosta.
echo $jotakin
echo $muuta
Menetelmä on ollut päivittäisessä käytössäni jo iäisyyden ja se on aina toiminut moitteetta.
- pääohjelmaan sijoitettu 'command substitution' palauttaa funktion tulostaman arvon. Command sustitution -> rakenne: $(funktion kutsu parametreineen)
- funktion 'returnvalue' kannattaa jättää asettamatta.
- tämä 'parametrien palautus' on toinen asia kuin:
Koodia: [Valitse]
function myfunc() { myresult='some value' ;}

myfunc
echo $myresult
sillä siinä ei ole kyse parametrista - toisinsanoen se palauttaa funktiossa määrätyn muuttujan eikä pääohjelman funktiokutsussa määrättyä muuttujaa.
**
Mikäli yritettäisiin käyttää edellisen kaltaisessa funktiossa parametrien arvoja ei se onnistuisikaan, sillä muuttujista on passattu vain nimet eikä arvoja - nimestä tosin saa arvon kirjoittamalla:
Koodia: [Valitse]
eval \$$nimi
mutta jos eval-käskyä ei haluta käyttää niin joko kutsuun on liitettävä myös arvot tai funktion alkuun liitettävä lause: 
Koodia: [Valitse]
parametri1=${!1}
ja käytettävä tarvittaessa näin saatuja arvoja.
- siis parametri1 on muuttujanimen $1 arvo. Usealle parametrille voidaan liittää oma lause.


Edellinen esimerkki muuttuisi seuraavankaltaiseksi:
Koodia: [Valitse]
#!/bin/bash
function palautaparametri () { parametri=${!1}   
read -e -p "editoi parametria: " -i "$parametri" parametri
read $1 < <(echo "$parametri") # tämä käsky siirtää määrättävän parametrin muutetun arvon globaalialueelle. Ainoastaan tämä lause on tarpeen.
}

jotakin=data0001; palautaparametri jotakin    # siis passataan muuttujan nimi; arvon jos passaa niin ei onnistu näin
muuta="78 56"       ; palautaparametri muuta  # arvoissa saa olla välilyöntejä mutta se on silloin laitettava lainausmerkkeihin niinkuin bash:issa muutenkin.   
echo editoitu arvo: "$jotakin"
echo editoitu arvo: "$muuta"
-editoitavassa saa olla mikä hyvänsä merkkiryhmä, vaikka yksinäinen kova lainausmerkki. Välilyöntejäkin saa olla.
**
Aikaisemmin esitetyissä sorttauksissa ainoastaan tulostetaan ja joskus tuloste ei riitä vaan matriisi täytyy luoda sortattuna uudestaan.:

Kuvaus vaikeuksista joita saattaa tulla:
1. Matriisin sorttauksessa voi sortata arvon tai osoitteen mutta kumpi sortataankin täytyy toinen hylätä -> oletetaanpa että on sortattu arvot. Sorttauksessa muodostuneen uuden matriisin osoitteilla ei  ole mitään korrelaatiota vanhojen osoitteiden kanssa joten miksi raahata matkassa niitä vanhoja osoitteita ? Sorttaustuloksesta muodostuvan uuden matriisin osoitteet alkavat nollasta ja kasvavat siitä monotonisesti yhdellä aivan kuin muissakin kielissä; jos siis sortattu matriisi talletetaan vanhalla nimellä niin vanhaan matriisiin ei ole paluuta; tilanteesta riippuen täytyy muodostaa uusi matriisi uudella nimellä joten talletusnimen saattaa joutua muuttamaan. Itse sorttausrutiinihan on sama kuin näytölle sorttavassakin.
2. funktion "function sorttaamatriisi" sort-käskyn parametreja täytyy joskus määritellä sopiviksi; esimerkiksi -g sorttaa yleensä parhaiten numeroarvon perusteella mutta -n on joskus parempi. Tekstuaalinen sorttaus vaatii määreeksi esimerkiksi -d jota saattaa joutua vahvistamaan määreellä -b. Muitakin muutoksia saattaa joutua tekemään.
Koodia: [Valitse]
#!/bin/bash
export LC_ALL=C # desimaalipiste on tällä sivulla piste eikä pilkku niinkuin normaalilla koodisivulla -> desimaalit sorttautuvat oikein samoinkuin tieteellinen esitysmuoto kytkimellä -g.
function sorttaamatriisi () { declare | grep ^$1= | sed "s/$1=//;s/\s\[/\n/g" | cut -d= -f2 | tr -d \(\)[]\" | sort -g ;} # sort-käskyn parametreja saattaa joutua muuttamaan.

# tässä lohkossa on koematriiseja toiminnan kokeilemiseksi. Viimeinen kommentoimaton unset:illä alkava matriisimäärittely jää voimaan.
unset mat
mat[7]=4
mat[1]=15
mat[4]=0.2
mat[3]=0.1
mat[5]=-7
mat[6]=-3
mat[2]=11
mat[8]=0.05
mat[9]=4e-2
mat[10]=6.1e-2
mat[11]=6.05e-2
# unset mat; mat=($(seq 1000 -1 1))
unset mat; mat=($(seq 100 | awk '{print 2*rand()-1}'))

echo matriisi sorttaamatta: ;echo ${mat[*]}
alkuhetki=$(date +%s.%N); mat=($(sorttaamatriisi mat)); loppuhetki=$(date +%s.%N) # "mat=" on talletusnimi ja sen sattaa joutua vaihtamaan.
echo matriisi sortattuna: ;echo ${mat[*]}
echo -n sorttaukseen kulunut aika sekuntia: ; echo $loppuhetki-$alkuhetki | bc
Sorttauskäskyn "sorttaamatriisi mat 1" selvitys: sorttaamatriisi matriisinnimi sorttaus_kentän_numero
ja tässä: sorttaus_kentän_numero -> 2=osoitteiden sorttaus ja 1=arvojen sorttaus

Sorttaaminen kestää noin 4-8ms 1000-jäsenisen matriisilla tai 2.4s miljoonajäsenisellä.
- tämä skripti ei sortatessaan välitä ollenkaan siitä onko sortattava luku kokonaisluku vai desimaaliluku.
**
Vaikka bash:ista toitotankin niin minäkään en osaa ottaa huomioon sitä ettei bash:issa yleensä skriptejä tehdä sillä monimutkaisetkin hommat hoituvat yhdellä rivillä. Äskettäin halusin saada selville mikä on kansiossa olevista tiedostoista runsas-rivisimmän rivien lukumäärä. Suunnittelin jo monirivistä skriptiä jossa olisi looppeja ja pelejä_ja_tokkaroita. Liian hidashan semmoisesta tulisi joten väsäsin yksirivisen:
Koodia: [Valitse]
find kansio_polkuineen -maxdepth 1 -type f | xargs wc -l | awk '{print $1}' | sort -n | tail -2 | head -1
Eikä sen tekemisessä ollut mitään muuta vaikeutta kuin hommaan ryhtyminen. Bash:in monimuotoisuudesta kertoo se että homman voi ratkaista paljon paremmin näin: tämä onkin ehdoton nopeuskunkku eikä hermostu edes binääritiedostoista.  Excludea sattaa joutua säätämään.
Koodia: [Valitse]
grep -n --exclude=*.* ^.*$ kansio_pollkuineen/*  | cut -d: -f2 | sort -n | tail -1
bash:issa jokainen tehtävä voidaan ratkaista lukemattomilla täysin erilaisilla  tavoilla, esimerkiksi edelliset voidaan kirjoittaa myös:
Koodia: [Valitse]
wc -l kansio_polkuineen/* | awk '{print $1}' | sort -n | tail -2 | head -1
Mutta jokaisella ratkaisulla on omat ominaisuutensa ja puutteensa. Tuosta wc-virityksestä saa kyllä paremman lisäämällä siihen osan joka hyväksyy vain tiedostot joissa on tekstiä:
Koodia: [Valitse]
wc -l $(file kansio_polkuineen/* | grep text | cut -d: -f1) | awk '{print $1}' | sort -n | tail -2 | head -1
****

Kun tekee mitähyvänsä niin kohtaa ennemmin tai myöhemmin ongelman: vähän aikaa sitten tein sitä-ja-sitä, mutta en muista tiedoston nimeä enkä edes kansiota. Ratkaisu: todennäköisesti se löytyy listaamalla mitä viimeisillä kerroilla on tiedostojärjestelmässä tehty:
Koodia: [Valitse]
find mistä_kansiosta_alkaen_etsitään -type f  -printf '%T@ %p\n' | sort -n | tail -40 | cut -f2- -d" "
joka listaa aikajärjestyksessä ne 40 asiaa joita on editoitu viimeksi. Esimerkiksi:
Koodia: [Valitse]
find ~/OMATSKRIPTIT -type f  -printf '%T@ %p\n' | sort -n | tail -40 | cut -f2- -d" "
--
Mutta joskus tulee tehtyä niin paljon hommia että tuloksia tulee niin paljon että niiden lukumäärää täytyy pienentää antamalla jotain rajoittavaa tietoa siitä mistä etsitään:
Koodia: [Valitse]
find mistä_kansiosta_alkaen_etsitään -type f  -printf '%T@ %p\n' | grep -v mistä_ei_etsitä | sort -n | tail -40 | cut -f2- -d" "
esimerkiksi:
Koodia: [Valitse]
find ~/OMATSKRIPTIT -type f  -printf '%T@ %p\n' | grep -v \/ARKISTO\/ | sort -n | tail -40 | cut -f2- -d" "
--
Tai voidaan määritellä että etsittävien tiedostojen nimistä jotakin: 
Koodia: [Valitse]
find mistä_kansiosta_alkaen_etsitään -type f  rajoittaminen_tiedostonimen_perusteella -printf '%T@ %p\n' | sort -n | tail -40 | cut -f2- -d" " 
Esimerkiksi:
Koodia: [Valitse]
find ~/OMATSKRIPTIT -type f  -regex '.*html$' -printf '%T@ %p\n' | sort -n | tail -40 | cut -f2- -d" "
joka rajoittaa listavat sen perusteella että peräliite täytyy olla html.
--
tai voidaan rajoittaa sen perusteella että etsittävässä tiedostossa tulee olla joku sana tai merkkiryhmä:
Koodia: [Valitse]
IFS=$'\n' unset apu; apu=($(find ~/OMATSKRIPTIT -type f  -printf '%T@ %p\n' | sort -n | tail -400 | cut -f2- -d" ")) ; for (( n=0; n<=${#apu[@]}-1 ; n++ )); do [[ $(cat ${apu[n]} | rajoitus_senperusteella_että_tiedostossa_on_määrätty_sana ) ]] && echo ${apu[n]}; done; unset IFS
esimerkiksi:
Koodia: [Valitse]
IFS=$'\n' unset apu; apu=($(find mistä_kansiosta_alkaen_etsitään -type f  -printf '%T@ %p\n' | sort -n | tail -400 | cut -f2- -d" ")) ; for (( n=0; n<=${#apu[@]}-1 ; n++ )); do [[ $(cat ${apu[n]} | grep -w joo) ]] && echo ${apu[n]}; done; unset IFS
--
- rajoitusmahdollisuuksia on kirjaimellisesti rajoittamattomasti. Yleensä nämä yksiriviset ovat nopeita mutta joitakin rajoitteita on erittäin vaikea saada nopeiksi sillä keinot niiden käyttämiseksi ovat tosivaikeita. Esimerkiksi tuon viimeisen esimerkin looppi tekee esimerkin hitaaksi ja jos keksii kuinka siitä pääsee eroon niin toiminta nopeutuu oleellisesti.
- eihän tämmöisiä kukaan tarkasti muista eikä se riitä että muistaa suurinpiirtein: täytyisi siis tehdä joku paikka josta näiden käskyjen rungot saa kopioitua. Kopioinnin perustella voisi nopeasti väsätä prototyyppi-ohjelman mihinhyvänsä; prototyypin joka toimii senverran nopeasti ettei varsinaista ohjelmaa tarvitse ehkä tehdäkään. Verkossa on tähän tehtävään on tehty lukemattomia verkkosivuja mutta huonon toteutuksensa takia ne kaikki vain sotkevat. 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.03.17 - klo:15.51
Ikuisuuskysymys ovat tiedostojen kopiot elikä duplikaatit. Kirjaimellisesti otettuna niiden etsiminen on yksinkertaista:
Koodia: [Valitse]
find mistä_kansiosta_alkaen_etsitään 2>/dev/null \( ! -regex ".*/\..*" \) -type f -exec md5sum '{}' ';' | grep -v mistä_ei_etsitä | sort | uniq --all-repeated=separate -w 33 | cut -c 35-
koodin selvennystä:
- 1: 2>/dev/null kehottaa jättämään "permission denied" viestit pois.
- 2: \( ! -regex ".*/\..*" \) kehottaa jättämään piilotiedostot väliin, sillä harvoin piilotiedostoja käytetään kun talletetaan jotakin joten ne sotkisivat vaan. Käsky on tarpeen vain kun etsitään kotikansiosta, mutta ei käsky haittaa vaikka etsittäisiin muualtakin joten se voi olla tuossa aina.

Käytännossä lause on seuraavanlainen kun etsitään kotikansiosta:
Koodia: [Valitse]
find ~ 2>/dev/null \( ! -regex ".*/\..*" \) -type f -exec md5sum '{}' ';' | grep -v /ARKISTO/ | sort | uniq --all-repeated=separate -w 33 | cut -c 35-
- bash-tulkki tekee käskystä virtuoosien optimoimaa C-koodia eikä mukana ole bash:ia juuri ollenkaan. Siis vain hyvin tehdyt C-koodiset toimivat nopeammin. Näitä nopeasti toimivia skriptejä tuntuu kokoajan tulevan lisää ja alkaa tuntua siltä että bash ei ole hidas mutta joku muu on.
- tuloksen tulkinta: tuloksessa on kahden tai useamman tiedoston ryhmiä joita erottaa tyhjä rivi. Ryhmän tiedostot ovat duplikaatteja elikä sisällöltään täysin samanlaisia vaikka nimet olisivat erilaisia.

Käskyllä saa nopeasti kuvan siitä kuinka paha koneen duplikaatti-tilanne on ja. Se löytää vain duplikaatit jotka ovat sisällöltään täysin samanlaiset - mutta siinä se ei erehdykään. Mutta eiväthän ne bitti-bitiltä samanlaiset ole kuin jäävuoren huippu. Toisenlaisia duplikaatteja voi olla rajattomasti - esimerkiksi videotiedostot voivat olla samasta asiasta mutta koodaus on toisenlainen, dokumentit voivat olla muuten samoja mutta toisesta on yksi kirjoitusvirhe korjattu .... Niiden löytäminen muilla keinoin on 10000 kertaa hitaampaa mutta yritetäänpä:
 ------
- toiminnan kehittäminen täytyy tehdä ympäristössä jonka koko on rajattu ja jossa on runsaasti "melkein-duplikaatteja". Skriptiajurin ARKISTO on paras mahdollinen.
Työ alkaa siitä että muodostetaan matriisi tiedostojärjestelmän halutusta osasta:
Koodia: [Valitse]
IFS= readarray mat < <(find ~/OMATSKRIPTIT/MATRIISIOPERAATIOITA/ARKISTO/SKRIPTIT/"matriisin jäsenen arvoa vastaavan osoitteen etsiminen matriisista" 2>/dev/null \( ! -regex ".*/\..*" \) -type f); unset IFS; printf "%s\n" "${mat[*]}"
- ei saa luottaa siihen että tuon käskyn matriisinkaltainen tulostus tarkoittaa sitä että kysymyksessä on matriisi, vaan vasta kun vaihdettaessa printf-käskyn merkki * merkkiin 0 tulostuu vain yksi rivi toimitaan varmasti matriisin kanssa. Jos tuloste ei muutu toimitaan muuttujan kanssa eikä jatko tule toimimaan. (siis muuttuja on sama kuin samannimisen matriisin jäsen 0)
------
Seuraavaksi muodostetaan jokaiselle parit jokaisen kanssa - huolehtien siitä että jokainen pari muodostuu vain kerran. Vertailtavien parien lukumäärä on mahdoton: esimerkkitiedostossa on noin 260 tiedostoa ja pareja siitä tulee noin 33800 - muuten jokaisella rivillä on kaksi rivinsiirtoa joten käsky : "wc -l" näyttää kaksinkertaista arvoa. Pareja muodostetaan noin .6 sekuntia. Esimerkki-skripti on seuraavanlainen:
Koodia: [Valitse]
IFS= readarray mat < <(find ~/OMATSKRIPTIT/MATRIISIOPERAATIOITA/ARKISTO/SKRIPTIT/"matriisin jäsenen arvoa vastaavan osoitteen etsiminen matriisista" 2>/dev/null \( ! -regex ".*/\..*" \) -type f); \
 unset IFS; unset apu; for (( n=0; n<${#mat[*]}-1; n++ )); do for (( m=n+1; m<${#mat[*]}; m++ )); do apu+=("${mat[n]}""${mat[m]}");  done; done; printf "%s\n" "${apu[*]}"
------
Sitten seuraa se aikaavievä osuus: tässä on sovellus wdiff joka vertailee tiedostoja - mutta sovellustahan voi vaihtaa. Mikäli toiminnon kokee mielekkääksi ja tarpeelliseksi kannattaa sitä kehittää edelleen - tulosteiden lukumäärää saa ilmanmuuta vähennettyä ja tehtyä mielekkäämmiksi jolloin toiminta lisäksi nopeutuu:
Koodia: [Valitse]
IFS= readarray mat < <(find ~/OMATSKRIPTIT/MATRIISIOPERAATIOITA/ARKISTO/SKRIPTIT/"matriisin jäsenen arvoa vastaavan osoitteen etsiminen matriisista" 2>/dev/null \( ! -regex ".*/\..*" \) -type f); \
 unset IFS; unset apu; for (( n=0; n<${#mat[*]}-1; n++ )); do for (( m=n+1; m<${#mat[*]}; m++ )); do apu+=("${mat[n]}""${mat[m]}");  done; done;  \
for (( n=0; n<${#apu[*]}; n+=2 )); do wdiff -s123  "$(printf "%s\n" "${apu[n]}" | cut -d$'\n' -f1)" "$(printf "%s\n" "${apu[n]}" | cut -d$'\n' -f2)"; echo; done
- sovelluksen wdiff ehkä joutuu aluksi lataamaan: sudo apt install wdiff . Kun nyt kehitys on suoritettu voi find-käskyn polkua muuttaa minnehyvänsä. Mutta muistaa kannattaa että toiminta-aika muodostuu erittäin suureksi mikäli annetulla polulla on paljon tiedostoja
*****
Myös tuossa parien muodostamisessa loopeista eroon pääseminen auttaa suunnattomasti. Bash osaa korvata muuttujan matriisilla joten toisesta loopista pääsee eroon helposti ja se auttaa jo paljon. Esimerkki skriptit; tässä tapauksessa ihan selviä käskyjä:
kahdella loopilla:
Koodia: [Valitse]
time ( unset IFS; unset apu;mat=($(seq 1000)); for (( n=0; n<${#mat[*]}-1; n++ )); do for (( m=n+1; m<${#mat[*]}; m++ )); do apu+=(${mat[n]}¤${mat[m]}) ; done; done; printf "%s\n" ${apu[*]} )
yhdellä loopilla:
Koodia: [Valitse]
time ( unset IFS; unset apu;mat=($(seq 1000)); for (( n=1; n<${#mat[*]}; n++ )); do printf "%s\n" ${mat[n]//${mat[n]}/${mat[*]:n}} | sed "s{.*{${mat[n-1]} &{g" ; done )
- arvolla 1000 ero on vain kaksinkertainen mutta jo arvolla 2000 ero on viisikertainen ja 3000:lla monikymmenkertainen ja sitten kaksilooppisen suoritusaika kasvaa mahdottomiin.
- varmaan toisestakin loopista pääsisi jotenkin eroon jolloin toiminta-ajat siirtyisivät millisekunti-alueelle.
- käsky osaa käyttää prosessorin kaikkia ytimiä, kylläkin tehottomasti niin että yhteistulos on vain noin 120%.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 19.04.18 - klo:09.10
Onkohan mitenkään helpohkosti toteutettavissa "timeout" scriptiin?
Eli minulla on scripti joka toimii ainakin 90% tapauksista normaalisti mutta joissakin tapauksissa ei saa haluamaansa tietoa jota tjää sitten odottamaan kunnes se käsin lopetetaan.
Eli nyt olisi mielessä tuohon scriptiin "timeoutti" joka lähtisi esim. scriptin alussa laskemaan aikaa ja jos scripti on ollut ajossa yli 60sek niin tapetaan se (mahdollisesti logitetaan tms.) ja jatketaan matkaa jolloin kaikki muu ei jää odottamaan tuon scriptin valmistumista.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 19.04.18 - klo:09.26
Onkohan mitenkään helpohkosti toteutettavissa "timeout" scriptiin?
Eli minulla on scripti joka toimii ainakin 90% tapauksista normaalisti mutta joissakin tapauksissa ei saa haluamaansa tietoa jota tjää sitten odottamaan kunnes se käsin lopetetaan.
Eli nyt olisi mielessä tuohon scriptiin "timeoutti" joka lähtisi esim. scriptin alussa laskemaan aikaa ja jos scripti on ollut ajossa yli 60sek niin tapetaan se (mahdollisesti logitetaan tms.) ja jatketaan matkaa jolloin kaikki muu ei jää odottamaan tuon scriptin valmistumista.

Helpoin keino on coreutilsin timeout-komento (https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html), joka on vakiona käytettävissä nykyisissä Linux-jakeluissa.

Koodia: [Valitse]
timeout 60 jumittavakomento
Lokituksen voi tehdä exit statuksen perusteella.


Muita vaihtoehtoja löytyy googlaamalla. Esimerkiksi: https://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 20.04.18 - klo:07.14
Onkohan mitenkään helpohkosti toteutettavissa "timeout" scriptiin?
Eli minulla on scripti joka toimii ainakin 90% tapauksista normaalisti mutta joissakin tapauksissa ei saa haluamaansa tietoa jota tjää sitten odottamaan kunnes se käsin lopetetaan.
Eli nyt olisi mielessä tuohon scriptiin "timeoutti" joka lähtisi esim. scriptin alussa laskemaan aikaa ja jos scripti on ollut ajossa yli 60sek niin tapetaan se (mahdollisesti logitetaan tms.) ja jatketaan matkaa jolloin kaikki muu ei jää odottamaan tuon scriptin valmistumista.

Helpoin keino on coreutilsin timeout-komento (https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html), joka on vakiona käytettävissä nykyisissä Linux-jakeluissa.

Koodia: [Valitse]
timeout 60 jumittavakomento
Lokituksen voi tehdä exit statuksen perusteella.


Muita vaihtoehtoja löytyy googlaamalla. Esimerkiksi: https://stackoverflow.com/questions/687948/timeout-a-command-in-bash-without-unnecessary-delay

Kiitän. Ainakin tuolla timeout komennolla pääsee alkuun =) Ja jos vaikka kerrankin pitäisi scriptin yksinkertaisena eikä lähtisi kikkailemalla pilaamaan sitä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 09.10.18 - klo:10.01
Minulla on txt tiedosto jossa on läjä URLeja jokainen omalla rivillään.
Miten tosta saisi kätevästi scriptillä tehtyä linkkejä?
Kokeilin n+1:llä tavalla lukea tiedoston rivi riviltä ja tehdä noista rivejä tyyliin <a href="$osoite">$osoite</a> mutta en saanut tuota toimimaan vaan aina jäi joko alku pois tai molemmat :-/

Eli riittäisi jos saisi jokaisen tuossa tiedostossa olevan urlin muutettua klikattavaksi linkiksi jossa linkki osana oli myös itse linkki.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 09.10.18 - klo:12.38
Minulla on txt tiedosto jossa on läjä URLeja jokainen omalla rivillään.
Miten tosta saisi kätevästi scriptillä tehtyä linkkejä?

Onnistuu vaikkapa tällaisella skriptillä:

Koodia: [Valitse]
#!/bin/sh

inputfile="$1"

if [ $# -lt 1 ]; then
    >&2 echo "\nusage: $0 urls.txt > urls.html\n"
    exit 0
fi

cat << EOF
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Linkkejä</title>
</head>
<body>
<ul style="list-style-type:none">
EOF

while IFS= read -r url; do
    printf '<li><a href="%s">%s</a></li>\n' "$url" "$url"
done < "$inputfile"

cat << EOF
</ul>
</body>
</html>
EOF

Käytetään siis tähän tapaan:

Koodia: [Valitse]
list-urls.sh linkit.txt > linkit.html
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 10.10.18 - klo:09.35
Koodia: [Valitse]
list-urls.sh linkit.txt > linkit.html

Kiitän.
Tämä antoi tällaisen herjan:
bash: ./scripti.sh: /bin/sh^M: bad interpreter: No such file or directory

Tosin minulla on pieni epäillys, että tämä johtuu siitä että copy/pastesin scriptin windows koneella tikulle tiedostoon.
Tuo on joskus aikaisemminkin aiheuttanut ongelmia jotka on saanut dos2unix:lla ratkaistua mutta sitä ei tuossa kyseissä koneessa ollut joten joudun testaamaan uudelleen myöhemmin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 10.10.18 - klo:11.03
Tämä antoi tällaisen herjan:
bash: ./scripti.sh: /bin/sh^M: bad interpreter: No such file or directory

Tosin minulla on pieni epäillys, että tämä johtuu siitä että copy/pastesin scriptin windows koneella tikulle tiedostoon.

Kyllä. Vaatii dos2unixin tai vastaavan korjausoperaation CR-merkkien poistamiseen rivinvaihdoista. Pitäisi onnistua myös tr:llä:

Koodia: [Valitse]
tr -d '\r' < scripti.sh > korjattuscripti.sh
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 11.10.18 - klo:08.37
Tämä antoi tällaisen herjan:
bash: ./scripti.sh: /bin/sh^M: bad interpreter: No such file or directory

Tosin minulla on pieni epäillys, että tämä johtuu siitä että copy/pastesin scriptin windows koneella tikulle tiedostoon.

Kyllä. Vaatii dos2unixin tai vastaavan korjausoperaation CR-merkkien poistamiseen rivinvaihdoista. Pitäisi onnistua myös tr:llä:

Koodia: [Valitse]
tr -d '\r' < scripti.sh > korjattuscripti.sh

Jees. Täytyy ottaa tulevaisuutta varten tuo tr rivi talteen. Sain tällä kertaa onneksi iPhonen kautta yhteyden nettiin ja sain copy/pastettua tuon scriptin ubuntu puolella. Scripti toimi ja teki juuri sen mitä pitikin joten kiitoksia taas kerran.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: sigfrid - 27.10.18 - klo:17.41
Moi,

tuli tenkkapo.  Pitäisi korvata positioarvo toisesta tiedostosta kaikilta riveiltä,  jos vastaava position arvo on ensimmäisessa tiedostossa 9.  Esimerkki

Tiedosto 1:
1221110091211219000121

Tiedosto 2:
1212210011200220000222

Ensimmäisen tiedoston 9. positio tulisi arvoon 1 ja 16. positio arvoon 0.  Tiedostot ovat kooltaan suuria ja identtiset rivimääriltään ja-pituudeltaan.  Voin tietenkin muuttaa rivien merkit sarakkeiksi lisäämällä erottimet merkkien väliin.


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 27.10.18 - klo:20.32
@sigfrid:

Kuulostaa sen verran hankalalta ongelmalta, että kannattaa ottaa joku Bashia monipuolisempi kieli käyttöön. Vaikka Python, tai mikä tuntuu itselle helpoimmalta.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 04.01.19 - klo:13.33
Saan lähetetty sähköpostia komentoriviltä sendemail:lla OK ja saan lähetettyä HTML muotoisia sähköposteja myös joihin saan liitteeksi kuvan mutta mitenköhän sen saisi sisällytettyä itse viestiin?

Tarkoitus olisi siis lähettää sähköposteja joissa on kuva viestissä HTML:n seassa <img src="kuva.jpg"> mutta en millään saa päähäni miten saisin tämän tehtyä. Tiedostonahan tuon saa helposti mukaan -a kuva.jpg:llä mutta tällöin se näkyy viestin lopussa erillisenä liitteenä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 04.01.19 - klo:15.06
Tarkoitus olisi siis lähettää sähköposteja joissa on kuva viestissä HTML:n seassa <img src="kuva.jpg"> mutta en millään saa päähäni miten saisin tämän tehtyä. Tiedostonahan tuon saa helposti mukaan -a kuva.jpg:llä mutta tällöin se näkyy viestin lopussa erillisenä liitteenä.

Voit viitata viestin HTML-koodissa liitteenä olevaan kuvaan CID:llä, kuten täällä on esitetty: https://stackoverflow.com/a/17981118

Koodia: [Valitse]
sendmail -t <<EOT
TO: XXX@YYY.com
FROM: <TEST_IMAGE@YYY.com>
SUBJECT: Embed image test
MIME-Version: 1.0
Content-Type: multipart/related;boundary="XYZ"

--XYZ
Content-Type: text/html; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-15">
</head>
<body bgcolor="#ffffff" text="#000000">
<img src="cid:part1.06090408.01060107" alt="">
</body>
</html>

--XYZ
Content-Type: image/jpeg;name="sathy.jpg"
Content-Transfer-Encoding: base64
Content-ID: <part1.06090408.01060107>
Content-Disposition: inline; filename="sathy.jpg"

$(base64 sathy.jpg)
--XYZ--
EOT



Kuvan voi myös enkoodata suoraan img-tagiin base64-muodossa, mutta kaikki sähköpostiohjelmat eivät tue sitä. Toinen laajemmin yhteensopiva ratkaisu on ladata kuva jollekin nettipalvelimelle ja viitata siihen koko osoitteella.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 07.01.19 - klo:11.53
Voit viitata viestin HTML-koodissa liitteenä olevaan kuvaan CID:llä, kuten täällä on esitetty: https://stackoverflow.com/a/17981118


Kiitoksia vinkistä mutta nyt pätkii sen verran pahasti päässä, että en tajua miten homma toimii ja onnistuuko käyttämälläni sendemail:lla samoin, kuin sendmail:lla.
Pitänee lukea tuo ohje vielä pariin otteeseen ja lähteä testailemaan sillä tuskin tuo niin vaikea on, kuin miltä se luettuna näyttää.

Itse viesti tökätään --XYZ rivin väliin ja "<img src="cid:part1.06090408.01060107" alt="">" src:llä viitataan myöhemmin (--XYZ:n jälkeen) "Content-ID: <part1.06090408.01060107>" kohdassa olleeseen .jpg:n jonka polku kerrotaan "Content-Disposition: inline; filename="sathy.jpg"" kohdassa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 07.01.19 - klo:12.21
Itse viesti tökätään --XYZ rivin väliin ja "<img src="cid:part1.06090408.01060107" alt="">" src:llä viitataan myöhemmin (--XYZ:n jälkeen) "Content-ID: <part1.06090408.01060107>" kohdassa olleeseen .jpg:n jonka polku kerrotaan "Content-Disposition: inline; filename="sathy.jpg"" kohdassa.

Content-Type-rivin parametrilla name ja Content-Disposition-rivin parametrilla filename määritellään liitteen tiedostonimi, jonka sähköpostiohjelma sitten osaa näyttää. Epäilen, että Content-Disposition-rivi on sähköpostin tapauksessa turha, tai inlinen sijaan se voisi yhtä hyvin olla attachment. Sähköpostiohjelmat esittävät molemmat samalla tavalla liitteinä.

Joka tapauksessa lähdetiedoston polkua ei määritellä viestissä, eikä sitä anneta sendmailin käsiteltäväksi. Sen sijaan tiedoston sisältö enkoodataan base64-muotoon ja sisällytetään viestiin rivillä:

Koodia: [Valitse]
$(base64 sathy.jpg)
Eli jos kuvatiedosto sijaitsee vaikkapa käyttäjän kotihakemiston Kuvat-kansiossa, sen voisi syöttää base64:lle näin:

Koodia: [Valitse]
$(base64 "$HOME/Kuvat/kuva.jpg")

Lisäys: RFC2183:n mukaan (https://tools.ietf.org/html/rfc2183) "Content-Disposition: inline" määrittää, että liitteet tulisi esittää automaattisesti, kun viesti avataan. Enpä sitten tiedä, miten moni ohjelma oikeasti tekee eroa inline- ja attachment-asetuksen välillä...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.05.19 - klo:09.51
Olen pitkään ihmetellyt päätteeni historian temppuilemista: esimerkiksi duplikaatit eivät poistu vaikka käskee, ja jokakerran saa pikkuisen ihmetellä että mitähän sieltä nyt tulee.

Luin netistä muutaman kommentin joita en purematta niele mutta ajateltavaa ne antaa:
- historia tykkää kyttyrää joistakin ei-ascii merkeistä
- kun historia alkaa temppuilla niin ainoa pätevä keino on tyhjentää historiatiedosto.

Tein siis historiatiedostosta kopion ja tyhjensin vanhan. Ainakin duplikaatit alkoivat poistua ja jotenkin toimintaan tuli jämäkkyyttä.

Apuahan tässä täytyy pyytää.

***

Tämmöistä tein:
Koodia: [Valitse]
Vanhankin .bash_history:n sai käyttöön kun poisti sen duplikaatit ulkoisella käskyllä:
cat ~/.bash_history | tac | awk '!a[$0]++' | tac > ~/koe . Ja koe-tiedoston tutkimisen jälkeen talletin sen .bash_history:n tilalle.

Samalla kirjoitin .bashrc:hen:
export HISTSIZE=100000
export HISTFILESIZE=100000

HISTCONTROL=ignoredups:erasedups
shopt -s histappend
PROMPT_COMMAND="history -n; history -w; history -c; history -r; $PROMPT_COMMAND"

Mutta myöntää täytyy että liian syvillä vesillä liikutaan.

***

 Kukaan ei kykene tekemään yhtäkään skriptiä jossa ei olisi parantamisen varaa - ja usein parantamista on paljon sillä pitkät ja hitaaat skriptit supistuvat nopeiksi muutaman sanan yksirivisiksi - mutta kannattaako sen vaatima työ on kyseenalaista - eikä niiden kehittämiseen kenenkään omat rahkeet riitä. Joten puhumalla skripteistä mitähyvänsä puhuu ilmanmuuta paljon myös palturia.

***

Väite etteivät BASH:in funktiot kykene palauttamaan parametrejaan on vain puolittain tosi, se nimittäin osaa palauttaa nimiparametrin eikä sitäkään automaattisesti. Useimmilla asian merkitys on hakusessa, mutta virtuooseilta on tahallinen ja ilkeämielinen unohdus että BASH osaa tehdä tuosta parametrin palauttamisesta turhaa. Ikuisesti on tunnettu seuraava esimerkki:
a=b;let $a=55;echo b -> tulostaa 55

Funktiomuotoon sovellettuna tämä on:
Koodia: [Valitse]
function koe () { let $1=55 ;}; koe a; echo $a
joka myös tulostaa 55
- siis passataan nimiparametri.
- $1 eteen lisätään joku käsky, esimerkiksi let, read, readarray, test, eval ...
- let toimii vain numeroiden kanssa.
- readarray toimii vähän erilailla kuin muut:
Koodia: [Valitse]
function koe () { readarray $1 < /boot/grub/grub.cfg ;}; koe a; printf "%s\n" "${a[@]}"
Väite kyvyttömyydestä palauttaa parametrejä ontuu muutenkin vähäsen sillä kun joku kirjoittaa näytölle - ei ole väliä  mikä - niin toinen voi käydä lukemassa sen sieltä; toiminta on erittäin nopea eikä se jätä näytölle jälkeä siitä että sitä on käytetty. Tosin se toimii ongelmitta vain kun tulostusta on yksi rivi mutta se riittääkin usein. Esimerkiksi
Koodia: [Valitse]
function koe () { echo töttöröö ;}; a=$(koe); echo $a           # tulostaa: töttöröö
function koe () { cat /boot/grub/grub.cfg ;}; a=$(koe); echo $a # koko teksti menee yhdelle riville ja se on usein ongelmallista.
Siis BASH:in funktiot eivät palauta parametrejään automaattisesti vaan se pitää määrätä.
Ja aina voi funktiossakin kirjoittaa levylle ja pääohjelmassa käydä lukemassa sieltä. Keino on ihan ongelmaton ja se hidastaa toimintaa olemattoman vähän - ajatuskin on kyllä inhottava.

Nopeasti toimiva matriisin sorttausfunktio numero-arvoille
=====================================
- sort-ohjelma sorttaa mitä sille tulostetaan - on aivan sama tulostetaanko sille tiedosto vaiko matriisi.
- matriisia ei muodosteta uudestaan funktiossa, vaan pääohjelmassa käydään näytöltä lukemassa funktioon tulostus ja muodostetaan matriisi uudestaan senperusteella - sentakia kutsu on omituinen mutta toiminta on paljon nopeampaa.
- 1900 jäsenisen matriisin sorttauksessa kuluva aika on noin 60ms
 
#!/bin/bash
export LC_ALL=C # desimaalipiste on tällä sivulla piste eikä pilkku niinkuin normaalilla koodisivulla ->
                # desimaalit sorttautuvat oikein samoinkuin tieteellinen esitysmuoto kytkimellä -g.
Koodia: [Valitse]

function sorttaamatriisi () { declare | grep ^$1= | sed "s/$1=//;s/\s\[/\n/g" | cut -d= -f2 | tr -d \(\)[]\" | sort -g ;} # sort-käskyn parametreja saattaa joutua muuttamaan.

#mat=($(seq 10000;seq 10000))
mat=($(seq -5700 1.5 5700;seq -5700 1.0  5700)) # noin joka neljäs on duplikaatti ja niiden pitää löytyä
#echo matriisi sorttaamatta: ;echo ${mat[*]}
alkuhetki=$(date +%s.%N)
mat=($(sorttaamatriisi mat))
loppuhetki=$(date +%s.%N)
echo matriisi sortattuna: ; echo "${mat[*]}"
echo -n sorttaukseen kulunut aika sekuntia: ; echo $loppuhetki-$alkuhetki | bc
(( ${#mat[*]} > 1 )) && echo kysessä on tosiaan matriisi
# duplikaattien lukumäärä
singlikaatit=$(echo "${mat[*]}" |  tr ' ' '\n' | sort | uniq | wc -w)
kaikki=$(echo "${mat[*]}" | wc -w)
echo duplikaatteja $((2*($kaikki-$singlikaatit)))
export LC_ALL=
- BASH:in sorttausrutiinit syövät usein duplikaatteja ja testatessa on syytä varmistaa että duplikaatteja ei syödä - esimerkiksi laskemalla ne ja vertaamalla siihen paljonko niitä pitäisi olla.
- jos luulee työskentelevänsä matriisin kanssa niin kannattaa testata työskenteleekö matriisin vaiko samannimisen muuttujan kanssa - jossa matriisin koko sisältö on on yhtenä tekstijonona. 
- nimenomaan kannattaa huomioida ettei tässä funktiossa tarvita eval-käskyä.
- tämä kykenee sorttaamaan vain numeroita - ja yksinkertaista tekstiä.
- toki parametrin voisi "palauttaakin". Mutta aikaa se vain tuhraa.

***

Verkkosivuilla esitetystä BASH-koodista ei juuri koskaan skriptin kokeilemisen takia tarvitse tehdä skripti-tiedostoa eikä edes lausetta #!/bin/bash välttämättä tarvita. Riittää kun maalaat koodin kokonaisuudessaan ja painat ctrl-c. Siten avaa pääte painamalla ctrl-alt-t ja sitten paina ctrl-shift-v . Lopuksi kannattaa painaa enter. Näyttö ei ole ihan siisti mutta asia selviää kyllä.

***

BASH:illa oli aikoinaan kirjastot ja niitä alettiin oppia hyödyntämään. Mutta BASH:ista halutaan eroon eikä se passannut ja niinpä tietoturvaan vedoten BASH:in kirjasto-osoitin poistettiin. Se ei tee kirjastojen käyttämisestä mahdotonta mutta koska entiset kirjastoja hyödyntävät skriptit lakkasivat toimimasta niin koko kirjastoidea haudattiin.

Mikäli kieleltä viedään sen kirjastot niin kielestä tulee kelvoton - niinkuin BASH:ista tuli kun siltä vietiin kirjastot. Ja BASH:issa kirjastot ovat vielä tärkeämpiä kuin muissa kielissä sillä BASH:in toiminnoista monet ovat kammottavaa merkkisotkua jota ei saa kasattua kohtuullisella työllä mitään kevollista edes asiaan vihkiytynyt.

Silloin kun BASH:illa oli kirjastot se olikin kiistaton kunkku. Mutta monikin haluaa kunkuksi kunkun paikalle ja sehän ei onnistu mikäli vanha kunkku on edes jotenkin kelvollinen. Siispä tietoturvaan vedoten BASH:in kirjasto-osoitin poistettiin. Se ei tee kirjastojen käyttämisestä mahdotonta mutta koska entiset kirjastoja hyödyntävät skriptit lakkasivat toimimasta niin koko kirjastoidea haudattiin.

Mutta mikäli ohjelmointikielen kirjastoista ei löydy jotain ominaisuutta ja joutuu koodaamaan itse tuon ominaisuuden kestää koodaaminen joskus iäisyyksiä ja koodista tulee suurinpiirtein aina hyvin hidas virhegeneraattori. 

Kuitenkin maailmalla on vieläkin monia BASH-kirjastoja - joitain löytyy netistäkin. Mutta tällähetkellä tilanne on sellainen että jos haluat olla aikaansaapa skriptaaja niin sinun on tehtävä omat kirjastosi - sillä jos hyvästä koodista ei ole kirjastofunktiota rönsyilee sen käyttö ikuisesti.

Seuraava isku BASH:in tuhoamisessa oli eval-käskyn lainsuojattomaksi julistaminen. Siinäkin käytettiin tekosyytä että eval-käsky on tietoturvariski - ja onkin ilmanmuuta selvää että se on tieoturvariski sillä koko ATK on pelkkää tietoturvariskiä. Kirjastofunktioista valtaosa tarvitsee eval-käskyä.

Tosiasiahan on että BASH on ohjelmointiin sopimaton varsinkin kun toiset heittelee kapuloita rattaisiin. Muiden hämärähommien lisäksi huomasin omituisen ilmiön: skripti toimi yhtänopeasti surkealla tabletilla kuin tehokkaalla pöytäkoneella. Millätavoin BASH:in nopeutta rajoitetaan? Sillä rajoittamistahan on pakko harrastaa moniajoympäristössä tai muuten joku´omii kaikki resurssit - mutta BASH:in suhteen koko raudan parantuminen viedään ilmeisesti toisiin käyttöihin.

Pahan iskun BASH:ille ovat antaneet BASH:in kehittäjät itsekin jättämällä opettamatta sen että BASH-kieliset loopit hidastavat BASH:in toiminnan mateluksi.

***
 
Sensijaan että tekee skriptiin BASH-kielisiä looppeja tulisi käyttää BASH:in monessa käskyssä sisällä olevaa looppia - ja on käskyissä hyödynnettäväksi nopeita mariisejakin. Käskyjen sisäisten looppien hyödyntäminen nostaa BASH:in toiminnan nopeammaksi. Nämä esimerkit ovat työskentelyä matriisin kanssa - mutta toisaalta kaikki on matriisia. Muuten looppien hidastava vaikutus kertautuu jos niitä hölvätään jokapaikkaan.

Kokeile itse:
Koodia: [Valitse]
matriisi=({1..1000000}); time printf "${matriisi[*]}"                      # kestää 1.2s
matriisi=({1..1000000}); time for n in {1..1000000}; do printf $n' '; done # kestää 7.5s vaikka looppi on nopein mahdollinen - tuommoiseen looppiin ei voi laittaa muutujaa, mutta loopit jotka hyväksyvät muuttujan ovat parikin sekuntia hitaampia.


- nopeuden muutokset ovat sittenkin pieniä. Mutta koko skriptin osalta nämä pinet erot usein kertautuvat eivätkä summaudu.
- ei BASH:in kanssa pidä toimia kovin suurten matriisien kanssa mutta tämä on vain osoitus että nekin toimivat. Suurten matriisien sivuvaikutukset ovat myös suuria siinä päättessä jossa toimitaan. Ehkä raja on jossain välillä:  20.000 - 200.000. Sen näkee myös liitteistä.

***

Kun käsitellään tiedostoja se ei onnistu kovalevyllä vaan siitä täytyy ensin tehdä käskyn sisällä matriisi RAM:miin. Eihän silloin ole kovin odottamatonta ole että totunnaisesti vain tiedostojen käsittelyyn käytetyt käskyt toimivat matriisienkin kanssa? Esimerkiksi matriisin sorttaus onnistuu ihan hyvin mikäli matriisi tulostetaan sinne sort-rutiiniin:
Koodia: [Valitse]
matriisi=({1..1000000}); time echo "${matriisi[*]}" | tr ' ' '\n' | sort -nr    # kestää 2.5 sek
tai saman tekee "käänteinen cat" tässä erikoistapauksessa
matriisi=({1..1000000}); time echo "${matriisi[*]}" | tr ' ' '\n' | tac 

Sitkeässä elää harhaluulo että funktion parametrin palauttamisesta olisi jotain hyötyä. BASH:issa funktioon lähetetään muuttujan nimi - siis matriisinkin tapauksessa vain nimi. Funktio käsittelee muuttujan. Funktiosta ei tarvitse palauttaa mitään sillä muuttuja on muuttunut BASH:in kirjanpidossa.

Seuraava matrisin sorttausfunktio joka sopii pelkästään numeroille ja tekstille jossa ei ole välilyöntejä ei palauta parametreja sillä se olisi tarpeetonta:
Koodia: [Valitse]
function sorttaamatriisi () { readarray $1 < <(eval echo \${$1[*]} | tr ' ' '\n' | sort -nr) ;}

- 20.000-jäsenisen matrisin sorttaaminen kestää noin 150ms  - täysin uskomatonta BASH:ista.
- eval-käsky mahdollistaa funktion tekemisen - readarray vain yksinkertaistaa koodia.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 30.06.19 - klo:13.48
Tuossa edellisessä funktiossa on pari puutetta jotka kaipaavat välitöntä korjausta: ensiksi on turha antaa ammuksia BASH:in tuhoajille tuolla eval-käskyllä jos voi olla antamatta - ja toisekseenkin tuo jokeri * on ongelmallinen ja teoriassa pitäisi käyttää viisikertaa hitaampaa jokeria @. Kumpikin korjautuu kun käsitellään BASH:in kirjanpitoa. Nimittäin käsky-yhdistelmä:

declare | grep ^matriisin_nimi=   
tulostaa sen, kuinka BASH pitää kirjaa matriisin_nimi nimisestä matriisista. Sen avulla voi tulostaa matriisin arvot:
Koodia: [Valitse]
mat=(1 kaksi 3 4 "viisi ja puoli" kuusi 7 8 9 0); declare | grep ^mat= | sed "s/mat=//;s/\s\[/\n/g" | cut -d= -f2 | tr -d \)\"
Siitä saa kasattua erittäin nopean sorttausfunktion jossa ei ole eval-käskyä eikä ongelmallista tähteä:
Koodia: [Valitse]
sorttaamatriisi () { readarray $1 < <(declare | grep ^mat= | sed "s/mat=//;s/\s\[/\n/g" | cut -d= -f2 | tr -d \)\" | sort -g) ;
Muistikuvan arvot voidaan erottaa myös jollakin sopivalla regex:ällä, esimerkiksi:
Koodia: [Valitse]
grep -Po '(?U)\".*\"' | tr -d \" tai:grep -Po '(?<==\").*?(?=\")'  tai:grep -Po '(?<=\")[0-9]*'

- nimenomaan huomioi ettei mitään tulosteta - eihän sitä pyydetäkään
- matriisin tekstijäsenet erottuvat näppärästi noilla regex:llä - välilyöntejäkin saa olla. Mutta käytännossä tekstissä saattaa olla kaikenlaista inhottavaa ja tiedostot ovat ihan kauheita. Tekstillä kannattaa käyttää sitä eval-versiota.
- pienin koodimuutoksin sortataan osoitteet. Varsinkin assosiatiivisten matriisien kanssa se on tarpeellista. Osoitteet saa erotettua vaikka käskyllä:
Koodia: [Valitse]
mat=({1..10}); declare | grep ^mat= | grep -Po '(?U)\[.*\]' | tr -d []

***
 
Muistikuvan avulla saa tehtyä matriiseille (=siten myös tiedostoille) monenlaista, esimerkiksi etsittyä maximia, minimiä, keskiarvoa tai jotain muuta odotusarvoa.Tai etsittyä pisimmän jäsenen pituus, suoritettua monenlaisia vertailuja ... samankaltaisia nopeita on lukemattomia.

Kyllähän nämä muistikuva-muutokset jäävät toiseksi kaikille: sed, awk, perl, python - siinä se BASH:in kirous onkin: professorit kehittävät vaikka mitä uudella nimellä koska siten saavat omankin nimensä esiin varsinkin painottaessaan että sitä heidän kehittämäänsä tarvitaan kun BASH ei kykene - tai eihän professorit voi enää tunnustaa että ovat joskus BASH:ia käyttäneetkään.

***

BASH loistaa sellaisten tutkimuksien tekemisessä joista alkuun epäillään että mahtaako niissä mitään tutkittavaa olla. Mutta BASH:illa tekee nopeasti riittäävän hyvän skriptin sen päättelemiseksi kannattaako tehdä tutkimukseen kunnon ohjelma.

Aloin noiden muistikuvaa käsittelevien regex:ien suhteen epäillä että regex:n ajankäyttö kasvaa lievästi exponentiaalisesti datamäärän kasvaessa ja tein siitä käyrän - asia on tärkeä siksi että regex:ää käytetään monissa kielissä. Epäily osottautui vääräksi - mutta sensijaan paljastui että regex:illä on vaikeuksia sisäisten datarakenteidensa kanssa - tai BASH:illa muistinvarauksessa. Siis tarkastelun kohteena on regex: grep -Po '(?<==\").*?(?=\")' ja rinnalla on normaali muistikuvaversio.

Käskyn koodi on tässä (ja se toimii heti kun kopioit sen päätteeseen - muista painaa return. Skriptin toimimisen jälkeen kotikansioon tulee tiedosto regex.eps. Sen saa näkyviin tiedostoselaimella - joskus se toimii vasta kun antaa ensin käskyn: sudo apt-get install evince):
Koodia: [Valitse]
rm -f regex.eps regex;
for (( n=1; n<=1000000; n=n+500 )); do echo $n; unset mat; mat=$(seq -s' ' $n); alkuhetki=$(date +%s.%N); declare | grep mat= | grep -Po '(?<==\").*?(?=\")' ; echo $n' '$(echo $(date +%s.%N)-$alkuhetki | bc ) >> regex; done; gnuplot -p -e 'set terminal postscript eps color enhanced; set xlabel "matriisin koko"; set ylabel "aika sec."; set output "regex.eps"; plot "regex"'
Käsky on pitkäpötkö. Mutta ei sitä kannata paloitella sillä se on suora ja yksioikoinen. Skriptin toiminta kestää viitisen minuuttia sillä dataa liikkuu kymmenkunta gigaa - skriptin toimiessa näytöllä pitää olla kasvava numero osoittamassa että jotain tapahtuu ja missä mennään.

***

BASH:in matriiseja inhotaan ja väheksytään monesta syystä. Yksi väite on se etteivät BASH:in matriisit mitään matriiseja olekaan vaan mitälie vektoreita.

Mutta tavallaan se ei pidä paikkaansa. Kirjoita tiedostoon nimeltä koe:
1 22 333
-4444 5.'$5e55 6666666
7777777 "8888 8888" 9999999999

ja anna sitten käsky:
Koodia: [Valitse]
readarray mat < koe; printf "%s" "${mat[@]}"
jolloin tulostuu:
1 22 333
-4444 5.'$5e55 6666666
7777777 "8888 8888" 9999999999

mutta BASH:in kehittäjät ovat unohtaneet yhden asian: BASH osaa erottaa siitä jäsenen vain kovin hankalasti:
Koodia: [Valitse]
set -- ${mat[rivi_numero]}; echo $jäsenen_numero. Siis esimerkiksi:
set -- ${mat[1]}; echo $2 -> tulostaa: 5.'$5e55
Hidasta ja hankalaahan tuommoinen on, mutta matriisista on silti kysymys.
- matriisin jäsenissä saa olla kaikkia merkkejä paitsi välilyöntejä sillä välilyönnit on varattu erottamaan jäseniä.

Vielä hankalampaa on kirjoittaa tuohon muisti-matriisiin. Esimerkiksi seuravankaltainen toimii:
Koodia: [Valitse]
set -- ${mat[rivi]}; mat[rivi]=${@:1:alkio-1}' uusi '${@:alkio+1}

***

Ei tuollaista "kaksiulotteista" matriisia tarvise editorissa tehdä ja lukea sitten "readarray"-käskyllä vaan onnistuu se ihan BASH:issakin - erikoismerkkejä ei kylläkään hyväksytä, mutta matriisin saa helpommin alkamaan ykkösestä:
Koodia: [Valitse]
rivi1=(1 22 333)
rivi2=(-4444 5.5e55 6666666)
rivi3=(7777777 "8888 8888" 9999999999)
mat=([1]=${rivi1[@]} [2]=${rivi2[@]} [3]=${rivi3[@]})

Siitä saa arvot kahdella tavalla:
set -- ${mat[rivino]}; echo $sarakeno
echo ${mat[rivino]} | awk "{print \$sarakeno}"
- esimerkiksi:echo ${mat[2]} | awk "{print \$2}" tulostaa: 5.5e55

***

Pitää täysin paikkansa että BASH:in matriisioperaatiot ovat niin omituisia että niitä kannattaa välttää mikäli niitä joutuisi koodaamaan. Mutta silti ne toimivat ja mikäli toiminta tapahtuisi kirjastoista noudetuista funktioissa niin koodista saisi selväpiirteistä, koodi nopeutuisi suunnattomasti ja tietoturvaankin saataisiin jonkinlainen ote.

Tai itseasiassa ei siihen kirjastoja tarvita: funktion/funktioita voi toki lisätä koodiinsa alkuun. Pitäisi vain olla paikka josta niitä funktioita voisi kopioida.

Kirjastojen merkitys on helppo todeta: tee millä kielellä hyvänsä jotain monimutkaisempaa ilman kirjastoja.

***
BASH on surkimus. Suurin syy taitaa olla se että BASH on tehty opetustarkoituksiin ja siihen on tosiaan laitettu paljon sitä opetettavaa. Siis sitä ei ole tarkoitettu ohjelmointiin eikä se siihen kunnolla sovikaan.

Jokaisen toiminnan voi koodata lukemattomilla täysin erilaisilla tavoilla - ja vain muutama niistä on lähes virheetön. On liiankin helppoa saada aikaiseksi skriptejä jotka toimivat vain kun niille annetaan dataa josta ne pitävät.

Kirjastot korjaisivat sen että skriptintekijän täytyy oppia BASH:in salat. Ja kirjastothan toimisivat, niitä ei vain ole kirjoitettu - tämä ei pidä täysin paikkaansa sillä netistäkin löytyy aikamonta.

Tämä kirjastojen puute koskee meitä tavallisia tallaajia - roistoilla on kyllä kirjastot: ja katsopas mihin se on johtanut. Olisikohan aika käyttää skripejä puolustukseenkin?
 
Eikä BASH:ia pidä esittää negatiivisessa valossa sillä se olisi oman oksan sahaamista. Nimittäin BASH:ista ei tulla pääsemään eroon ihan pian. Ja jos BASH tuhotaan jossakin distrossa menee tuo distro perässä.

***

Yleensä funktiot ovat tyyppiä: function () { ... }. Silloin funktiolla ja pääohjelmalla on samat muistikuvat - ja kummassakin paikassa on muistikuvaan sekä luku- että kirjoitusoikeus. Jos funktiossa muutetaan muistikuvaa muuttuu se pääohjelmassakin. Parametreja ei siis tarvise palauttaa jos muuttaa parametrin muistikuvaa - joka on yleensä huomaamattoman pieni toimenpide. Funktiossa voi muuttaa niin monen muuttujan tai matriisin muistikuvaa kun haluaa.

Levylle tallettaminen ja levyltä lukeminen kannattaa toteuttaa muistikuvan avulla. Siinä on kahdet menetelmät:

Moitteettomat menetelmät jotka sopivat myös assosiatiivisille matriiseille, noille äsken kuvatuille kaksiulotteisille elikä kun matriisin jäsenet ovat matriiseja:
Koodia: [Valitse]
function matriisilevylle () { declare | grep ^$1= > $2 ;}
# sitä kutsutaan: matriisilevylle matriisin_nimi tiedoston nimi
# "declare -p" on kolmekertaa nopeampi mutta se ei toimi assosiatiivisten matriisien kanssa.   
   
function matriisilevyltä () { . "$1" ;}
# sitä kutsutaan: matriisilevyltä tiedoston_nimi

# siis nämä soveltuvat myös assosiatiivisen matriisin talletukseen ja -lukuun.
# Siinä yhteydessä tarpeellinen funktio:
function tulostamatriisi () { echo "arvo     osoite"; paste <(IFS= eval printf "%s\\\n"  "\${$1[@]}") <(IFS= eval printf "%s\\\n" "\${!$1[@]}") ;}

Nopeat ja likaiset menetelmät jotka sopivat parhaiten yksinrivisille numeromatriiseille silloin kun osoitteesta ei välitetä:
Koodia: [Valitse]
function matriisilevylle () { declare | grep ^$1= | tr ' '  '\n' | cut -d\" -f2 > $2 ;}
# sitä kutsutaan: matriisilevylle matriisin_nimi tiedoston nimi
   
function matriisilevyltä () { readarray $1 < $2 ;}
# sitä kutsutaan: matriisilevyltä matriisin_nimi tiedoston_nimi

- funktioita käytetään kirjoittamalla ne skriptin alkuun - jos ei halua sotkeutua kirjastoihin.
- funktionimissä voi käyttää ä:tä ja ö:tä.

***

Yksinkertaisen tekstijonon saa matriisiksi helposti: matriisi=($tekstirivi). Mutta muunnos muuttuukin todella monimutkaiseksi mikäli tekstijonossa on heittomerkein suojeltuja lauseita joissa on välilyöntejä - käytännössä niitä usein on, ei  tämä ole keksitty erikoistapaus. Koko ratkaisukoodi on tässä sillä funktiokutsukin kaipaa selittämistä:
Koodia: [Valitse]
#!/bin/bash
function tekstijonostamatriisiksi () {
teksti=${@:1:$#-1} # lähtöpään funktiokutsu jakaa tekstijonon välilyöntien kohdalta aina uudeksi parametriksi. Tässä funktioon tulleista parametreista kootaan se munnettava tekstijono takaisin yhtenäiseksi ottaen huomioon että viimeisenä parametrina on sen matriisin nimi johon muunnos suoritetaan.   

# tehdään apumatriisi niistä heittomerkkien välisistä tekstijonoista:
apumatriisi=($(echo " $teksti" | tr \" '\n' | sed 's/^ .*//g;s/.*/\"&\"/g;/\"\"/d;s/\"\\n\"/\" \"/g;s/ /$£/g'))

# korvataan löydettyjen heittomerkkien välissä olevien tekstijonojen välilyönnit $£-merkkiryhmällä:
for (( n=0; n<=${#apumatriisi[@]}; n++ )); do apustring1=${apumatriisi[n]}; apustring2=$( echo ${apumatriisi[n]} | sed 's/$£/ /g'); teksti=$( echo ${teksti//$apustring2/$apustring1}) ; done
 
# muodostetaan tullesta teksijonosta pyydetty matriisi - jossa siis on $£-merkkiryhmät heittomerkkien välisten tekstijonojen välilyöntien paikalla:
apu=("$teksti")

# muodostetaan muistikuva nimellä: ${@:$#} (joka on sen matrisin nimi joka olisi tarkoitus muodostaa) korvaten samalla välilöynneillä merkkiryhmät: $£ :
readarray ${@:$#} < <(printf "%s\n" ${apu[@]} | sed 's/$£/ /g')
}



# kutsuva pääohjelma
echo jokumatriisi:
unset jokumatriisi # varmistaa että mitä tulostuukin niin se tulee tässä kutsutusta funktiosta
teksti='0 "1 2" "2 3" 4 5 "6 7" "8 9" 10 "11 12 13 14 15 16" 17 18 19 "20 21 22 23 24"'
tekstijonostamatriisiksi $teksti jokumatriisi
printf "%s" "${jokumatriisi[@]}"                 # tuloste on sama olipa kyseessä matriisi tai muuttuja
echo matriisissa on jäseniä: ${#jokumatriisi[@]} # joten täytyy testata montako jäsentä siinä on.

echo; echo jokutoinenmatriisi:
teksti='"koira kylmällä kalliolla" "^ _ $ / #" 2 "kissa kuumalla katolla" "Åålannista Öölannin kautta Ääniselle"'
tekstijonostamatriisiksi $teksti jokutoinenmatriisi
printf "%s" "${jokutoinenmatriisi[@]}"
echo matriisissa on jäseniä: ${#jokutoinenmatriisi[@]}

echo; echo jokumatriisi on arvoiltaan edelleen sama:
printf "%s" "${jokumatriisi[@]}"
- varmaankin joku awk- ja regex-velho saisi samasta aikaiseksi nopeasti toimivan version.

***

muistikuvan manipulointia tapahtuu normaaleissakin skripteissä aina kun jollekin muuttujalle tehdään jotakin: olkoonpa kyse tavallisesta muuttujasta tai matriisista - niin pääohjelmassa kuin funktiossakin. Eikä siinäkään ole uutta että funktiossa siirretään muistikuva osoittamaan jotain toista muuttujaa sillä toiminto on tunnettu aina; toiminto vastaa täysin parametrin palauttamista. Kysymys on siitä ettei haluta puhua BASH:ista mitään positiivista.

Se merkitsee vähän että nopeus nousee huomattavasti sillä nopeus on edelleen kelvottoman huono. Vaan nimenomaan se merkitsee että voidaan käyttää totunnaisempia merkintätapoja - ne BASH:in kummalliset merkinnät ovat funktioissa ja teoriassa käyttäjien ei tarvitse tehdä funktioita vaan noutaa ne kirjastoista.

***

Päätteeseen tulostuu usein tekstiä monta sivua - joskus jopa kymmeniä. Mutta kaikki se on tallessa näyttöbufferissa. Näyttöbufferia voi selata rullahiirellä  tai jos hiiri on tavallinen niin oikean reunan "hissillä". Kun on selannut jonnekin historian hämäriin niin kun näpäyttää enter palataan heti sinne mistä lähdettiin. Voit siis käydä katsomassa mitä aikaisemmin tulostettiin.

***

Tässä yksi mitättömän pieni ja merkityksetön funktio mutta kun samantapaisia alkaa olla tarpeeksi monta niin merkitystä alkaa tulla. Kyseessä on funktio joka kääntää matriisin.

Ainahan näitä matriisin kääntöohjelmia on ollut ja jos haluaa pysyä BASH:issa niin esimerkiksi awk-toteutukset ovat paljon nopeampia - minkätakia tämmöistä tehdä? Osittain osoituksena että kyllä BASH osaa kaksiulotteisiakin matriiseja sekä tehdä että käsitellä. 

Matriisissa saa olla tekstiä tai numeroita mutta välilyönnit on tarkoitettu erottamaan alkioita eivätkä lainausmerkit auta tässä yksinkertaisessa toteutuksessa:
Koodia: [Valitse]
#!/bin/bash
function transpose () {
numberoffields=$(($( declare | grep ^$1= | cut -d\[ -f2 | tr -dc [[:space:]] | wc -c ) -1 ))
readarray $1 <  <(for (( n=1; n<=$numberoffields; n++ )); do
  echo $(printf "%s" "${array[@]}" | cut -f$n -d' ' )
done) # > testb # tämä muodostaa levytiedoston käännetystä kun ensimmäisen kommenttimerkin poistaa
}

# Pääohjelma toiminnan kokeilemiseksi:
readarray array < testa
echo "matriisi alunperin:"
printf "%s\n" "${array[@]}"
echo "matriisi käännettynä:"
transpose array
printf "%s" "${array[@]}" | column -t
echo

- siis tiedostoon nimeltä: testa kirjoitetaan esimerkiksi:
kissa ja koira ovat ikuisia vihollisia 0 1 2 3 4 5 6 7 8 9 20 11 12 13 14
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 62 63

niin skripti tulostaa ajettaessa:
kissa       21  42
ja          22  43
koira       23  44
ovat        24  45
ikuisia     25  46
vihollisia  26  47
0           27  48
1           28  49
2           29  50
3           30  51
4           31  52
5           32  53
6           33  54
7           34  55
8           35  56
9           36  57
20          37  58
11          38  59
12          39  60
13          40  62
14          41  63

- toki matriisi voidaan muodostaa BASH:issakin käyttämättä tiedostoa. Mutta ehkä näin on selvempää
- skripti kääntää matriisin samaan matriisiin missä se funktioon toimitettiinkin ja tulostaa sen samalla.
- koodilisäyksillä tulostumisjärjestyksiä voi muuttaa mielinmäärin, suorittaaa sorttaamista, matematikkaa ...
- tuo loppussa oleva: " | column -t " kutsuu automaattiformatointi ohjelmaa.
- BASH:in kehittäjän ratkaisu readarray:n toimintaan aiheutti kylläkin paljon:
1. Tulostusasultaan matriisit ovat melkein normaaleja. Sen aikaansaaminen edellytti varmaan melkoisia ponnistuksia eikä kai seuraavista pitäisi valittaa:
2. readarray lisää näkymättömiä merkkejä - muunmuassa rivinsiirron ja muutenkin se murjoo muistikuvaa.
3. BASH:in koko sparse-matrix-käsitteen tilalle tuli melkein sama "osoitteeton" kuin muissakin kielissä.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.07.19 - klo:14.06
Readarray toi BASH:iin kaksiulotteiset matriisit ja niitähän yleensä tarkoitetaan kun muissa kielissä puhutaan matriiseista. BASH:in vanhantyyppiset matriisit ovat yksiulotteisia ja niiden kanssa toimiminen on kaikinpuolin selväpiirteisempää - mutta se täytyy myöntää että puurot ja vellit menee nyt sekaisin. Seuraavissa tarkoitetaan aina yksiulotteisia matriiseja. Nimittäin readarray pahoinpitelee muistikuvaa joten kaksiulotteisten matriisien muistikuvien käsitteleminen tapahtuu vähän erilailla.

Kyllä ne matriisitkin palautuvat funktioista -> tässä funktioon menee vertailtavat matriisit ja tyhjä matriisi, se tyhjä täytetään siellä kahden muun matriisin eroilla ja sen muistikuva muodostetaan joten se on täytettynä funktiosta palattaessa - siis mitään ei palauteta mutta tulos on sama kun jos olisi palautettu.

Surkean hitaitahan nämä muistikuvan avulla tehdyt matriisinkäsittelyt ovat, mutta ne kestävät sentään järjellisiä aikoja - nämä kestävät vain sekunnin mutta looppi-versiot kymmeniä minuutteja. 

Myös matriisien eroavaisuuksia ilmoittavan funktion toiminta muistikuvaan tukeutuen on nopeampi kuin pääohjelmaan tehdyt viritykset. Tarkemmalta nimeltään skripti on: mitä toisessa matriisissa on sellaista mitä ensimmäisessä ei ole.

Tässä funktioon menee vertailtavat matriisit ja tyhjä matriisi, se tyhjä täytetään siellä kahden muun matriisin eroilla ja sen muistikuva muodostetaan - siten se on täytenä pääohjelmassa.
Koodia: [Valitse]
#!/bin/bash
function arraydiff1 () { readarray $3 < <(grep -Fxvf <(declare | grep ^$1= | tr ' '  '\n' | cut -d\" -f2 | sort) <(declare | grep ^$2= | tr ' '  '\n' | cut -d\" -f2 | sort)) ;}
matriisi1=({100000..1})
matriisi2=({-2..100002})
time arraydiff1 matriisi1 matriisi2 matriisi3
printf "%s\n" ${matriisi3[@]}


Tämä tulostaa kahden matriisin erot - jättäen pois yhteiset jäsenet:
Koodia: [Valitse]
#!/bin/bash
function arraydiff2 () { readarray $3 < <( comm -33 <(declare | grep ^$1= | tr ' '  '\n' | cut -d\" -f2 | sort) <(declare | grep ^$2= | tr ' '  '\n' | cut -d\" -f2 | sort)) ;}
matriisi1=({3..100000})
matriisi2=({0..99999})
time arraydiff2 matriisi1 matriisi2 matriisi3
printf "%s\n" "${matriisi3[@]}" | column -t


Vaikeista puuttuu enää se joka kertoo mitä yhteisiä jäseniä matriiseissa on:
Koodia: [Valitse]
#!/bin/bash
function arraydiff3 () { readarray $3 < <( comm  -12 <(declare | grep ^$1= | tr ' '  '\n' | cut -d\" -f2 | sort) <(declare | grep ^$2= | tr ' '  '\n' | cut -d\" -f2 | sort)) ;}
matriisi1=({3..5} {7..11})
matriisi2=({0..9} {11..53})
time arraydiff3 matriisi1 matriisi2 matriisi3
printf "%s\n" "${matriisi3[@]}" | column -t

***

Myös matriisin maksimi ja minimi kannattaa etsiä muistikuvan avulla:
Koodia: [Valitse]
#!/bin/bash
export LC_ALL=C # toiminta-aika muttuu 260->100ms
function matriisinmaxmin () {
declare | grep ^$1= | tr ' '  '\n' | cut -d\" -f2 | sort -n > /tmp/delme
readarray $3 < <(tail -1 /tmp/delme)
readarray $2 < <(head -1 /tmp/delme) ;}
# readarray lukee myös yksijäsenisen matriisin. Ja yksijäsenisen matriisi on sama kuin tavallinen muuttuja
# joten readarray lukee muuttujiakin.
 
# pääohjelma toiminnan kokeilemieksi: 180 188
unset maksimi minimi
matriisi=($(seq 100000 | awk -v seed=$RANDOM 'BEGIN{srand(seed)}{printf "%1.9f\n", rand()}')) 
time matriisinmaxmin matriisi maksimi minimi
echo minimi:$minimi'  maksimi:'$maksimi
LC_ALL=

***

BASH:in muistissa jo valmiiksi olevasta kaksiulotteisesta matriisista etsitään maksimi näin:
Koodia: [Valitse]
#!/bin/bash
# BASH:issahan voi olla kaksiulotteinen matriisi muistissakin ja mikäli näin on niin nopeinta on etsiä
# suoraan sieltä - tässä on kyse sellaisesta tapauksesta.
# Mutta mikäli se matriisi on jo valmiiksi levyllä niin nopeinta on etsitä heti levyltä.

function kaksiulotteisenmatriisinosoitettavankentänmaximi () { readarray $3 < <(declare | grep ^$1= | cut -d\( -f2- | tr "\'" '\n' | sed '/\[/d;s/\\n//g' | tr -d \) | cut -d' ' -f$2 | sort -n | tail -1) ;}
 
readarray matriisi < koe
kaksiulotteisenmatriisinosoitettavankentänmaximi matriisi 2 maximi
echo $maximi

*** 

Funktiotkin toimivat jokaisella suorituskerralla hieman erikauan. Tämä skripti tutkii transpose-funktiota ja nimenomaan sen suoritusaikojen vaihtelua odotettaessa mittausten välissä .1-20 sekuntia - nimittäin LINUX aiheuttaa tuskaa ainakin tällä alueella. Mutta pienin koodimuunnoksin tällä voi tutkia melkein minkätahansa funktion mitätahansa ominaisuutta. Tässä mittaus kestää monta tuntia mutta se on ihan liian lyhyt aika saada dataa josta tosiaan voisi sanoa jotakin.
Koodia: [Valitse]
#!/bin/bash
function transpose () {
numberoffields=$(($( declare | grep ^$1= | cut -d\[ -f2 | tr -dc [[:space:]] | wc -c ) -1 ))
readarray $1 <  <(for (( n=1; n<=$numberoffields; n++ )); do
  echo $(printf "%s" "${array[@]}" | cut -f$n -d' ' ) # & nopeuttaisi hieman mutta sillä on varjopuolensa
done)
}

< testa  readarray array
rm -f ~/koe
 
for n in $(seq .1 .1 20 | tr , .); do # mitattaessa muita funktioita x-akseli on usein logaritminen
# (esimerkiksi taajuus) ja pyyhkäisy saattaa silloin kannattaa vaihtaa "exponentiaaliseen" askellukseen
# jossa jokainen askel on aina esimerkiksi 5% suurempi kuin edellinen: 
# for n in $(echo "for (i = .001 ; i < 1000; i *= 1.05)  i" | bc -l ); do echo $n; done
# tällöin gnuplot:ille kannattaa kertoa että x-akseli on logaritminen: set logscale x 10

# suoritusaikoja tarkkaileva pääohjelma;
< testa readarray array # luetaan matriisi uudestaan jokakierroksella jotta alkuasetelma olisi aina sama
sleep $n
alkuhetki=$(date +%s.%N)
transpose array # tämän rivin paikalla olevan funktion ajankäyttöä tutkitaan. Sille voidaan antaa
                # parametrejä tai sitten ei.
loppuhetki=$(date +%s.%N)
echo $n' '$(echo $loppuhetki-$alkuhetki | bc) | tee -a ~/koe
done

gnuplot -p -e 'set terminal postscript eps color enhanced; set xlabel "odotusaika"; set ylabel "toiminta-aika sec."; set output "transpose.eps"; plot "~/koe"'
- skripti ei tulosta käyrää suoritusajoista vaan tekee käyrästä tiedoston nimellä transpose.eps. Sen voi lukea tiedostoselaimella - jos se ehdottaa lataamaan lisäpaketin niin lataa se.
- tai eihän se varsinainen käyrä ole vaan tuloste mittauspisteistä.

***

Muutaman mittauksen pohjalta ei mitään tarkempaa voi esittää mutta muutaman huomion tein:

Skriptien toimimisen nopeus heittelee aina - usein luokkaa 10%. Mutta todella ihmeellisiäkin toiminta-aikoja esiintyy - esimerkiksi kestoaika voi joskus olla kaksinkertainen - tai joissain harvoissa tapauksissa normaalia pienempikin.

Äskeisessä skriptissä määriteltiin suoritusaika nanosekunnin tarkkuudella vaikka todellinen suoritusaika vaihtelee usein peräti useita kymmeniä millisekunteja. Sinänsä tuo nanosekunnin tarkkuus on jokseenkin oikea - mutta se kertoo vain sen kuinkakauan suoritus kesti silläkertaa. Käytännössä sen tarkkuudesta jää hyötykäyttöön vain hieman enemmän kuin time-käskystä, sen tarkkudeksi voisi sanoa parisataa mikrosekuntia.

Mutta se kelpaa tilastollisiin tarkasteluihin - tosin noiden tarkastelujen oikea suorittaminen ja tulosten tulkinta on niin vaikeaa että tulokset jäävät usein epätarkoiksi. Mutta yhden asian ne kertovat luotettavasti: kuka on keskimäärin nopein ja antaa pienimmän hajonnan. Muuten tulokset ovat vain suuntaa-antavia.

***

Silloin kun BASH vielä oli elinkelpoinen koetettiin foorumeilla vastata kysymyksiin: voiko BASH:illa tehdä sitä-ja-tätä. Onhan se kiva ehdottaa jotakin, mutta itseasiassa noihin kysymyksiin on ainoastaan yksi vastaus: tapoja tuon tekemiseen on ainakin miljoona eikä kukaan tiedä sitä parasta tapaa - joten hyvä keino osoittaa itsensä idiootiksi on vastata. Mutta koska kaikkihan me idiootteja ollaan niin ...

Tosiasia kyllä on että BASH:in työkalut on päästetty rappeutumaan surkeaan kuntoon eivätkä sen hampaat pure enää juuri ollenkaan.

***

Aikoinaan tällä foorumilla oli juttua kuinka nopeasti kukin skriptikieli lukee tiedoston ja muuttaa siitä määrättavän sanan tai kirjaimen. Näistä normaaleista skriptikielistä nopein oli Python, mutta hyväksi kakkoseksi tuli BASH:in sed - itse BASH oli niin surkea ettei kukaan siitä edes puhunut.

Mutta senjälkeen BASH on nopeutunut suunnattomasti sillä se on saanut käskyn readarray joten tetävästä saa nykyään tehtyä loopittoman version. Ei sen nopeus ole vieläkään kuin vajaa puolet sed:n nopeudesta mutta toiminta-nopeus on sentään jo mielekäs. Ja BASH:issa on koneen muistissa tiedostosta tehty matriisi josta varmaan on joskus hyötyä. Matriisin jokaisen rivin lopussa on muuten ylimääräinen rivinsiirto - joskus se kannattaa poistaa. Tämmöinen se käsky on:
Koodia: [Valitse]
readarray matriisi < tiedosto; printf '%s' "${matriisi[*]//mikä/miksi}" > jokutoinentiedosto
- muuten tuo mikä voi olla myös yksinkertainen regex, esimerkiksi [[:upper:]] tai [0-9]

***

Kukaan ei pysty puhumaan BASH:ista niin ettei puheessa olisi paljonkin korjattavaa. Joten mikäli BASH:ia opettelee oppilaitoksissa kirjojen ja virtuoosien johdolla oppii sen normaalin kyvyttömyyden. Ja mikäli kokeilee ja kertoo kokemuksistaan päätyy usein naurettavuuksiin ja virheisiin. Mutta jos et ota aasinhattua päähäsi niin  BASH pysyy ikuisesti niin kuolleena kuin miksi virtuoosit ovat sen saattaneet.

Mutta vaikka virtuooseja rienaankin niin se ei merkitse sitä ettenkö tunnustaisi teorioiden arvoa ja virtuoosithan niitä parhaiten hallitsevat. Mutta erittäin harvat osaavat soveltaa niitä käytäntöön - eivätkä suhtaudu vähääkään kannustavasti kun toiset yrittävät jotain mitä luullaan teorian vastaiseksi - kun itseasiassa kyse on vain teorian väärästä tulkinnasta.

***

BASH:issa on hyvät käskyt tekstijononkäsittelyyn, mutta niiden ulkoasu on useimmiten niin hankala muistaa ja naputella koneseen ettei niitä juuri kukaan käytä. Esimerkiksi määrittely missä pienisana sijaitsee isossasanassa:
Koodia: [Valitse]
apu=kemblefordmetri; echo ${apu%met*}
# joka tulostaa kembleford - ja siitä pääsemme funktioon nimeltään tekstijononalkupaikka:

function tekstijononalkupaikka () { apu=$(echo ${1%$2*}  | wc -m); (( $apu > ${#1} )) && echo ei_löydy || echo $(($apu-1)) ;}; tekstijononalkupaikka kemblefordmetri met
joka tulostaa 10. Oikeellisuuden tarkistus:
Koodia: [Valitse]
apu=kemblefordmetri; echo ${apu:10:3} tulostaa: met
- mikäli pienempää tekstijonoa ei isommassa ole niin palautusarvoksi tulee: ei_löydy 
- tekstijonon ensimmäisen merkin järjestysnumero on 0. 
- skannataan muuten lopusta alkuun - siis m on 10 eikä 2

***

BASH:in funktiot ovat samanlaisia kuin muissakin kielissä eli niitä voidaan kutsua samassa skriptissä eripaikoissa toisilla parametreilla. BASH:in funktiot tuntevat sekä arvo- että nimiparametrit. Niiden käyttäminen:
- arvoparametrit: kuulut joukkoon joka ajattelee "elämä ilman eval-käskyä on yksinkertaisempaa".
- nimiparametrit: haluat parametrit käsitelynjälkeen takaisin. Nimiparametrit eivät muuten ilmanmuuta johda eval-käskyn käyttöön.

Koska funktiot ovat perusosa BASH:ia on niiden kutsuminen erittäin nopeaa (noin 25mikrosec.)

Funktiot kannattaa sijoittaa kirjastoihin. Kirjasto on perusmuodossaan ihan tavallinen tiedosto josa on funktioita. Eikä kirjastossa ole lausetta: #!/bin/bash tai muita shebangeja. Kirjasto-tiedostossa saa olla funktioita kuinmonta vaan ja kirjasto-tiedostojakin saa olla kuinkamonta vaan - tai onhan niillä montakin rajaa: käyttäjän kyvyt tulevat ensin vastaan ja sitten koneesta loppuisi muisti. Kunkin tyyppin asioille kannatta tehdä oma kirjastonsa asianmukaisesti nimettyyn tiedostoon.

Jotta skriptissään saisi kirjastot käyttöön kirjoitetaan skriptissä riviä #!/bin/bash seuraaviksi riveiksi:
. ~/bash_kirjastot/ensimmäisen kirjaston nimi 
. ~/bash_kirjastot/toisen kirjaston nimi
. ~/bash_kirjastot/kolmannen ... ja niin monta kirjastoa kun tarvitsee - mutta yksikin kirjastonimi kyllä riittää.

Jotkut eivät halua käyttää kirjastoja. Silloin funktion voi kopioida oman skriptinsä alkuun sieltä kirjastotiedostosta.

***

Funktionimien oikea valinta on tärkeää useastakin syystä:
- kun nimi on kerran valittu tulee sen vaihtamisesta nopeasti vaikeaa olipa siinä kuinka idioottimainen virhe hyvänsä. Nimen täytyy kuvata hyvin sitä mitä funktio tekee - ei saa välittää siitä kuinka pitkä nimestä tulee, sillä tarvittavat nimet tulee jokatapauksessa aina leikata-liimata eikä niitä naputella.
- kun olettehnyt jotakin niin varmaankin haluaisi myöhemmin löytää sen mitä olet tehnyt - ja mikä sen parempi kuin nimi jonka osia voit aavistella. BASH:in merkinnät ovat pikkuisen kummallisia joten vaihtoehtona on etsiä jotain ?<=$2 kaltaista.

Sitä voi itsekseen pähkäillä laittaako nimiin ä:tä ja ö:tä. BASH nimittäin hyväksyy ne mutta editorit eivät aina tykkkää. Sitäipaitsi jos BASH jossain vaiheessa lopettaa toimimisen niiden kanssa niin se aiheuttaa melkoisen hämmingin.
Funktionimissä ei voi olla välilyöntejä edes lainausmerkeissä.

Ovatkoha nämä nimet oikein annettu? Tulevaisuus näyttää:
Koodia: [Valitse]
function hakuasanojenvälinenteksti () { echo "$1" | grep -Po "(?<=$2).*(?=$3)" ;}; hakuasanojenvälinenteksti "jb khavain1[@'123  4567890avain2k jnj" avain1 avain2
# tulostaa: [@'123  4567890
# jos jompaakumpaa avainta ei löydy ei myöskään tulosteta mitään

function avain1onhaussa_avain2täytyyollaperässämuttasitäeivalita () { echo "$1" | grep -Po "$2(?=.*$3)" ;}; avain1onhaussa_avain2täytyyollaperässämuttasitäeivalita "jb khavain111123  4567890avain2k jnj" avain1 avain2
# tulostaa: avain1

function avain1onhaussa_avain2eisaaollahetiperässä () { echo "$1" | grep -Po $2'(?!'$3')' ;}; avain1onhaussa_avain2eisaaollahetiperässä "jb khavain1avain2k jnj" avain1 avai2
# tulostaa: avain1 # mutta ei tulosta mitään kun avai2 muutetaan muotoon avain2

function avain1täytyyollahetiedessämuttasitäeivalita_avain2onhaussa () { echo "$1" | grep -Po "(?<=$2)$3" ;}; avain1täytyyollahetiedessämuttasitäeivalita_avain2onhaussa "jb khavain1avain2k jnj" avain1 avain2

# en edes yritä keksiä nimeä seuraavalle - jolloin siitä voisi tehdä funktion:
# -Poz '(?ism:^BEGIN.*?END)'   # i=älä välitä merkkikoosta, s=begin ja end voivat olla eri riveillä, m=merkin:^ käyttö sallitaan lukitsemaan begin rivin alkuun

Pieniten ja melkomerkityksettömien funktioiden nimeämistä ei saa väheksyä. Esimerkiksi kun joudut sanomaan mikä on kirjaimen A ascii-arvo niin sielusta lentää päreitä kun ei muista eikä kovin nopeasti saa siitä tehtyä skriptiäkään. Mutta funktion nimi on kaikenjärjen mukaan: merkkinumeroksi - ja se löytyy nopeasti.

Koodia: [Valitse]
function numeromerkiksi () { printf \\$(printf '%03o' $1) ;}

function merkkinumeroksi () { printf '%d' "'$1'" ;}

***

Ilman ohjeistusta skripti ei saa dataa käsitellessään olettaa millainen aakkosto on ollut siellä missä sen käsiteltäväksi annettu data on tehty. Eikä tulkki voi mitekään tietää ettei mitään kummallista merkkiä ole tulossa esimerkiksi kun skripti etsii jotain matemaattista.

Skriptikoodissa voidaankin antaa tulkille monenlaisia ohjeita, muunmuassa että käytetään yksinkertaisinta koodisivua skriptiä suoritettaessa. Skriptin alkuun laitetaan silloin lause: export LC_ALL=C. Silloin sriptin viimeiseksi lauseeksi on kirjoitettava export LC_ALL= . Skriptin toimintanopeus nousee silloin - yleensä vain vähän mutta joskus yli kaksinkertaiseksi.
- tuo C on kodisivuista yksinkertaisin ja ääkkösetkin temppuilee kun sitä käyttää.
- ongelmaa on aikoinaan ratkottu ja osin onnistuttukin mutta edelleen esimerkiksi nuo muistikuvaa hyödyntävät skriptit nopeutuvat yli kaksikertaa nopeammiksi silloinkuin käsitellään yksinomaan numeroita tai yksinkertaisia kirjaimia.

***

Yritin äskettäin mitata kuinka skriptin suoritusaika vaihtelee kerrasta toiseen. Teorihan on sellainen, että BASH:in tulkin tulkkaustuloksia säilytetään vähän aikaa - osasekunneista sekunteihin. Jos siis mitataan jonkun skriptin suoritusaika monta kertaa odottaen suoritusten välillä 0.1-20 sekutia niin suoritusajat plottaamalla pitäisi saada erittäin valaiseva käyrä. Ja niin saikin, mutta käyrässä oli aivan liikaa kohinaa jotta siitä olisi voinut varmuudella väittää mitään tarkempaa - eikä vuorokaudenkaan mittaus antanut kunnollista käyrää.

Teorian tarkistamisen sotki se että normaalisti skriptin suoritusaika heittelee luokkaa 10% ja joskus suoritusaika peräti kaksinkertaistuu. Toistin nyt saman mittauksen mutta määräsin skriptin alussa: LC_ALL=C. Se nopeutti hieman mutta ennenkaikkea peräkkäiset suorituskerrat kestivät lähes saman ajan. Ja käyrästä sai selvää jo muutamassa minuutissa: näyttää tosiaan siltä että hetkellistä talletusta tehdään ja nimenomaan että säilytettävät unohdetaan nopeasti.

Siis skriptit seikkailevat epämääräisiä aikoja koodisivullaan joten yksinkertaisin koodisivu on usein paras.

***

Toinen ohje minkä skripti voi tulkille antaa on IFS; se merkki jonka kohdalta lause jaetaan sanoiksi.

Kun skriptinsuoritus alkaa on IFS:n arvona: <välilyönti><tab><rivinsiirto> - perusmuodossaan IFS siis muodostuu kolmesta vaihtoehtoisesta merkistä - jako suoritetaan mikä niistä kohdataankaan. Tuo merkki jonka kohdalta jaetaan ei jää kummallekaan puolelle, vaan se häviää.

Mikäli skripti muuttaa IFS:ää olisi mukava jos sen arvo palautettaisiinkin etteivät muutkin pääse nauttimaan kummallisesti toimivasta päätteestä - tämä edellyttää että skriptissä on funktio jonka se suorittaa silloin kun skriptinsuoritus katkeaa virheeseen. Toki skriptin viimeiseksi lauseeksi laitetaan jokatapauksessa: unset IFS . Tuo lopussa suoritettava IFS:n palauttaminen tehdään lauseilla:
Koodia: [Valitse]
trap jälkiensiivous SIGTERM   
# trap kirjoitetaan skriptin alkuun alustuksien joukkoon. Kun kaikki alustukset on tehty tulevat funktiot'
# eikä niiden järjestyksellä ole väliä. Kirjastoja käytettäessä funktioita ei ole. Kirjastojen liitoskäskyt
# kirjoitetaan skriptin alkuun muiden alustuksieen joukkoon. Siis fnktioiden joukossa on :
function jälkiensiivous (){ unset IFS ;} # mukaan liitetään kaikki muukin siivottava

***

Alkaessaan harrastamaan  BASH:ia ihan jokainen saa eteensä "hyvän konstin" joka on jotain tällaista:
echo "aaa bbb ccc ddd fff" | awk '{print $3}' . Ja tuontapaisia tulee sitten tehtyä aina, sillä toimiiha se ja muistaahan sen helposti. Kitkerä kiitos väärästä opetuksesta. Sillä helposti sitä ei unohda ja totu käyttämään montakertaa nopeampaa menetelmää - tosin se on melko mahdoto muistettavaksi joten kopioipa tästä siitä funktio:
Koodia: [Valitse]
function echofieldno () { read -ra apu <<< "${2}"; echo ${apu[$1-1]}; unset apu ;}
# ja kutsuesimerkki:
teksti="aaa      bbb ccc ddd"     
echofieldno 3 "$teksti"
- todella pitkistä teksijonoista kentän tulostuminen kestää - kuten esimerkiksi tekstijonosta:
$(echo {100000..0}) - mutta silloinkin toiminta on parikertaa nopeampaa kuin awk:illa.

Tiedostoille sovitettuna käsky olisi ihan toisenlainen:
Koodia: [Valitse]
function catlineno () { tail -n $1 $2 | head -n 1 ;}
# ja kutsuesimerkki:
catlineno 292 /boot/grub/grub.cfg
- eihän tätä tarvita juurikoskaan. Mutta väärällä tavalla tässä millisekuntitehtävästä tulee minuutihomma ja BASH:in maine kasvaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 05.08.19 - klo:20.41
Ja samantapaisia hitaita menetelmiä tulee käytettyä monissa tehtävissä: ei kannata käskeä "cat tiedosto | grep jotakin". On nopeampaa toiminnallisesti ja helpommin käsittävääkin käskeä: "grep jotakin tiedosto" - ja sama koskee useimpia muitakin käskyjä: ei kannata käskeä: "cat tiedosto | tail -1" vaan kannattaa käskeä: "tail -1 tiedosto". 

Miksi noitä putkia on alunperinkään opetettu laittamaan jokapaikkaan? 

- awk ja sed ovat yksinään paljon tehokkaampia kuin BASH, mutta BASH:in apulaisiksi niistä ei aina ole sillä ne ovat ulkoisia käskyjä joten niiden lataaminenkin kestää. Niitä kannattaa käyttää vain kun niille annetaan ratkaistavaksi monimutkainen tehtävä.

***

Skriptien nopeuttamiseksi on miljoonia muitakin silkkaa BASH:ia olevia keinoa - eivätkä ne ole kummallisia niinkuin tuo LC_ALL=C joka ei aina edes tehoa. Ja mikäli jollekin toiminnalle löytyy BASH-käsky niin se on myös nopeampi kuin sed ja awk-viritykset. Toisaalta BASH kyllä kompastelee kaikissa esteissä. Esimerkiksi tämmöisiä löytyy:
Koodia: [Valitse]
apu=kilometri; echo ${apu//[a-l]/x}  # tulostaa: xxxomxtrx. Elikä tehdään BASH:issa se muunnos joka
# yleensä tehdään sed:issä. Ja BASH:kin tuntee regex:än, vaikka varsin rajoitetusti. 

apu=kilometri; echo ${apu//kilo/'~'} # tulostaa: ~metri elikä vaihdetaan sananosa eikä merkkejä.

apu=kilometri; echo ${apu#*ilo}                          # tulostaa:metri elikä toimitaan sananosilla
apu=kilometri; apu=$(echo ${apu%*tri}); echo ${apu#*ilo} # tulostaa:me sieltä välistä
# siis kun echotaan niin käsky cut on pahvia.
Samantapaisia on loputtomasti. Ja yleensä ne toimivat matriisienkin kanssa joten niitä voi soveltaa loopittomiin ratkaisuihin - jolloin jo nopeampi nopeutuu lisää. Tosin nuo BASH:illa tehdyt kompastelevat enemmän kun awk ja sed.

***

BASH:in toiminta virhe- ja poikkeustilanteissa:

Mikäli skiptissä viitataan johonkin jota ei ole olemassakaan oletetaan että kutsutaan funktiota jota ei ole määritelty ja kutsutaan automaattisesti funktiota: command_not_found_handle. Koska tuossa funktiossa voidaan tehdä mitävaan skriptintekijä määrää niin sen muodostaminen on skriptintekijän vastuulla. Käytännössä skriptintekijä haukkuu BASH:in kivenrakoon kun ei tiedä tuota funktiota määritellä. Muiden poikkeustilanteiden käsittelemiseen pätee seuraava:

Tehdäänpä koneessa mitähyvänsä niin käyttöjärjestelmä kyllä havaitsee sen ja asettaa vastaavan signaalin. BASH käy jokaise käskyrivin jälkeen tutkimassa määriteltyjen 78:n signaalin tilan ja tekee mitä trap-käskyllä on määrätty tuossa tilanteessa tekemään. Esimerkiksi seuraavankaltaisia trap-komentoja yleensä tehdään:
Koodia: [Valitse]
trap 'echo virherivi:${LINENO[@]};echo ${FUNCNAME[@]} ; read' ERR
trap 'rc=$?; echo "virhekoodi $rc rivillä $LINENO-${FUNCNAME[0]}" ' ERR
trap ' (( $? )) && echo ${FUNCNAME[0]}" palautti virheen:"$?'  RETURN
trap "echo kutsupino: ${FUNCNAME[@]}" RETURN
trap pomppaaTänne SIGINT                    # mitä tehdään kun painetaan CTRL-c (nimi eikä numero)
trap 'echo "Control-C disabled."' 2
trap jälkiensiivous SIGTERM                 # kun skripti loppuu tai tekee suoritusaikaisen virheen
trap 'echo "VARIABLE-TRACE> \$variable = \"$variable\""' DEBUG # muuttujanarvon seuraaminen
set -x; trap "echo hit_return;read x" DEBUG                    # käskyrivien suoritus yksi kerrallaan
- trap:peja voidaan kirjoittaa funktioisakin vaikka ne yleensä määritellään skriptin alussa. Esimrkiksi kun virhettä käsitellään voidaan virhekäsittelyn ajaksi määrätä virhekäsittely pois päältä käskyllä: trap - ERR ja palauttaa se vanhassa muodossaan käsittelyn jälkeen - jottei näyttö sotkeutuisi kovin pahasti.

-samoin trapeissa saa olla funktiokutsujakin.

- jos skriptinsuoritus päätyy virheeseen niin tulostuva virheviesti on BASH:issa normaaliakin kehnompi - mutta ei se virheen tarkka ilmoittaminen paljoa auttaisikaan. Nimittäin skriptintekijälle riittää tieto siitä tapahtuuko virhettä vai ei - ja missä se tapahtuu: silloin virheen syyn pitäisi olla melkoselvä.

- BASH ei lopeta skriptinsuorittamista ihan pienistä virheistä vaikka se asettaakin virheen kohdalla muuttujan $? . Yritetään väittää että tällainen toimintatapa ei ole hyväksyttävää ja neuvotaan laittamaan skriptin alkuun: set -e. Mutta siitä on joskus hyötyä ja joskus haittaa.

BASH-skripteissä ei enää käytetä rakennetta: if..then vaan se korvataan rakenteella:
käsky1 && käsky2 -> käsky2 suoritetaan vain mikäli käsky1 onnistuu, tai:
käsky1 || käsky2 -> käsky2 suoritetaan vain mikäli käsky1 epäonnistuu, tai:
käsky2 && käsky2 || käsky3 -> käskyryhmää käytetään siellä missä ennen oli if..then..else .
- usein käsky1 on ehto, esimerkiksi: [[ -f tiedosto ]], mutta ei sen tarvitse olla. Jokatapauksessa käsky: "set -e" olettaa tuon ensimmäisen olevan ehto ja eikä välitä siitä.
- mieti kaksikertaa mihin || milloinkin viittaa, minkä kanssa se OR-funktion muodostaa.

- useimpia muitakaan tulkille annettavia ohjeita ei kannata antaa muuten kuin virhettä metsästettäessä.

***

Skriptin debuggaamisessa ei ole mieltä - kuvaannollisesti huonompi yrittää korjata parempaansa. Hermotkin kiittää jos heität huonostitoimivan skriptin roskikseen ja teet uuden. Vaikka tuleehan siihen debuggaamiseen joskus syyllistyttyä.

Muutenkin on suositeltavaa välillä "hukata" joku skripti ja tehdä se sitten uudestaan sillä yleensä siitä tulee paljon parempi.

Skriptissä kaikkien käytettävien nimien tulee olla valittu niin ettei kommentointia enää juurikaan tarvita. Nimittäin kommentoidessasi skriptiä tajuat että jos se toimisi toisella tavalla nopeutuisi se paljon. Teet siis uuden version ja kommentointi alkaa alusta ja ennenkuin kommentointi on tehty niin taas huomaat että uusi olisi parempi ja ... Ja koska asiallinen kommentointi vie aikaa enemmän kuin koodaaminen niin hiukka idioottimaista se kommentointi on.

Maailmalla monet skriptit on suurella vaivalla kommentoitu pilalle. Sikäli pilalle että koodia ei ole helppo ymmärtää kun pääosa siitä on kommentointia. Kestää nimittäin kauan ennenkuin ymmärrät toisen kommentointia sillä toiselle ilmanmuuta selvä on toiselle vaikeaa joten sitä ei selitetä tarpeeksi - ja toisaalta toiselle vaikea on toiselle itsestäänselvää joten siitä läärätään turhaan ja loputtomasti.

Ihminen tajuaakin koodin sitä hitaammin mitä useammalla rivillä se on esitetty - ja lisää hitautta tulee jos koodi pursuaa näytön toiselle sivulle - varsinkin raamatun kokoinen skripti on vaikea ymmärtää vaikka siinä vain osoitettaisiin että 1+1=2 vielä tänäänkin. Paras esimerkki on jo vanhentunut käsky if..then..else jota ei pitäisi enää käyttää. Koska sitä kuitenkin käytetään niin minkätakia se täytyy jakaa monelle riville kun sen voi kirjoittaa yhdellekin?

***

Nopein keino skriptin nopeuttamiseksi on poistaa siitä tarpeettomia osia - usein poistaminen johtaa huomattavaan nopeutukseen. Sen havaitseminen mikä on tarpeetonta on valitettavasti kokeellista hommaa ja jotta se onnistuisi järjellisessä ajassa tarvitaan työkaluja - aivankuin niitä työkaluja tarvitaan kaikissa kielissä ja ne ovatkin lähes kaikissa kielissä jo perusversiossa - mutta BASH:ista ne on poistettu ja joudut itse luomaan ne uudestaan.

Samalla BASH:ista on poistunut paljon muutakin. Esimerkiksi BASH on pakotettu käyttämääm merkintätapoja joita ihminen ei kykene käyttämään tehokkaasti. Sama merkintätapojen kummallisuus pätee lopulta kaikkiin kieliin mutta niissä inhottavia merkintöjä ei tarvitse käyttää sillä ne on kätketty kirjastoihin.

Mutta paras keino skriptin nopeuttamiseksi on kirjoittaa skripti kokonaan uudestaan käyttäen parempaa toimintaperiaatetta jolloin nopeutta tulee usein paljon lisää, tulee vakautta ja vaikka mitä - kymmenenkertaa nopeampi on arkipäivää. Kannattaako se on toinen asia sillä BASH on sittenkin niin hidas ettei siitä isompaan käyttöön ole - tai hitaaksi BASH:ia ainakin monet väittävät.

Uudet menetelmät tuovat skripteihin uusia ominaisuuksia - esimerkiksi tässä esitetty muistikuva-menetelmä nopeuttaa oleellisesti useimpia isoja tehtäviä. Jos samalla hyödynnetään kuvattua uutta ja yksinkertaista menetelmää yhden tai useamman erityyppisen parametrin palauttamiseksi funktiosta päädytään johonkin aivan uuteen.

***

- koska tuo parametrien palauttaminen on niin perustavalaatuinen asia - ja koska näin loppuunajettuna en kykene asioita loogisesti esittämään niin tässä parametrien palauttaminen on esitetty niin ettei sitä voi ymmärtää väärin vaikka kuinka tahtoisi :
Koodia: [Valitse]
function pienlukujenkertoma () { let $2=$(echo $(($(seq -s* $1)))) ;}

pienlukujenkertoma 6 kuuskertoma
pienlukujenkertoma 20 kakskytkertoma

echo "6  kertoma: "$kuuskertoma
echo "20 kertoma: "$kakskytkertoma

- parametrien palauttamiseen on muitakin keinoja: esimerkiksi kutsurakenne $( ... ) - elikä palautetaan haluttu arvo näytön kautta; toiminta on erittäin nopeaa eikä jätä näyttöön mitään. Sillä on se puute ettei se toimi oikein kun palautettavaa on montariviä.
- tai peräti niin että funktio kirjoittaa kovalevylle ja kutsuja käy sitten lukemassa kovalevyltä. BASH:in hitauden huomioonottaen tämä on ihan käyttökelpoinen keino.
- nämä kaksi keinoa toimivat silloinkin kun funktiokutsu on muotoa: funktionimi () ( ... ) - siis kaarisulut aaltosulkujen tilalla. Kutsu on hitaampi koska se muodostaa ensin oman prosessinsa - mutta lähes kaikki on sitten ihan omaa joten mikään funktiossa tehty ei näy pääohjelmassa. Paitsi levyoperaatiot.

***

Ikuista ratkutusta:
Missään kielessä ei voi tehdä mitään merkittävää ilman kirjastoja. Olisi mielekästä alkaa uudestaan käyttää BASH:iin  kirjastoja sillä BASH:ista ei tulla pääsemään eroon vaikka halu onkin hirveä - ja kirjastoilla varustettuna se olisi ihan kelvollinen kieli - ja alkaa hiljokseen tuntua ettei se ole mahdottoman hidaskaan mikäli käskee oikein. Kuitenkin BASH:in käskyjä olisi syytä parantaa - ilmeisesti se olisi BASH:in kehittäjille helppoakin, mutta nähtävästi rintakarvoja puuttuu - en minäkään kyllä uskaltaisi käskyjä parannella.

***

Käsky sleep toimii millisekunnista ylöspäin eikä se ole kovin tarkkakaan. Tarkemman viiveen saat käskyllä: read -t .xxxxx  - muuttujaa ei tarvitse määrätä. Aika on sekunneissa joten toimitaan 10:stä mikrosekunnista ylöspäin. Pienillä viiveillä toimimista varten tein sitä testaavan skriptin - sadasta mikrosekunnista yhdeksään millisekuntiin:

Skriptissä on kaksi prosessia pyörimässä yhtäaikaisesti. Ensin omia aikojaan tiedoston lukuarvoa tasaisesti kasvattavava prosessi joka työnnetää taustalle käskyllä: & .

Ja sitten normaalina edusta-ajona toinen nopeampi looppi ottamaan tiedostosta ensin näyte, sitten suoritetaan tasaisesti kasvava viive jonka perään otetaan toinen näyte ja tulos on näyte2-näyte1. Tuloksien tulisi kasvaa tasaisesti, mutta koska BASH on ajallisesti horjahteleva niin eihän näin aina ole. Yleensä tämä ei edes tulosta kaikkia määrättyjä; esimerkiksi jos ensimmäinen prosessi kirjoittaa juuri kun toinen haluaa lukea niin BASH luovuttaa.
 
Suuntaus on kuitenkin selvä joten kyllä pienetkin viiveet toimivat ainakin suurinpiirtein - ehkäpä tarkastikin. Joten vaikka tällainen viive on tällähetkellä tarkoitukseton niin on mukava tietää että tulevaisuudessa tällainenkin viive on käytettävissä. 
Koodia: [Valitse]
#!/bin/bash
for (( n=0; n<=99999999; n++ )); do echo $n > file1; done & # tämän loopin pitää olla hitaampi

apu1=0; apu2=0; for m in {0001..0090}; do read -t 0.$m; apu1=$apu2;apu2=$(cat file1); [[ $apu1 && $apu2 ]] && echo -n $(($apu2-$apu1))' ' ; done
kill $! # tämä tappaa alussa määrätyn taustaprosessin - siis tuon jonka perässä on merkki &
rm -f file1

***

Kevennystä:
Voi sen sinin laskea näinkin:
Koodia: [Valitse]
b='$(echo "s(4*a(1)/180*$a)" | bc -l)'; a=30; eval echo $b
- siis muuttujaan laitetaan se kaava. Tekstijono se on kaavakin.

***

Yhdessä asiassa mistään kielestä ei ole BASH:in kilpailijaksi: yhdenrivin skriptit. Kooltaan ne ovat yleensä alle rivinmittaisia, mutta monet ovat kyllä jopa kolmenrivin mittaisia - mutta aina yksi kokonaisus.

Niitä käytetään usein sellaisenaan sillä päätteessä on tavallaan jo rivi: !#/bin/bash. Eikä noille yhdennrivin skripteille haeta koskaan suoritusoikeutta. Mikäli käytettävissä on kunnollinen varasto yhdenrivinskripejä ja niitä osaa käyttää tuleekin käyttäjästä legendaarinen.

Mutta noita yksirivisiä täytyy olla tuhansia ja ne täytyy olla järjestetty niin että löytää tarvitsemansa. Ja hyvin harvasta on siihen sillä kaikki me hallitsemme epäjärjestyksen.

Näyttää lisäksi siltä ettei ole mitään rajaa sille mitä yksirivisellä voi tehdä. Ja ne ovat hämmästyttävän nopeita.

Mutta on yksirivisillä varjopuolensakin: esimerkiksi ne toimivat usein vain jossakin tietyssä distrossa ja joskus ne tarvitsevat sellaisia järjestelmäasetuksia joita harvassa koneessa on.

***

Aikaisemmin käsityksenä oli että vaikka BASH:illa saakin nopeasti nopeasti tehtyä skriptin mihin vaan, niin siitä selviää tutkittavasta vain senverran että voi päättää kannattaako muilla kielillä tehdä kunnollinen ohjelma. Mutta usein BASH-skripti kelpaa kyllä lopulliseksikin versioksi. Esimerkiksi seuraava joka kelpaa pohjaksi monenlaiseen:

Graafisessa xy-esityksessä akseli voidaan esittää joko lineaarisena tai logaritmisena. Esitettäessä jotakin x-akselin ollessa logaritminen saadaan vähimmillä ponnistuksilla sujuva käyrä jos sen esityspisteet sijaitsevat x-akselilla tasavälein.

Koska pisteiden luomisessa tarvittava laskukaava on BASH:issa mahdoton muistaa tein siitä funktion: tämänjälkeen kun tarvitsee tasavälisiä esityspisteitä ei enää tarvitse laskea niitä vaan voi kutsua funktiota tekemään sen.

Mutta logaritmialueella pisteiden muodostamisessa tuli eteen periaatteellinen ongelma: epäilin että peräkkäisten pisteiden suhde on vakio eikä millääntavalla logaritminen. Tämä täytyi varmistaa tekemällä käyrä jossa peräkkäisten x-akselin pisteiden suhde on vakio - ja mikäli väite pitää paikkansa sijaitsevat pisteet tasavälein koko x-akselilla dekadien vahtuessakin - ja lisäksi exponentiaalisen käyrän kuvaaja on suora y-akselin ollessa lineaarinen:
Koodia: [Valitse]
#!/bin/bash
function luoaskellus () { # lineaarisen  asteikon kutsu: luoaskellus alku loppu askel
                         # logaritmisen asteikon kutsu: luoaskellus alku loppu *suhde 
                        # negatiivisilla arvoilla liikutaan imaginääriluvuissa. Siis nollaa ei saa ylittää
unset $1 # varmistus että matriisissa kaikki on tässä funktiossa muodostettua
[[ $(printf '%d' "'${4:0:1}'") != 42 ]] && apu=$(seq $2 $4 $3 | tr '\n' ' ' | tr , . ) || { echo "for (i = $2 ; i < $3; i *= ${4:1})  i" | bc -l > /tmp/delme; apu=$( cat /tmp/delme | tr '\n' ' ') ;}
apu2=($apu)                                # matriisi x-akselin pisteistä   
readarray $1 < <(printf "%s\n" ${apu2[@]})
let $5=${#apu2[@]} ;}                      # pisteiden lukumäärä   
 
function plotlogx () {
# tämä funktio tulostaa ainoastaan tiedostoon: ~/koe.eps eikä näytölle tule mitääm. Tiedosto avautuu tiedostoselaimella
gnuplot -p -e 'set terminal postscript eps color enhanced; set logscale x ;set xlabel "x"; set ylabel "logaritmi"; set output "~/koe.eps"; plot "~/koe"' ;}

luoaskellus askellus .000001 1000000 *1.5 askelluku
rm -f ~/koe; for (( n=0; n<=$askelluku-1; n++ )); do echo -n ${askellus[n]}' ' >> ~/koe; echo $(echo "l(${askellus[n]})") | bc -l >> ~/koe; done

plotlogx
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 17.08.19 - klo:11.09
Muuttujien vertaaminen ei onnistu matemaattisella vertailulla:
Koodia: [Valitse]
a=2; b=1; (( $1 > $2 )) && echo 'eka on suurempi' # eikä mikään vaihtoehto onnistu tuolle > merkille.
# se onnistuu kylläkin kun kirjoittaa:
(( 2 > 1 )) && echo 'kaksi on suurempi kuin yksi'
Mutta BASH kykenee vertailemaan kokonaisluku- tai desimaalimuuttujiakin ihan peruskäskyillään, ensiksi yhtäsuuruus:
Koodia: [Valitse]
a=2.1; b=2.1; ((${a//./} ^ ${b//./})) || echo yhtäsuuria
# mutta: a=21.1; b=2.11; ((${a//./} ^ ${b//./})) || echo yhtäsuuria  # menee väärin. Korjaus tähän:
a=21.1; b=2.11; ((${a%.*} ^ ${b%.*})) || ((${a//./} ^ ${b//./})) || echo yhtäsuuria
seuraava vääriä tuloksia aiheuttava on .... ja sen korjaus .... Kyllä BASH kykenee kaikkiin korjauksiin mutta kaavat tulevat jatkuvasti pitemmiksi ja mahdottomammiksi muistaa ja loogiset operaatiot saattavat skriptintekijän sekaisin. Nopeus ei laske paljoakaan

***

Sitten varsinainen vertailu. Siitä kannattaa tehdä funktio. Ei se super-nopea ole, mutta eipä kuhnailekaan. Eiköhän siinä ole vielä parsimista, mutta tämmöinen se nyt on:
Koodia: [Valitse]
function onkoekasuurempi () { ((0${1%.*} ^ 0${2%.*})) || (( ${1//./} < ${2//./} )) && echo 0 || echo 1 ;}
#                                                                    ^
#                                            vertailumerkki on tässa | ja sitä voi muuttaa mieleisekseen
# ja sen kutsuesimerkki:
onkoekasuurempi -0.127 -0.126 
Muuten nopeudesta: BASH:in peruskometoja saa olla pitkä rivi ennenkuin  kokonaisus kestää edes yhtäkauan kuin paraskaan ulkoinen ohjelma. Esimerkiksi:
Koodia: [Valitse]
a=-1.01;b=-1.02; echo -e $a'\n'$b | sort -n | tail -1
on hidas ja toimiikin väärin kuten näet kun muutat miinukset plussaksi: plussapuolen suuremman iitseisarvon pitää nimittäin olla negatiivisen puolen pienemmän itseisarvo. Ja tuo pitkä hiviö on montakertaa nopeampikin.

Funktiokutsu vie hyvin vähän aikaa. Funktiokutsuun kuluvan ajan korvaa ylimäärin se että se tekee skriptin rakenteesta selväpiirteisen ja ongelmattoman. Funktiot on syytä tallettaa kirjastoon. Sillä nuo merkinnät ovat tosiaankin niin kummallisia että ellei niitä saa kopioitua jostakin niin käyttämättä jää.
Ja kirjaston toinen nimi on automaattikopioija.

Tämä johtaa aiheseen nimeltään desimaalimatematiikka jota BASH kyllä hallitsee myös. "salamannopeasti" lisäksi; se jää kyllä nähtäväksi pystynkö minä tekemään kaavat sille.

- kaavat ovat tosiaan kauheita vaikka ne suoritetaankin nopeasti. Ja salahautoja olisi runsaasti ja niiden kiertäminen hidasta. Pääsisihän sillä muutamaan millisekuntiin, mutta koska bc toimii ongelmitta jo viidessä milliskunnissa niin toistaiseksi viisainta on sittenkin käyttää seuraavantyylistä:
Koodia: [Valitse]
bc <<< "1.000001+5.27"

***

Vaikka pääohjelmassa ei voi kirjoittaa: (( $1 > $2 )) && ... niin funktiossa se on sallittu:
Koodia: [Valitse]
function max () { (( $1 > $2 )) && echo $1 || echo $2 ;}
 
# ja funktion kutsuesimerkki:
max 5 9
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 20.08.19 - klo:11.33
Muuttujien vertaaminen ei onnistu matemaattisella vertailulla:
Koodia: [Valitse]
a=2; b=1; (( $1 > $2 )) && echo 'eka on suurempi' # eikä mikään vaihtoehto onnistu tuolle > merkille.

Tuossa esimerkissä ei ole määritelty positionaalisten argumenttien 1 ja 2 arvoja, vaan muuttujat a ja b, joten vertailussa on käytettävä niitä:

Koodia: [Valitse]
a=2; b=1; (( a > b )) && echo 'eka on suurempi'

a=2.1; b=2.1; ((${a//./} ^ ${b//./})) || echo yhtäsuuria
# mutta: a=21.1; b=2.11; ((${a//./} ^ ${b//./})) || echo yhtäsuuria  # menee väärin. Korjaus tähän:
a=21.1; b=2.11; ((${a%.*} ^ ${b%.*})) || ((${a//./} ^ ${b//./})) || echo yhtäsuuria
[/code]

Tämä menee metsään ainakin siinä, että vertailuun päätyy oktaalilukuja, jos kokonaislukuosa on 0:

Koodia: [Valitse]
a=0.7; b=0.8; ((${a%.*} ^ ${b%.*})) || ((${a//./} ^ ${b//./})) || echo yhtäsuuria
Koodia: [Valitse]
-bash: �L: 07 ^ 08: liian iso luku lukujärjestelmälle (virheellinen avainsana on ”08”)
yhtäsuuria

En kyllä itse lähtisi toteuttamaan tukea desimaaliluvuille tällä tavalla. Mahdollisia sudenkuoppia on joka mutkan takana.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.08.19 - klo:18.41
Kaikenlaiseen tulee näköjään syyllistyttyä. Mutta en hölmöile ensikertaa joten on helppoa tunnustaa.

Minunmielestäni BASH:in viehätys onkin siinä että kaikessa on miljoona mahdollisuutta virheille - mutta virhetilanteita voi estää muodostumasta. Kaikkia mahdollisia virhepaikkoja ei ole mahdollista huomata mutta minkähyvänsä huomaamansa virhepaikan voi välttää.

Desimaalilukujen vertaaminen on sinälläänkin mukava apu nopeutensa tähden mutta sen varsinainen merkitys on siinä että se avaa oven nopeaan desimaalilaskentaan.

Matematiikkaohjelmat kuten bc viettävät vertailussa aikaa viisikertaa enemmän, mutta sen korvaukseksi niilä monia erikosiominaisuuksia vertailun tueksi. Joten monimutkaiset vertailut täytyy edelleen suorittaa  bc:llä.

Vertailufunktio toimii millisekunnissa vaikka onkin kooltaan hirviö. Funktion pituus ei vaikuta nopeuteen paljoakaan sillä koska käskyjä on vain yksi rivi niin se tulkataan kokonaisuudessan C-kieliseksi ohjelmaksi ja senpäälle aikaa kuluttaa vain yksi funktiokutsu. Tämmöinen vertailufunktio on nyt - mutta sitä täytyy korjata vielä montakertaa: 
Koodia: [Valitse]

function max () { a=${1//+/}; b=${2//+/}; b=${b//-./-0.}; a=${a//-./-0.}; [[ ${a%.*} == $a ]] && a=$a'.0';[[ $b{%.*} == $b ]] && b=$b'.0'; [[ ${a%.*} == '' ]] && a='0'$a; [[ ${b%.*} == '' ]] && b='0'$b; ((0${a%.*} == 0${b%.*})) && { ((${a//[^-]/}1${a##*.}${b##*.} > ${b//[^-]/}1${b##*.}${a##*.})) && echo $a || echo $b ;} || { ((${a%.*} >= ${b%.*})) && echo $a || echo $b ;} ;}

# kutsuesimerkkejä vaikeista vertailuista (muuten kun kummankin luvun etumerkin vaihtaa niin valitunkin pitää vaihtua):
max 2222222222222222.000000000000000001 2222222222222222.000000000000000002
# max .08 +.079
# max -.08 +.079
# max +.99 1

- vertailu tehdään bc:llä yksinkertaisimmillaan näin: bc <<< "$a > $b" joka tulostaa totuusarvon 0 tai 1.
- tekstijonona vertaaminen onnistuu sekin ihan hyvin. Sekin on nopea, hyväksyy desimaalit ja rajattomasti numeroita, ei välitä vaikka desimaalipiste olisikin pilkku eikä ole aina kovin kranttu vieraiden merkkienkään suhteen. Mutta se ei toimi oikein etumerkkien suhteen, ei ole niin nuuka missä se desimaalipiste on ja voi höpertää muutenkin, joten täytyy miettiä kaksikertaa missä sitä käyttää. Siis tarkoitan tämäntyyppisiä vertailuja:
[[ 2.19mW < 2.20mW ]] && echo 'eka on pienempi'

***

Desimaalilaskenta. Ensin yhteelasku, ja alkuunsa vain jotenkin toimiva periaate:
Koodia: [Valitse]
luku1=1.2; kokonaisosa1=${luku1%%.*}; desimaaliosa1=${luku1##*.}
luku2=3.4; kokonaisosa2=${luku2%%.*}; desimaaliosa2=${luku2##*.}

kokonaisosiensumma=$(($kokonaisosa1+$kokonaisosa2))
desimaaliosiensumma=$(($desimaaliosa1+$desimaaliosa2))

echo $kokonaisosiensumma.$desimaaliosiensumma

Mutta desimaalien yhteenlaskemisessa on sekin ongelma että kun yhteenlaskettavien desimaalien määrä on erilainen niin yhteenlaskettaessa tulee melkoinen virhe mikäli desimaalilukumäärät eroavat. Sen korjaukseksi pikkuisen huonolla mutta yksinkertaisella tavalla voi lisätä kummankin desimaaliluvun loppuun niin monta nollaa kuin toisessa on merkkejä sillä silloin ne ovat yhtäpitkiä:
Koodia: [Valitse]
desimaaliosa1pituus=$(echo ${#desimaaliosa1}); desimaaliosa1=$desimaaliosa1$(printf "%0"$desimaaliosa2pituus"d")
desimaaliosa2pituus=$(echo ${#desimaaliosa2}); desimaaliosa2=$desimaaliosa2$(printf "%0"$desimaaliosa1pituus"d")

Seuraava korjattava on desimaaliosan ylivuoto. Ylivuoto on otettu huomioon jo perusasetelmassa, siinähän jo alkujaan on kummankin desimaaliosan alussa suojanumerona 1 joten ylivuotonumerona on 2. Siis kun tapahtuu todellista ylivuotoa niin ylivuotonumeroksi tulee 3. Muuta ei tarvitse tehdä kuin lisätä desimaaliosan ylin numero kokonaisosan lopputulokseen ja vähentää 2. Siis viimeinen lause muutetaan muotoon:
Koodia: [Valitse]
echo $(($kokonaisosiensumma+${desimaaliosiensumma:0:1}-2))."${desimaaliosiensumma:1}"

***

"BASH ei tunne desimaalilukuja ja niitä ei voi käyttää matemaattisissa tehtävissä - niitä ei voi laskea yhteen, kertoa eikä mitään muutakaan matemaattista. Ne ovat vain tekstijonoja vailla matemaattista merkitystä". Se siitä pilkunviilaamisesta sillä myös BASH:issa on käsitelty desimaalilukuja aina - eikun tekstijonoja.

Tein tähän desimaalimatematiikkaan parikin erilaista skriptiä yhteenlaskusta - muuta ei niitä kannata esitää sillä niillä on vielä paljon rajoitteita vaikka ne toimivatkin muuten hyvin. Nuo rajoitteet voisi kiertää paremmalla koodilla, mutta ei taida kannattaa - ei BASH:in matematiikasta hyvää saa tekemälläkään - luultavasti. Mutta loppupäätelmä totisesti kannattaa esittää: tämä on kokonaisuudessaan aluetta joka on tarkkaan suunniteltu BASH:in alkuperäisten kehittäjien toimesta. Siis seuraan joidenkuiden jalanjäljillä ja näitä BASH:in desimaalilaskimia on ollut jo aikoinaan - miksi niistä ei ole kerrottu en käsitä. Vai onko niistä kerrottu mutta on miellyttävämpää kirjoittaa BASH-pitfail:eista?

***

Taas kertaalleen ällistyin sitä kuinka BASH:illa voidaan ratkaista mikähyvänsä ongelma monin eritavoin huolimatta väitteistä ettei onnistu ollenkaan. Esimerkiksi desimaalilukujen vertailussa edellinen nopeuskuningashan oli:
Koodia: [Valitse]
function max () { a=${1//+/}; b=${2//+/}; b=${b//-./-0.}; a=${a//-./-0.}; [[ ${a%.*} == $a ]] && a=$a'.0';[[ $b{%.*} == $b ]] && b=$b'.0'; [[ ${a%.*} == '' ]] && a='0'$a; [[ ${b%.*} == '' ]] && b='0'$b; ((0${a%.*} == 0${b%.*})) && { ((${a//[^-]/}1${a##*.}${b##*.} > ${b//[^-]/}1${b##*.}${a##*.})) && echo $a || echo $b ;} || { ((${a%.*} >= ${b%.*})) && echo $a || echo $b ;} ;}

Sille löytyi koodiltaan ja toimintaperiaatteeltaan erilainen voitaja joka toimii vielä nopeammin ja sallii melkolailla rajoittamattoman desimaalimäärän vertailtaviin lukuihin:
Koodia: [Valitse]
function max () { [[ ${1//[^-]/} ]] && m1=-1 || m1=1; [[ ${2//[^-]/} ]] && m2=-1 || m2=1; [[ ${1//./} == $1 ]] && a=$1".0" || a=$1; [[ ${2//./} == $2 ]] && b=$2".0" || b=$2; a=${a//+/}; b=${b//+/}; b=${b//-./-0.}; a=${a//-./-0.}; ((10#${a%.*} == 10#${b%.*})) && { (($m1*10#${a#*.} >=  $m2*10#${b#*.})) && echo $1 || echo $2 ;} || { ((10#${1%.*} > 10#${2%.*})) && echo $1 || echo $2 ;} ;};
# ja esimerkkikutsu:
max -1111111111111111111111111111111111111111.000000000000000000000000000000002 -1111111111111111111111111111111111111111.000000000000000000000000000000003

- ihan varmasti on edelleen tilanteita joissa funktio toimii väärin sillä koska BASH on opetuskieli on siinä erittäin runsaasti kompastuskiviä - ja semmoinenkin ilmiö on että jos suorittaa korjauksia väärässä järjestyksessä tulevat ne vanhat virheet pahempina takaisin joten loppuelämä kuluu rattoisasti testatessa alusta alkaen jokaisen korjauksen jälkeen.

- samoin on ihan varmaa että parempikin ratkaisu löytyy.

***

Desimaalilaskenta aivan uudestaan - ei ne vanhat väärin ole, kyllä niissä ajatuksentynkää on.

"BASH ei tunne desimaalilukuja ja niitä ei voi käyttää matemaattisissa tehtävissä - niitä ei voi laskea yhteen, kertoa eikä mitään muutakaan matemaattista. Ne ovat vain tekstijonoja vailla matemaattista merkitystä". Tarkoituksena on luoda mielikuva etä BASH ei pysty desimaalilaskentaan, mutta niin ne eivät sanoneet sillä tiesivät itsekin että kyllä pystyy - ja joissain tilanteissa säällisen nopeastikin. Hyvin harvoin desimaalilaskentaa kannattaa silti nykyisellään  käyttää ja saako siitä koskaan kelvollista yleisempään käyttöön on toinen asia - mutta kun esitetään asia niin ettei BASH kykene niin se on törkeää sillä senjälkeen ei edes yritetä. Nimittäin kaikessa on aina parantamisen varaa, ideoista se vain riippuu - ja teoriatiedoistakin.

Ilmanmuuta BASH:iin on aikoinaan tehty desimaalilaskennan funktioita - sillä jokaisessa funktioden tekemisen vaiheessa tuntuu siltä että nyt kuljetaan jonkun jalanjäljissä. Joskus tarvittavat funktiot ovat sangen yksinkertaisia mutta esimerkiksi jakolaskufunktion tekeminen vaatisi matematiikkaneroa - mutta eiköhän joku ole tehnyt jakolaskuunkin funktion. Kukahan on aktiivisesti "unohtanut" että desimaalilaskentakin toimii - ja uskotellut muillekin niin?

Siten käytäntö: koska käyttäjä ei voi määritellä + merkkiä uudestaan on yhteenlaskuun suorittamiseksi käytettävä funktiokutsua: add luku1 luku2  - ja mikäli skriptissään haluaa suorittaa yhteenlaskun niin skriptissä merkintä on tyypiltään: vastaus=$(add luku1 luku2)

Desimaaliyhteenlaskun funktio toimii periaatteessa tällätavoin: laskettaessa yhteen lukuja, esimerkiksi 222 ja 0.123 muokataan luvut siten, että niiden desimaaliosissa on yhtämonta numeroa: 
                         222.000
                           0.123
Tämänjälkeen lasketaan erikseen yhteen kokonais- ja desimaaliosat ja tulostetaan ne desimaalipisteen eroittamina peräkkäin. Desimaaliosia yhteenlaskettaessa etunollat poistettaisiin tulkin toimesta ellei sitä estettäisi: koska desimaaliosat ovat yhtäpitkät niiden molempien eteen lisätään suojanumeroksi 1 ja tulostettaessa jätetään tuo suojanumero pois joten "etunollat" säilyvät mutta noita suojanumeroita ei tulosteta. Desimaaliosan ylivuoto yhteenlaskussa otetaan huomioon siten että kokonaisosaan lisätään desimaaliosan ensimmäinen numero (=se suojanumero - ja ylivuoto kasvattaa sitä) ja vähennetään tuloksesta 2.
- sekä kokonais- että desimaaliosassa kummassakin saa olla korkeintaan 19 numeroa.
- mikäli lukualue ylittyy ei edes tule varoitusta.
- toimintanopeus jää noin 4:ään millisekuntiin - ehkä vähän paremmaksi kuin mitä matematiikkaohjelmat tarjoavat.
Koodia: [Valitse]
function add () {
# reset # testauksen aikana tarpeen mutta käytössä kommentoitava
# [[ ${1//[^-]/} ]] && m1=-1 || m1=1; [[ ${2//[^-]/} ]] && m2=-1 || m2=1 # vähennyslaskun alkulause
luku1=$1
luku2=$2
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
desimaaliluku1=${luku1##*.}
desimaaliluku2=${luku2##*.}
 
(( ${#desimaaliluku2} >= ${#desimaaliluku1} )) &&
{ apu=$desimaaliluku1"0000000000000000000"; desimaaliluku1=${apu:0:${#desimaaliluku2}} ;} || { apu=$desimaaliluku2"0000000000000000000"; desimaaliluku2=${apu:0:${#desimaaliluku1}} ;}
# echo a$desimaaliluku2' 'b$desimaaliluku1 ; read # testatessa tämä on tarpeen

kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
desluku=$((1$desimaaliluku1+1$desimaaliluku2)) # 1 on se suojanumero
echo $((10#$kokonaisluku1+10#$kokonaisluku2+${desluku:0:1}-2)).${desluku:1} ;}
koe suurimmalla sallitulla numeromäärällä ja ylivuodolla:
add 1111111111111111111.511111111111111111 1000000000000000000.600000000000000001  # ja se tulostaa:
    2111111111111111112.111111111111111112

***

Päivitin positiivisten desimaalilukujen skriptin. Millisekunnin se enää kestää hakaten 1-4 matematiikkaohjelmia käyttävät laskut. Pitää kai sitten yrittää tehdä siitä versio joka hyväksyy negatiivisetkin luvut.

BASH:in sisäisten käskyjen suorittaminen on ihmeen nopeaa: iso koodiröykkiö vie aikaa vain millisekunnin.

Jatkuva testaaminen puuduttaa ja välillä täytyy ajatella jotakin muuta:

Teoria on että skripti suunnitellaan etukäteen niin hyvin ettei se virheile - sillä korjaamalla ei saa moitteetonta vaan se täytyy synnyttää virheettömänä. Mutta ei skriptiä noin tehdä - harhaluulo tulee siitä että kuvitellaan että skriptinteossa merkitsee jotakin mitä siinä BASH:in kelvottomassa virheviestissä lukee. Niitä ei lueta - se riittää viankorjaukseen että virhe ylipäätään tapahtuu.

En aikaisemmin käsittänytkään että itseasiassa BASH:in mollaajat kaivavat linuxille hautaa - ymmärtämättä että heitä on älytetty.

***

Tällainen tästä BASH:in yhteenlaskusta tuli. Onhan tämä neljäkertaa nopeampi kuin matematiikkaohjelmat, mutta myöntää täytyy että en jaksanut testailla ja silittää ryppyjä joten eihän tämä vielä luotettava ole. Ainoastaan positiivisten desimaalilukujen yhteenlasku on aikahyvin testattu. Mutta tekee naurettavaksi väitteet ettei BASH osaa. 

Tämä kelpaa kaikkiin yhteenlaskuihin - siis kummankin yhteenlaskettavan etumerkki voi olla + tai -. Siis siten tämä kelpaa vähennyslaskuihinkin. Lukualue on 19 numeroa sekä kokonais- että desimaaliosassa.
Koodia: [Valitse]
function add () {
# reset # testauksen aikana tarpeen mutta käytössä kommentoitava
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
luku1=$1
luku2=$2
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
desimaaliluku1=${luku1##*.}
desimaaliluku2=${luku2##*.}
(( ${#desimaaliluku2} >= ${#desimaaliluku1} )) &&
{ apu=$desimaaliluku1"0000000000000000000"; desimaaliluku1=${apu:0:${#desimaaliluku2}} ;} || { apu=$desimaaliluku2"0000000000000000000"; desimaaliluku2=${apu:0:${#desimaaliluku1}} ;}
#echo a$desimaaliluku2' 'b$desimaaliluku1 ; read # testatessa tämä on tarpeen
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
case $m1$m2 in
-- ) desluku=$((1$desimaaliluku1+1$desimaaliluku2)) && apu=$((10#$kokonaisluku1+10#$kokonaisluku2$m2${desluku:0:1}+2)); (( $apu )) && echo $apu.${desluku:1} || echo -0.${desluku:1} ;;
++ ) desluku=$((1$desimaaliluku1+1$desimaaliluku2)); echo $((10#$kokonaisluku1+10#$kokonaisluku2+${desluku:0:1}-2)).${desluku:1} ;;
+- ) desluku=$((1$desimaaliluku1-1$desimaaliluku2)) && apu=$((10#$kokonaisluku1+10#$kokonaisluku2$m2${desluku:0:1})); (( $apu )) && echo $apu.${desluku:1} || echo -0.${desluku:1} ;;
-+ ) desluku=$((1$desimaaliluku1-1$desimaaliluku2)); echo $((10#$kokonaisluku1+10#$kokonaisluku2+${desluku:0:1})).${desluku:1} ;;
esac
}


Kokeita:
add 1111111111111111111.511111111111111111 1000000000000000000.600000000000000001  # ja se tulostaa:
    2111111111111111112.111111111111111112

add 08.08 08.08

***

Desimaalilukujen kertominen suoritetaan BASH:issa niin että desimaalipisteet poistetaan ja kerrotaan muodostunet kokonaisluvut. Syntyvässä numerosarjassa on oikeat numerot ja mikäli ratkaisee ongelman mihin desimaalipiste sijoitetaan niin tulos on oikea. Koodina tämä on:
Koodia: [Valitse]
function multiply () {
luku1=$1
luku2=$2
[[ $luku1 == 0 || $luku2 == 0 ]] && echo 0 && return
[[ ${luku1//[^.]/} ]] || luku1=$luku1".0"
[[ ${luku2//[^.]/} ]] || luku2=$luku2".0"
desimaaliosa1=${luku1##*.}
desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}
desimaaliosa2pituus=${#desimaaliosa2}
desimaalejakaikkiaan=$(($desimaaliosa1pituus+$desimaaliosa2pituus ))
tulo=$((${luku1//./}*${luku2//./}))
echo ${tulo:0: -$desimaalejakaikkiaan}.${tulo: -$desimaalejakaikkiaan} ;}

Vertaa itse:
time multiply 9.65438761 -.3723476896
time bc <<< "scale=18;9.65438761*-.3723476896"
- mutta toisaalta bc:ssä merkkiluku on rajoittamaton ja siihen saa helposti mukaan myös sinit, logaritmit ja muut.
- desimaalien maksimimäärä on tässä BASH-toteutuksessa pieni. Se korjaantuu tulevaisuudessa kaksoistarkkudella. Tai nelois ...
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 07.09.19 - klo:14.38
BASH tuntee ainoastaan kokonaisluvut ja niissäkin luvuissa saa olla korkeintaan noin 19 numeroa. Tuo numeromäärä ei riitä mihinkään, mutta BASH sunniteltiinkin käyttämään matematiikkaongelmissaan kunnon matematiikkaohjelmia.

Mutta yksinkertaisissa matematikkaongelmissa BASH on silti nopeampi kuin nuo matematiikkaohjelmat. Ihan ensimmäiseksi rajoitteeksi tulee pieni numeromäärä ja aluksi tehdään kaksinkertaisen numeromäärän summain kaikenmerkkisille kokonaislvuille joten tätä voi käyttää vähennyslskussakin. Toiminta-aika on luokka 1ms.
             
function addd () { # kokonaislukujen summain kaksinkertaisella numeromäärällä
luku1pituus=${#1}
luku2pituus=${#2}
luku1=00000000000000000000000000000000000000$1
luku2=00000000000000000000000000000000000000$2
luku1=${luku1: -36}
luku2=${luku2: -36}
# echo $luku1
# echo $luku2
luku11=${luku1:0:18}
luku12=${luku1:18}; luku12=${luku11//[^-]/}$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; luku22=${luku21//[^-]/}$luku22
# echo a$luku11' 'b$luku12
# echo c$luku21' 'd$luku22
tokaluku=$((10#$luku12+10#$luku22))  # Kun käy niin että tokaluku vuotaa yli lisätään ekalukuun 1 ja #leikataan tokaluvusta ensimmäinen merkki pois (se on se ylivuotonumero ja sen arvo on silloin 1)
((10#${#tokaluku} > 10#${#luku12} || 10#${#tokaluku} > 10#${#luku22} )) && ylivuoto=1 || ylivuoto=0
(( $ylivuoto )) && tokaluku=${tokaluku:1}
# echo $ylivuoto
[[ ${luku11//[^-]/} == - && ${luku21//[^-]/} == - ]] && ekaluku=$((10#$luku11+10#$luku21-$ylivuoto)) ||ekaluku=$((10#$luku11+10#$luku21+$ylivuoto))
(( $ekaluku )) && echo $ekaluku$tokaluku || echo $tokaluku ;}

tarkistus:
addd 55555555555555555555555555555555555 55555555555555555555555555555555555
pitää tulla: 111111111111111111111111111111111110

tai:
addd 55555555555555555555555555555555555 -55555555555555555555555555555555555
pitää tulla: 0

tai:
addd -55555555555555555555555555555555555 -55555555555555555555555555555555555
pitää tulla: -111111111111111111111111111111111110

tai:
addd 55555555555555555555555555555555555 555
pitää tulla: 55555555555555555555555555555556110

tai:
addd 555 555
pitää tulla: 1110

tämän kaksoistarkkuuden saisi desimaalilaskuihinkin. Niinkuin kertolaskuunkin. Ja voisi siirtyä 3:nkertaiseen numeromäärään ja siitä ylöspäin. Ja skriptit toimisivat edelleen kohtuullisen nopeasti. Ja tämä olisi toiminut jo silloin kun asialla oli jotain merkitystä käytännössäkin.

aivan varmasti skriptissä on vielä korjattavaa - niinkuin missähyvänsä skriptissä.

***

Jakolasku kokonaisluvuille. Tulos voi kuitenkin olla desimaaliluku ja se esitetään 16:lla desimaalilla.

Taas kertaalleen tämän tekemisessä vaivasi "ajatukset jämähdyttävä väärä luulo" - ja kuvittelin kauan että BASH:ille saa vain matematiikkanero tehtyä skriptin jakolaskuun - ja sekin on tuhatrivinen ja hidas - enkä siis uskaltanut yrittääkään. Mutta itseasiassa skriptinteossa ei nokka kauaa tuhissut.

Tämä jakolasku ei toimi oikein kaikissa tilanteissa mutta on tosiyksinkertainen, desimaaleja voi olla paljon ja lisäksi skripti on helppo ymmärtää. Desimaalijakolaskussa virhetoiminta poistetaan, mutta sen skripti on senkintakia paljon sotkuisempi.
Koodia: [Valitse]
function divide () {
luku1=$1
luku2=$2
kokonaiset=$((10#$1/10#$2)); jakojaannos="$((10#$1%10#$2))"
desimaalit="$(((1000000000000000*10#$jakojaannos)/10#$2))"
echo $kokonaiset.$desimaalit ;}
tarkistus:
divide 9000000000000001 3
Pitää tulla: 3000000000000000.33333333333333333

***

Jakolasku desimaaliluvuille. Kokeile, tämä on virtuooseille niin nyöryyttävää että tekevät parhaansa tämän torpeedoimiseksi.

Tämä täydentää desimaalilaskut sillä yhteenlaskua voi käyttää myös vähentämiseen. Kaikkien desimaalilaskujen suoritusnopeus on luokkaa 1ms kun matematiikkaohjelmia käytettäessä desimaalilaskut kestävät luokkaa 4ms - melkein koko 4ms menee siihen kun matematiikkaohjelmaa luetaan levyltä.

Desimaaliluvulla voi jakaa toisen desimaaliluvun kokonaisluvuille sopivalla menetelmällä kun poistaa luvuista desimaalipisteet - numerot tulevat silloin edelleen oikein.

Mutta kokonaisosa tulee heti aluksi oikein vain kun desimaaliosat ovat yhtäpitkät - siis jos ne eivät ole yhtäpitkät niin lyhyemmän desimaaliosan omaavan perään kirjoitetaan tarpeeksimonta nollaa senjälkeen kun on varmistettu että niissä molemmissa on desimaalipiste ainakin lopussa.
 
Tällä desimaaliosien yhtäpituuden varmistamisella on toinenkin syy: desimalit lasketaan muuten väärin.

Jokatapauksessa desimaaleja laskettaessa on vaikeutena se että etunollat katoavat eikä matematiikka kerro montako niitä etunollia on ollut. Mutta kun kirjoittaa desimaalien laskemisen jälkeen niiden eteen paljon nollia ja katkaisee muodostuneen desimaalijonon perästälukien siihen mittaan joka desimaaleille on varattu ovatkin etunollat näennäisesti tallella.

Sitten vain tulostetaan kokonaisosa <desimaalipiste> desimaaliosa

Skriptin loppuhionta on iäisyyskysymys: esimerkiksi desimaalien määrää saisi varmasti lisättyä ja oikeellisuuden tarkistaminen kestää vuositolkkua.

Tämä olisi toiminut aina - tässä ei ole mitään uutta tai keksinnöllistä - suuret virtuoosit ovat "unohtaneet" ratkaisun sillä itseasiassa tämä on noviisi-tasoa ja sopivien käskyjen löytyminen osoittaa että tämä on tehty jo. Mutta tämä kaipaisi kipeästi parantamista varsinkin desimaalimäärän suhteen - suuret ulkomaiden virtuoosit pystyisivät siihen ja toivottavasti nyt yrittävät päästä eroon narrinhatusta jonka ne tässä sai sillä tämä suorittaa "mahdotoman tehtävän" huippunopeasti.
Koodia: [Valitse]
function jaa () {
luku1=$1
luku2=$2
[[ ${luku1//[^.]/} ]] || luku1=$luku1"."
[[ ${luku2//[^.]/} ]] || luku2=$luku2"."
desimaaliluku1=${luku1##*.}
desimaaliluku2=${luku2##*.}
(( ${#desimaaliluku2} >= ${#desimaaliluku1} )) &&
{ apu=$desimaaliluku1"0000000000000000000"; desimaaliluku1=${apu:0:${#desimaaliluku2}} ;} || { apu=$desimaaliluku2"0000000000000000000"; desimaaliluku2=${apu:0:${#desimaaliluku1}} ;}

kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliluku1 #; echo $desimaaliluku1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliluku2 #; echo $desimaaliluku2
 
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
echo $kokonaiset.${desimaalit: -9} ;}
Tarkistus:
jaa 6666666666.250000002 2
täytyy tulla: 3333333333.125000001

tai:
jaa 1 1000000000
täytyy tulla: 0.0000000001

tai:
jaa 1233457890123.23 .123456
pitää tulla: 9991072852864.421332296
- lähellä lukualueen ylärajaa
- matematiikkaohjelma: bc <<< "scale=9;1233457890123.23/.123456"  tulostaa: 9991072852864.421332296

***

Desimaalijakolasku-skriptin uuden version suoritusnopeus on 0.4ms vaikka se muodostuu 21 lauseesta. Oikeantyyppisistä käskyistä muodostuvat BASH-skriptit voivat olla todella nopeita, viisikymmentäkertaa nopeampia kuin normaalisti.

Syy uuden version tekemiseen olikin se että halusin varmistaa että tuollaisten käskyjen suorittamisessa BASH on nopea.

Jokatapauksessa desimaalijakolaskun tuloksessa esitettävien desimaalien lukumäärä on vanhassa versiossa vain 9. Uudessa versiossa nostin desimaalien lukumäärän 17:ään. Lisäämisen vaatima laskenta ei hidastanut havaittavasti.

Nyt alkaa varmistua että BASH on täysin erilainen kuin miksi se esitetään. Minkähäntakia sitä roimitaan? Antaako se liikaa vapauksia eivätkä isoiset pidä siitä?

***

Saapa nähdä kuinka neliöjuuren laskeminen onnistuu - kovin rajoitettua se kyllä on, luokkaa 7 desimaalia. Veikkaanpa ettei se montaakaan millsekuntia kestä. Ehkä se on vain toive.

Täytyy tosiaan vetää länkiä kaulaan. Kyllä se lopulta onnistui mutta nopeudesta en enää puhu mitään sillä koodi on kammottavan suuri - mutta toisaalta desimaalipilkkujen raahaaminen matkassa tekee koodista pitkän - eiköhän se koodi tänne kohta ilmesty mutta välillä muuta.

***

BASH on piikki isojen putiikkien ahterissa sillä BASH antaa koneen käyttäjänsä leikkikaluksi. Kyseessä eivät ole nuo tekstinkäsittely tai numeronmurskaus-tehtävät vaan se että BASH:illa on syvällinen liitto käyttöjärjestelmän kanssa. BASH:ia käyttäessäsi hallitset omaa konettasi ja isojen putiikkien mielestä koneesi hallinta kuuluu yksinomaan heille - nimenomaan sinulla itselläsi ei saa olla mitään oikeuksia eikä edes kykyjä tarkistaa mitä koneessasi tehdään. 

Ja BASH:in kehittäjät auttavat noita putiikkeja ansiokkaasti: oletkos koskaan tullut miettineeksi miksi noita deb:bejä ja PPA:oita harrastetaan niin kiihkeästi? Olisikohan sillä jotain tekemistä senkanssa että BASH:illa menee tosihuonosti? Ja isot putiikit nauravat partaansa kun saivat distrot älytettyä yrittämään tuhota BASH.

Sillä nuo hienot palikat tuhoavat yritteliäisyyden koska kaikki mitä aikaansaat ovat noiden palikoiden rinnalla todella hitaita virhepesiä. Ja kun yritteliäisyys kuolee niin käy juuri niin kuin on käynyt BASH:ille: perustaidotkin katoavat. Ja niinpä ollaan päädytty tilanteeseen josta nouseminen on todella vaikeaa - ja isot putiikit yrittävät estää BASH:ia saamasta entistä asemaansa.

BASH:ia lyödään kuin vierasta sikaa virtuoosienkin taholta. Tosiasia kyllä on että BASH on hidas ja opetustarkoituksiin tehtynä siinä on niinmonta kompastuskiveä ettei kukaan selviä niistä kaikista. Silti BASH:in kanssa tässä nyt eletään eikä siitä päästä eroonkaan.

Mihinkä todelliset virtuoosit pystyisivätkään jos eivät kokoajan vain toistaisi hypnoosiin vaipuneina ettei BASH kykene?

Ilmeisesti montakin läpimurtoa on tehty näiden "BASH ei kykene" väittämien suhteen. Vaikka useimmat väitteet lienevät vain omaakehua niin jotkut taitavat pitää paikkansa. Mutta alkaapäälle nuo ratkaisut ovat ilmanmuuta hitaita, reikäisiä ja vaikeasti käsitettäviä hirviöitä eikä niitä kehdata esittää eikä jakseta jalostaa - sillä jalostaminen on niin rankkaa että terveys menee. Tuo kehtaaminen on erittän käsitettävää: esittämääsi raadellaan säälittä ja narrinhattua ei kukaan halua kantaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 15.09.19 - klo:13.43
Desimaaliluvun neliöjuuren määritys pelkästään BASH:in peruskäskyillä.

Mikäli x:n neliöjuuri on y niin silloin: y*y=x. Tätä tosiasiaa soveltaen voi tehdä binäärihaun neliöjuuren selvittämiseksi.
- tämän ohjelman pääasiallisin tarkoitus ei ole neliöjuuren etsiminen vaan desimaalilaskennan toimimisen testaaminen: jos yksikin palikka höpertää edes joskus niin metsään mennään. Silti binäärilaskentakin on merkityksellinen.

Binäärihaussa tässätapauksessa tarvittavat ohjelmat - mutta kaikissa toteutuksissa ne ovat samantapaisia:
y*y (luvun kertominen itsellään)
kahdellajako
yhteenlasku
vähennyslasku ( voidan käyttää yhteenlaskua kun yhteenlaskettavan eteen laitetaan miinusmerkki)
lukujenvertailu       
binäärihaun ohjaus.

- desimaalipisteiden kuljettaminen mukana kuluttaa paljon aikaa koskei Bash desimaalipistettä edes tunne. Mutta tässävaiheessa desimaalipisteiden mukanaolo tekee toiminnasta käsitettävämmän. Ainoastaan desimaaliluvuista väliltä 2-6 saa neliöjuuren käytettyjen toteutuksien rajoitteiden takia. Näidenkin lukujen neliöjuurissa on vain noin 8 desimaalia ja aikaakin kuluu luokkaa 80ms johtuen osittain siitä että toiminta vaatii noin 2100:n lauseen suorittamisen. Desimaalilaskennassakin on loputtomasti parannettavaa nopeuden, kykyjen ja koodin koon suhteen.  Toiminnankuvaus:

1. Muodostetaan alkuarvot: y=x/2 ja delta=x/4. Haun alkuarvoksi otetaan y.   
2. aloitetaan esimerkiksi 30:een hakuun rajoitettu binäärihaku. Teoriassa lopputulos tarkentuu 3:lla desimaalilla jokaista kymmentä binäärihakua kohden.
3. lasketaan y*y
4. mikäli y:ssä desimaalipisteen kanssa on yli 11 merkkiä niin tulostetaan y ja lopetetaan. Tätä kohtaa ei välttämättä kannata toteuttaa.
5. verrataan tulosta x:ään ja vertailun perusteella lasketaan uusi y kaavalla: y=y+delta tai y=y-delta
6. delta jaetaan kahdella ja palataan binäärihaun alkuun.
7. kun binäärihaku on loppunut niin tulostetaan y ja lopetetaan.

Koodia: [Valitse]
function y*y () {
luku1=$1
luku2=$1
[[ ${luku1//[^.]/} ]] || luku1=$luku1"."
[[ ${luku2//[^.]/} ]] || luku2=$luku2"."
desimaaliosa1=${luku1##*.}
desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}
desimaaliosa2pituus=${#desimaaliosa2}
desimaalejakaikkiaan=$(($desimaaliosa1pituus+$desimaaliosa2pituus ))
tulo=00000000000$((10#${luku1//./}*10#${luku2//./}))    # mikäli kokonaisia ei ole ja desimaaliosan alussa on nollia katoavat ne tässä laskussa joten lisätään tuloksen eteen paljon nollia.
luku=${tulo:0: -$desimaalejakaikkiaan}                  # etunollia otetaan lopputulokseen kumminkin vain niin monta kuin niitä on kadonnut. Siis yleensä ei yhtään.
echo $((10#$luku)).${tulo: -$desimaalejakaikkiaan:8} ;} # $((10#$luku)) poistaa kokonaisosasta etunollat


function lukujenvertailu () {
luku1=$1
luku2=$2
[[ ${luku1//[^.]/} ]] || luku1=$luku1".0"
[[ ${luku2//[^.]/} ]] || luku2=$luku2".0"
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
luku1=$kokonaisosa1$desimaaliosa1
luku2=$kokonaisosa2$desimaaliosa2
(( $luku1 >= $luku2 )) && echo 0 || echo 1 ;} 


function kahdellajako () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
luku2=2.0
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}
luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
echo $kokonaiset.${desimaalit: -9} ;}


function yhteenlasku () { # voi käyttää vähennyslaskuunkin
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=$1
luku2=$2
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
(( $m2 +1 )) && luku=$((10#$kokoluku1+10#$kokoluku2)) || luku=$((10#$kokoluku1-10#$kokoluku2))
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}


time { juurrettava=3 # tämä on binäärihakua ohjaava pääohjelma
y=$(kahdellajako $juurrettava)
delta=$(kahdellajako $y)
for ((i=0;i<30;i++)); do
  x=$( y*y $y)
  (( $(lukujenvertailu $juurrettava $x) )) && y=$(yhteenlasku $y -$delta) || y=$(yhteenlasku $y $delta)
  delta=$(kahdellajako $delta)
done
echo $y ;}

***

Virtuoositkin haluavat korvauksen ponnisteluistaan eivätkä siten kerro saavutuksistaan julkisesti. Mutta eivät malta olla vihjailematta.

Esimerkiksi aikoinaan luin erään virtuoosin kirjoituksia ja niissä oli ohje: ei pidä käyttää sed:iä tai awk:ia ellei se ole ihan välttämätöntä sillä ne hidastavat erittäin paljon. Ihmettelin kirjoitusta silloin sillä sed ja awk ne vasta nopeita ovatkin. Kuinka päteväntuntuinen virtuoosi voi tuommoisia kirjoittaa?

Mutta desimaalilaskenta opetti että virtuoosi puhui aivan totta sillä yksikin awk tai sed laskee nopeuden noin kymmenenteen osaan - kyse on siitä muutaman millisekunnin ajasta joka kuluu sed:iä tai awk:ia kutsuttaessa ja jos niille antaa vain jonkun pienen tehtävän niin BASH itse olisi suorittanut tehtävän paljon nopeammin.

Esimerkiksi BASH:ista löytyy käsky sille kuinka sana vaihdetaan toiseen elikä siihen hommaan johon sed:iä melkein aina käytetään - tosin BASH:in käsky tunnistaa vain yksinkertaiset regex:ät. Samoin moniin awk:in yksinkertaisiin tehtäviin löytyy BASH:ista vastaava toiminta.

Kuljenko siis desimaalilaskennassakin toisten jalanjäljillä?

*** 

Uudet versiot desimaalilukujen tuplaamiseen ja puolittamiseen - ja niille yksi testaus:
Koodia: [Valitse]

function tuplaa () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku <<=1";  apu=${luku: -$desimaaliosanpituus:$desimaaliosanpituus-1}; (( $apu )) && echo ${luku:0: -$desimaaliosanpituus}.$apu || echo ${luku:0: -$desimaaliosanpituus} ;}

function puolita () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku >>=1"; echo ${luku:0: -$desimaaliosanpituus}.${luku: -$desimaaliosanpituus:desimaaliosanpituus-$((${luku: -1} == 0)) } ;} 

# Tässä se testaus:
puolita $(tuplaa 555.555) # arvo 555.555 ja sen tupla elikä 1111.110 ovat muunnoksille haasteellisia. Tarkoituksena on myös selvittää tuleeko ulos sama luku kuin meni sisään.

***

Koskei BASH desimaalilukuja tunne niin desimaalilaskenta suoritetaan BASH:in matematiikkaoperaatioilla tuo desimaalipiste poistettuna. Siitä seuraa nopeus mutta myös se että luvuissa saa olla korkeintaan 19 numeroa laskematta mukaan tuota desimaalipistettä - myös laskujen tuloksen pitää olla 19 numeroa pienempi.

Desimaalilaskennan skriptit ovatkin liian nopeita jotta niiden suoritusajan saisi mitattua. Esimerkiksi uusien versioiden desimaaliluvun tuplaamiseen tai puolittamiseen pitäisi olla nopeampia kuin edellisten versioiden. Mutta kuinka todeta se selvin numeroin? Nimittäin time-käsky antaa nopeudeksi yleensä nollan ja harvemmin 0.001 sekuntia. Keskiarvo monesta mitauksesta on arviolta jotain .2ms - varsin epämääräistä. Tarkempi arvo saadaan näin:

- ensin mitataan pohja-aika: time { for n in {1..1000}; do : ; read -t .01 ; done ;}
joka on noin 10.300
- sitten mitataan ohjelman suoritusaika: time { for n in {1..1000}; do tuplaa 1 ; read -t .01 ; done ;}
joka on noin 10.750. Joten kokonaisaika on noin 0.450ms
- kokonaisaika on: (aika2-aika1)/1000
- onko aika sittenkään yhtään tarkempi on vain arvattavissa - ehkä se on moninumeroista potaskaa.
- käsky: read -t 1 on tarkka 1 sekunnin viive. Tarkkuus lienee mikrosekunteja.
- käsky : on käsky olla tekemättä mitään joten ei se oikein mitään kestäkään.
- ilmeisesti enin osa ajasta matematiikalaskennan skripteissä kuluu tulkin kutsumiseen sillä sillä ei tunnu olevan suurtakaan väliä montako lausetta niissä on. Siis ilmeisesti isonkin ohjelman suorittaminen olisi nopeaa jos sen lauseet kirjoittaa tulkille mieleisiksi.

tässä yhteydessä on syytä kertoa mitä time-käskyn real, user ja system ajat ovat:
- real on ohjelman käyttäjätilassa (user) viettämä aika ja siinä on lisänä kaikki ne ajat jotka prosessi joutuu odottamaan isokenkäisempien prosessien tunkiessa väliin. Tämä on se aika jonka käyttäjän kannalta on kuluu.
- user on se aika joka kuluu käyttäjätilassa työskentelyyn laskematta mukaan odotusaikoja.
- system on se aika joka kuluu systeemipalveluissa.
- ja kun soppaan lisätään linuxin kyky multiprosessointiin (system-tilassa) niin varsinkin nopeilla skripteillä user+system voi olla suurempi kuin real.

- oikeintehdyllä skriptillä real-aika on mahdollisimman pieni, user-aika siitä vain kotuullisesti pienempi ja system-aikakin jotain vähän suurempaa kuin 0. Eri suorituskerroilla arvot saattavat vaihdella paljonkin.

- käyttöjärjestelmä myöntää jokaiselle prosessille aikaviipaleita ja päättää myös koska niitä myönnetään. Käyttöjärjestelmä voi kyllä keskeyttää jo alkaneen prosessin mutta niin ei tapahdu usein vaan odotusajat ovat niitä aikoja kun toiset tunkee väliin. Jos siis aika on tarpeeksi lyhyt niin siihen tuskin kukaan kerkiää väliin ja silloin real-aika on pieni ja user-aika siitä vain vähän pienempi.

*** 

Kertolasku jossa sekä kertoja että kerrottava voivat olla 16-numeroisia kokonaislukuja ja tulos 32 numeroinen.
 
Desimaalilaskenta on nopeaa niinkauan kuin tyydytään toimimaan alle 19-numeroisilla luvuilla. Myös kaksinkertaisen numeromäärän desimaalisummain on nopea. Mutta "kaksinkertaisen numeromäärän" kertolasku kestää 8ms sillä sen täytyy käyttää neljää normaalia kertolaskua ja kolmea kaksinkertaisen numeromäärän yhteenlaskua.

Ei etumerkin käsittelyä eikä desimaalipistettä ole toteutettu vaikka se ei olisi vaikeaa. Sillä tämä on liian hidas yleisempään käyttöön mutta sopii desimaalilaskennan testaamiseen.
Koodia: [Valitse]
function summaa36 () { # kokonaislukujen summain kaksinkertaisella numeromäärällä
luku1pituus=${#1}
luku2pituus=${#2}
luku1=00000000000000000000000000000000000000$1
luku2=00000000000000000000000000000000000000$2
luku1=${luku1: -36} # echo $luku1
luku2=${luku2: -36} # echo $luku2
luku11=${luku1:0:18}
luku12=${luku1:18}; luku12=${luku11//[^-]/}$luku12 # echo a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; luku22=${luku21//[^-]/}$luku22 # echo c$luku21' 'd$luku22
tokaluku=$((10#$luku12+10#$luku22))  # Kun käy niin että tokaluku vuotaa yli lisätään ekalukuun 1 ja #leikataan tokaluvusta ensimmäinen merkki pois (se on se ylivuotonumero ja sen arvo on silloin 1)
((10#${#tokaluku} > 10#${#luku12} || 10#${#tokaluku} > 10#${#luku22} )) && ylivuoto=1 || ylivuoto=0
(( $ylivuoto )) && tokaluku=${tokaluku:1} # echo $ylivuoto
[[ ${luku11//[^-]/} == - && ${luku21//[^-]/} == - ]] && ekaluku=$((10#$luku11+10#$luku21-$ylivuoto)) ||ekaluku=$((10#$luku11+10#$luku21+$ylivuoto))
(( $ekaluku )) && echo $ekaluku$tokaluku || echo $tokaluku ;}


function kerro16 () { # kertolasku kun jäsenet ovat 16 numeroisia kokonaislukuja.
luku1=000000000000000000$1
luku2=000000000000000000$2
a=${luku1: -16:8}; b=${luku1: -8}
c=${luku2: -16:8}; d=${luku2: -8} # echo $a' '$b; read # echo $c' '$d
ala=$((10#$b*10#$d))
keski1=$((10#$d*10#$a))"00000000"
keski2=$((10#$c*10#$b))"00000000"
yla=$((10#$a*10#$c))"0000000000000000"
summa=$(summaa36 $ala $keski1)
summa=$(summaa36 $summa $keski2)
summa=$(summaa36 $summa $yla)
echo $summa ;}

kerro16 1111111122222222 3333333344444444  # tulokseksi pitää tulla 3703703753086418641975301234568

***

Rekursio on ohjelmointirakenne jossa funktio kutsuu itseään.

BASH:issa rekursio on hidas joten sitä ei kannata käyttää kuin joissain erikoistapauksissa. Teoria on kuitenkin hyvä tuntea.

Kullakin rekursiokierroksella kutsun parametrit ovat kutsun omia - siis muuttuja $1 viittaa jokaisella kierroksella senkertaisen kutsun $1:een.

BASH-tulkki pitää kirjaa funktiokutsuista matriisissa nimeltä: FUNKNAME.

Koodia: [Valitse]
#!/bin/bash
function kertoma () {
if [ "$1" -gt "1" ] # rekursiossa pitää ehdottomasti olla relkursion lopetusehto.
then
i=$(expr $1 - 1)
j=$(kertoma $i)
kertoma= echo "$1*$j" | bc | tr -d '\\\n'
echo $kertoma   
else
echo 1
fi
}
read -p 'mistä luvusta se kertoma lasketaan: ' x
kertoma $x

vertaapa nopeutta otettaessa kertoma luvusta 500: echo 500 | dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp' | tr -d '\\\n'

***

Murheellista tuo BASH:in kirjastojen vieroksuminen, sillä funktion tekeminen melkein mihinvaan on suorastaan rikollisen yksinkertaista - nimittäin tehdä ne tavalla joka on kymmeniäkertoja turhan hidas, virheellinen, sisältää turhia rajoituksia ja henkilökohtaisia luuloja. Joten nuo pienet funktiot ovat koneen kanssa taistelua mikä ei tosiaan kuulu yhtään mihinkään ja masentaa vain. Niinpä BASH kaipaisi ehdottomasti kirjastoja - tai siis että niitä kirjastoja olisi kirjoitettu ja että niitä käytettäisiin.

Kaikenlaista  kirjastokamaa löytyy eikä erkkikään muista edes näitä yksinkertaisia. Koska funktioita ei haeta kirjastosta niin tapahtuu seuraavaa: teet skriptiä ja kun saat sen toimimaan menet ja esittelet ylpeänä sen kavereillesi - jolloin se kaatuu heti alussa sillä jokaisessa vähänkin suuremmassa funktiossa on montakin salahautaa -> pienikin muutos dataan ja heti kosahtaa.
Koodia: [Valitse]
function int () { echo ${1%%.*} ;}

function fract () { desimaaliosa=${1##*[,.]}; (( ${#desimaaliosa} == ${#1} )) && echo 0 || echo $desimaaliosa ;}

function abs () { echo ${1//-/} ;}
 
function floor () { [[ ${1//[^-]/} ]] && m1=-1 || m1=0; kokonaisosa=${1%%[,.]*}; echo $(($kokonaisosa+(1*$m1))) ;}

function ceil () { [[ ${1//[^-]/} ]] && m1=0 || m1=-1; kokonaisosa=${1%%[,.]*}; echo $(($kokonaisosa-(1*$m1))) ;}

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.09.19 - klo:15.31
BASH:ista haastellaan potaskaa kaikkialla: osoitus siitä että BASH hallitsee ohjelmarakenteen nimeltään: first-class function
Koodia: [Valitse]
function koe () { local numero; $1 $2 numero; echo $numero ;}   
function joo () { readarray $1 < <(echo 12345) ;}
a=joo
koe $a

echo $numero # numerolla on arvo täälläkin vaikka se onkin määritelty local:iksi funktiossa: "koe"
#                      sillä se on funktiossa: "joo" tehty globaaliksi

***

Anonymous function on funktio jolla ei ole nimeä. Tarkastiottaen se ei ole totta, sillä anonymous funktiolla  on muuttujan nimi. Siis tässä tapauksessa tuo a - kaavassa: a=x**2+x-1
Koodia: [Valitse]
function koe () { apu=($@); for n in $(seq 1 $((${#@}-1))); do x=${apu[$n]}; echo $(( $1 )) ; done ;}; mat=(1 2 3);a=(x**2+x-1); koe $a ${mat[@]}

- tuon matriisin palautus sotkisi muutenkin hankalasti ymmärrettävää joten se jäi pois

***

Missään näissä desimaalimaematiikan laskuista ei ole käytetty bc:tä tai jotain muuta matematiikkaohjenlmaa sillä näillä pinillä hommilla ne hidastavat..

Neliöjuuri luvuista 1.0000001-99.999999. Välin ulkopuolisiin lukuihin pätee: "kerro tai jaa sadalla kunnes pääset tälle välille. Pilkku siirtyy aina yhdellä.". Desimaaleja tuloksessa on 8. Aikaa kuluu noin 2ms.
Koodia: [Valitse]
function neliöjuuri () {
juurrettava=${1//./}"000000000000000000"
juurrettava=${juurrettava:0:17}
y=2110000000
delta=1005000000
case ${1%%.*} in
    [1-9])    { for ((i=0;i<31;i++)); do
                  x=$(($y*$y))
                  (( $x>=$juurrettava )) && y=$(($y-$delta)) || y=$(($y+$delta))
                  delta=$(($delta/2))
                done ;} ;;
    [10-99]*) { for ((i=0;i<31;i++)); do
                x=$(($y*$y/10))
                 (( $x>=$juurrettava )) && y=$(($y-$delta)) || y=$(($y+$delta))
                 delta=$(($delta/2))
               done ;} ;;
    *) echo "kerro tai jaa sadalla kunnes pääset välille 1-100. Pilkku siirtyy aina yhdellä." ;;
esac
echo ${y:0:1}.${y:1} ;}

time neliöjuuri 2.43

***

BASH:in matematiikka toimii noin 50 mikrosekunnissa mutta se toimii vain kokonaisluvuilla. Mutta itseasiassa kaikki matematiikka tietokoneissa on kokonaislukumatematiikkaa, liukulukulaskut vain pitävät kirjaa mihin desimaalipiste kuuluu - mutta tämä kirjanpitokin voidaan tehdä joko vaikeasti tai helposti,  ja se helpompitapa on että käsiteltävien desimaaliosat muodostetaan yhtäpitkiksi lisäämällä lyhyempään desimaaliosaan riitävästi peränollia. BASH:ista tämä kirjanpito puuttuu ja se on itse lisättävä. Ja koska kirjanpito on toteutettava korkeatason käskyillä hidastaa se melkolailla. Desimaalimatematiikan laskujen nopeus onkin BASH:issa luokkaa 0.2-0.5ms.

Ja näiden desimaalilaskujen numeromäärä sillon kun haetaan suurta nopeutta rajoittuu kokonaislukulaskujen 19 numeroon. Tämä on usein täysin epätyydyttävä merkkimäärä.

***

Desimaalimatematiikan peruskiviin kuuluu desimaalilukujen vertaaminen. Silloin kun joudutaan vertailemaan lukuja joissa saattaa olla yli 19 numeroa kannattaa käyttää tekstijono-vertailua sillä salahautojen väistelemisestä huolimatta se on yhtänopea kuin aritmeettinen desimaalivertain. Eikä merkkimäärällä ole rajoituksia tekstijonovertailussa. Myös kaikki lukujärjestelmät toimivat haxadesimaalit mukaanlaskien.

Tekstijonovertailuahan on käytetty aina sillä se on erinomainen numeroitakin vertailtaessa. Mutta noinvain käytettynä se höpertää monessa tilanteessa. Seuraavilla korjauksilla siitä tulee kelvollinen:

1. verrattavien lukujen etumerkit täytyy erottaa omiin muutujiinsa ja poistaa senjälkeen.
2. luvuissa täytyy aluksi olla desimaalipiste ja jos ei ole niin sellainen liitetään lukujen loppuun.
3. desimaaaliosista täytyy tehdä yhtäpitkät, elikä katkaista pitempi desimaaliosa lyhyemmän mittaiseksi.
4. lopulliset verrattavat ovat KokonaisosaDesimaaliosa; pelkkiä numeroita ilman desimaalipistettä
5. varsinainen suuruuden testaaminen täytyy suorittaa eritavoin eri etumerkkikombinaatioilla.

Ja näiden korjausten jälkeen se on hidas ellei sitä kirjoita oikein:
Koodia: [Valitse]
function onkoekasuurempi () { # rajoittamaton numeromäärä kokonais- ja desimaaliosissa
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
luku1=${1//[-+]/}; luku2=${2//[-+]/}    # etumerkki pois, myös +.
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1"." # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2"." # joten lisätään sellaien mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
kokonaisosa1=${1%%.*}; kokonaisosa2=${2%%.*}
(( ${#desimaaliosa2} <= ${#desimaaliosa1} )) && { apu=$desimaaliosa1; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
luku1=$kokonaisosa1$desimaaliosa1; luku2=$kokonaisosa2$desimaaliosa2
case $m1$m2 in 
+- )  echo 1 && return ;;
-+ )  echo 0 && return ;;
++ )  [[ $luku1 > $luku2 ]] && echo 1 || echo 0 && return ;;
-- )  [[ $luku1 > $luku2 ]] && echo 0 || echo 1 ;;
esac ;}

time onkoekasuurempi 11111.2 11111.19999 # kestää 1ms

***

Lukujen yhtäsuuruus neuvotaan toteamaan näin:

((10#$a == 10#$b)) && <tehdään jotakin>

Tuo 10# poistaa luvuista etunollat joten vertailu selviää "oktaaliansasta" mutta useimmat neuvojat jättävät tuon tarpeellisen osan neuvostaan pois. Jokatapauksessa esitetyllä tavalla yhtäsuuruutta testattaessa testattavien numeromäärä on rajoitettu siihen noin 19 numeroon joka on BASH:in matematiikassa suurin hyväksytty numero eikä se riitä alkuunkaan sillä tottakai BASH joutuu tekemisiin ulkomaailman kanssa ja sietähän tulee 64 numeroisia lukuja ja suurempiakin.

Mutta yhtäsuuruutta ei kannatakaan testata matematiikkamoottorissa vaan käyttää tekstijonovertailua sillä se on joustavinta ja nopeinta. Tekstijonovertailussa on lukujen yhtäsuuruuden vertailussa vain vähän asioita jotka voisivat viedä harhaan:
1. etunolla
2. + merkki
sillä kumpikaan ei vaikuta luvun numeeriseen arvoon mutta tekstijonoarvoon kyllä joten ne molemmat poistetaan. Mutta sensijaan kaikki lukujärjestelmät käyvät ja vertailtavissa saa olla mitähyvänsä vakiotekstiäkin.

Asiasta on viisainta tehdä funktio:
Koodia: [Valitse]
function yhtäsuuruustesti () {
luku1=${1//+/}; [[ ${luku1:0:1} == 0 ]] && luku1=${luku1:1} 
luku2=${2//+/}; [[ ${luku2:0:1} == 0 ]] && luku2=${luku2:1}
[[ $luku1 == $luku2 ]] && echo "arvoltaan luvut ovat samoja" || echo "luvut eivät ole samoja" ;}
# ja sen testaus:
a=1234567890123456789012345678901234567890.1234567890123456789012345678901234567890; b=+01234567890123456789012345678901234567890.1234567890123456789012345678901234567890;
yhtäsuuruustesti $a $b

***

Aikoinaan BASH:issa harrastetiin muotoiluohjelmia tulosteiden muotoilemiseen - mutta muotoiluohjelma kykenee muotoilemaan vain koko sivun eikä yksittäistä numeroa. Siitä on menty toiseen äärimmäisyyteen ja muotoillaan ainoastaan yksittäisiä numeroita ja tekstikenttiä ja oletetaan että sehän johtaa siihen että koko sivukin on muotoiltu.

Mutta numeron tulostamiseen ei ole keinoa joka ei joskus pursuisi yli ja silloin joko tulosteen ulkoasu tai numero kärsii. Tieteellinen esitysmuotokin on vain osaratkaisu. Printf tekee hyvää työtä, mutta jotkin asiat ovat sillekin liian vaikeita.

Esimerkiksi lukua tulostettaessa desimaalit on pakko katkaista. Mutta nitä ei saa raa-asti katkaista vaan pyöristää ottaen huomioon mitä numeroita on katkaisukohdan perässä.

printf pyöristäisikin muotoilemisen ohella. Mutta pyöristettäessä sen toiminta tökkii koska laskuissa on usein desimaalipisteenä piste ja Suomalaisen koodisivun desimaalipiste on pilkku. Senvuoksi kun yrittää pyöristää käyttäen printf-käskyä se välillä epäonnistuu. Printf:ää pitää käskeä omalla tavallaan sekä pisteelle että pilkulle.

Kunnolliseen muotoilemiseen tarvitaankin sekä numeroiden muotoilemista tulostettaessa että muotoiluohjelmaa - ja nuo jo unohdetut BASH:in muotoiluohjelmat kyllä toimivat edelleen. "paras" muotoiluohjelma on column -t ja se putkitetaan tarvittaessa tulostukseen. Eiväthän nuo muotoiluohjelmat nykyisiä vaatimuksia täytä - mutta silti niistä on joskus hyötyä. Muotoiluohjelman sijoitaminen oikeaan kohtaan ei ole aina helppoa.

Seuraava tulostusfunktio ei muuta tulostettavan luvun kokonaiosaa mutta pyöristää desimaaliosan niinkuin halutaan - ja sille kelpaa sekä piste että pilkku. Se on lisäksi nopeampikin kuin printf: 
Koodia: [Valitse]
function round_in_decimals () { (( $# == 2 )) && decimals=$2 || decimals=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $decimals == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} >= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$decimals}+$(( ${desimaaliosa:$decimals+1} >= 50)) )) ;}
# kutsu:
round_in_decimals 1.51515773 2  # tulostaa 1.52

# lisätestaaminen:
round_in_decimals .5   # tämän pitää tulostaa 0 # parametria ei tarvitse kirjoittaa kun sen arvo on 0
round_in_decimals .51  # tämän pitää tulostaa 1
round_in_decimals 1.51 # tämän pitää tulostaa 2

Muut viritelmät lukujen tulostamiseksi alkavat nopeudeltaa yleensä 4ms nurkilta - mutta ei voi ihan yksiselitteisesti tuomita niitä huonoiksi sillä esimerkiksi sovellukset jotka käyttävät bc:tä voivat samalla tehdä matemaattisia ihmetekoja.

***

Se että tuo 6-rivinen funktio jossa on monimutkaista toimintaakin voi olla nopeampi kuin prinf-käsky sai jälleen kerran ajattelemaan legendaa BASH:in hitaudesta.

Nopeudessa taitaa olla kysymys seuraavasta: BASH:issa on nopeudeltaan kahdentyyppisiä käskyjä - siitä toki manuaaleissa ohimennen kerrotaan muttei sitä että niiden nopeudet eroavat monikymmenkertaisesti:
 
1. sellaisia käskyjä joiden koodi on BASH:iin liitetty. Tällaiset käskyt ovat tosinopeita - no nopeita BASH-yksiköissä. Näitä saa olla viitisenkymmentäkin peräkkäin ennenkuin suoritus kestää millisekunnin.

2. sellaisia käskyjä jotka täytyy tulkita ennen linkittämistä. Nämä ovat niitä hitaita joista useimmat kestävät luokkaa millisekunnin.

Noissa nopeissa käskyissä on paljon vaikeasti muistettavaa digisotkua jonka kirjoittaminen vaatii lisäksi näppäin-akrobatiaa - ja koska noiden käskyjen nopeudesta ei puhuta niin niitä ei opetella käyttämään vaan totutaan käyttämään yksinomaan helppoja ja hitaita ulkoisia käskyjä - jotka lisäksi kaipaavat apua sed:iltä ja awk:ilta jotka ovat pikkuhommissa vielä hitaampia.

Koskei ole täysin tajuttu että nopeitakin käskyjä löytyy niin on luultu ettei BASH:ista ole ollenkaan numeronmurskaamiseen ilman bc:n apua. Tätä on vahvistanut luulo ettei BASH kykene desimaalilaskentaan. Lisäksi luullaan että matematiikan 19:sta merkkin lukumäärää ei voisi mitenkään lisätä. Mutta pienissä "nelilaskin" laskuissa desimaaliluvuillakin BASH on neljäkertaa nopeampi kuin bc.

BASH:in vadtustajia täytyy ihailla: väitetään hieman väärin että BASH ei osaa palauttaa funktioparametreja, eval-käskyn tuomitseminen - mutta vain BASH:issa, kirjasto-osoittimen poistaminen, väärät väitteet ensimmäisenluokan funktioita ja niinpoispäin. Mikähyvänsä näistä riittää tuhoamaan melkein mitavaan. BASH halutaan tuhota.   

Toki BASH:issa on paljon ihan oikeaakin kritisoitavaa. Ja jotakin täytyisi tehdä jotta sen kritisoitavan voisi skripteissään tehdä oikealla tavalla - esimerkiksi palauttaa kirjasto-osoitin ja koota muutama kirjasto.

Sehän selvisi jo aikoinaan että sed, awk ja bc täytyy unohtaa kun halutaan nopeutta yksinkertaisiin tehtäviin. Mutta se täytyi yrittää selvittää mitä kaikkea muuta kuin BASH:in nopeita käskyjä voi nopeaan skriptiin ottaa mukaan: ilmeisesti ainakin funktiot ja looppit ovat nopeita - täytyy korjata pahaa puhettani looppien hitaudesta: eivät loopit ole hitaita vaan se minkä ne suorittavat.

Käytännön skripteistä näkee parhaiten mikä kannattaa:

Ensin rajoittamattoman monen rajoittamattoman moninumeroisten lukujen yhteenlaskin jossa on pari funktiota ja neljä looppia. Tätä ei ole suoranaisesti tarkoitettu käytettäväksi vaan se on tehty nopeiden käskyjen löytämiseksi. Lopussa kuvatulla laskulla skriptin läpikäyminen vaatii noin 150:n monipuolisen käskyn suorittamisen elikä noin 20 mikrosekuntia per käsky -> siis kaikki sen käskyt ovat nopeita.
Koodia: [Valitse]
function radd () { 

function desimaalisumma () { for((j=1;j<=$2;j++)); do summa=$((summa+${desimaaliosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0;summa=$((summa+$muistinumero)); muistinumero=0 ;}

function kokonaissumma  () { for((j=1;j<=$2;j++)); do summa=$((summa+${kokonaisosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0; summa=$((summa+$muistinumero)); muistinumero=0 ;}

i=1; maxdesimaalit=0; maxkokonaiset=0; muistinumero=0; for n in $@; do desimaaliosa[i]=${n##*.}; desimaaliosanpituus[i]=${#desimaaliosa[i]}; (( ${desimaaliosanpituus[i]} >= $maxdesimaalit )) && maxdesimaalit=${desimaaliosanpituus[i]}; kokonaisosa[i]=${n%%.*};  kokonaisosanpituus[i]=${#kokonaisosa[i]}; (( ${kokonaisosanpituus[i]} >= $maxkokonaiset )) && maxkokonaiset=${kokonaisosanpituus[i]}; (( i++ )); done

i=1; for n in $@; do desimaaliosa[i]=${n##*.}"000000000000000000"; desimaaliosa[i]=${desimaaliosa[i]:0:$maxdesimaalit};
kokonaisosa[i]="000000000000"${kokonaisosa[i]}; kokonaisosa[i]=${kokonaisosa[i]: -$maxkokonaiset};
echo ${kokonaisosa[i]}.${desimaaliosa[i]}; (( i++ )); done 

desimaalit=''; for((i=maxdesimaalit-1;i>=0;i--)); do summa=0; desimaalisumma i $# ; desimaalit=${summa: -1}$desimaalit; muistinumero=${summa:0: -1}; done; # echo $desimaalit' '$muistinumero; read

kokonaiset=''; for((i=maxkokonaiset-1;i>=0;i--)); do summa=0; kokonaissumma i $# ; kokonaiset=${summa: -1}$kokonaiset; muistinumero=${summa:0: -1}; done; echo $muistinumero$kokonaiset.$desimaalit
}

time radd 1.17 22.222 3.33 4.4




Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.10.19 - klo:04.13
BASH:ista haastellaan potaskaa kaikkialla: osoitus siitä että BASH hallitsee ohjelmarakenteen nimeltään: first-class function
Koodia: [Valitse]
function koe () { local numero; $1 $2 numero; echo $numero ;}   
function joo () { readarray $1 < <(echo 12345) ;}
a=joo
koe $a

echo $numero # numerolla on arvo täälläkin vaikka se onkin määritelty local:iksi funktiossa: "koe"
#                      sillä se on funktiossa: "joo" tehty globaaliksi

***

Anonymous function on funktio jolla ei ole nimeä. Tarkastiottaen se ei ole totta, sillä anonymous funktiolla  on muuttujan nimi. Siis tässä tapauksessa tuo a - kaavassa: a=x**2+x-1
Koodia: [Valitse]
function koe () { apu=($@); for n in $(seq 1 $((${#@}-1))); do x=${apu[$n]}; echo $(( $1 )) ; done ;}; mat=(1 2 3);a=(x**2+x-1); koe $a ${mat[@]}

- tuon matriisin palautus sotkisi muutenkin hankalasti ymmärrettävää joten se jäi pois

***

Missään näissä desimaalimaematiikan laskuista ei ole käytetty bc:tä tai jotain muuta matematiikkaohjenlmaa sillä näillä pinillä hommilla ne hidastavat..

Neliöjuuri luvuista 1.0000001-99.999999. Välin ulkopuolisiin lukuihin pätee: "kerro tai jaa sadalla kunnes pääset tälle välille. Pilkku siirtyy aina yhdellä.". Desimaaleja tuloksessa on 8. Aikaa kuluu noin 2ms.
Koodia: [Valitse]
function neliöjuuri () {
juurrettava=${1//./}"000000000000000000"
juurrettava=${juurrettava:0:17}
y=2110000000
delta=1005000000
case ${1%%.*} in
    [1-9])    { for ((i=0;i<31;i++)); do
                  x=$(($y*$y))
                  (( $x>=$juurrettava )) && y=$(($y-$delta)) || y=$(($y+$delta))
                  delta=$(($delta/2))
                done ;} ;;
    [10-99]*) { for ((i=0;i<31;i++)); do
                x=$(($y*$y/10))
                 (( $x>=$juurrettava )) && y=$(($y-$delta)) || y=$(($y+$delta))
                 delta=$(($delta/2))
               done ;} ;;
    *) echo "kerro tai jaa sadalla kunnes pääset välille 1-100. Pilkku siirtyy aina yhdellä." ;;
esac
echo ${y:0:1}.${y:1} ;}

time neliöjuuri 2.43

***

BASH:in matematiikka toimii noin 50 mikrosekunnissa mutta se toimii vain kokonaisluvuilla. Mutta itseasiassa kaikki matematiikka tietokoneissa on kokonaislukumatematiikkaa, liukulukulaskut vain pitävät kirjaa mihin se desimaalipiste kuuluu. BASH:ista tämä kirjanpito puuttuu ja se on itse lisättävä. Ja koska kirjanpito on toteutettava korkeatason käskyillä hidastaa se melkolailla. Desimaalimatematiikan laskujen nopeus onkin BASH:issa luokkaa 0.2-0.5ms.

Ja näiden desimaalilaskujen numeromäärä sillon kun haetaan suurta nopeutta rajoittuu kokonaislukulaskujen 19 numeroon. Tämä on usein täysin epätyydyttävä merkkimäärä.

***

Desimaalimatematiikan peruskiviin kuuluu desimaalilukujen vertaaminen. Silloin kun joudutaan vertailemaan lukuja joissa saattaa olla yli 19 numeroa kannattaa käyttää tekstijono-vertailua sillä salahautojen väistelemisestä huolimatta se on yhtänopea kuin aritmeettinen desimaalivertain. Eikä merkkimäärällä ole rajoituksia tekstijonovertailussa. Myös kaikki lukujärjestelmät toimivat haxadesimaalit mukaanlaskien.

Tekstijonovertailuahan on käytetty aina sillä se on erinomainen numeroitakin vertailtaessa. Mutta noinvain käytettynä se höpertää monessa tilanteessa. Seuraavilla korjauksilla siitä tulee kelvollinen:

1. verrattavien lukujen etumerkit täytyy erottaa omiin muutujiinsa ja poistaa senjälkeen.
2. luvuissa täytyy aluksi olla desimaalipiste ja jos ei ole niin sellainen liitetään lukujen loppuun.
3. desimaaaliosista täytyy tehdä yhtäpitkät, elikä katkaista pitempi desimaaliosa lyhyemmän mittaiseksi.
4. lopulliset verrattavat ovat KokonaisosaDesimaaliosa; pelkkiä numeroita ilman desimaalipistettä
5. varsinainen suuruuden testaaminen täytyy suorittaa eritavoin eri etumerkkikombinaatioilla.

Ja näiden korjausten jälkeen se on hidas ellei sitä kirjoita oikein:
Koodia: [Valitse]
function onkoekasuurempi () { # rajoittamaton numeromäärä kokonais- ja desimaaliosissa
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
luku1=${1//[-+]/}; luku2=${2//[-+]/}    # etumerkki pois, myös +.
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1"." # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2"." # joten lisätään sellaien mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
kokonaisosa1=${1%%.*}; kokonaisosa2=${2%%.*}
(( ${#desimaaliosa2} <= ${#desimaaliosa1} )) && { apu=$desimaaliosa1; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
luku1=$kokonaisosa1$desimaaliosa1; luku2=$kokonaisosa2$desimaaliosa2
case $m1$m2 in 
+- )  echo 1 && return ;;
-+ )  echo 0 && return ;;
++ )  [[ $luku1 > $luku2 ]] && echo 1 || echo 0 && return ;;
-- )  [[ $luku1 > $luku2 ]] && echo 0 || echo 1 ;;
esac ;}

time onkoekasuurempi 11111.2 11111.19999 # kestää 1ms

***

Lukujen yhtäsuuruus neuvotaan toteamaan näin:

((10#$a == 10#$b)) && <tehdään jotakin>

Tuo 10# poistaa luvuista etunollat joten vertailu selviää "oktaaliansasta" mutta useimmat neuvojat jättävät tuon tarpeellisen osan neuvostaan pois. Jokatapauksessa esitetyllä tavalla yhtäsuuruutta testattaessa testattavien numeromäärä on rajoitettu siihen noin 19 numeroon joka on BASH:in matematiikassa suurin hyväksytty numero eikä se riitä alkuunkaan sillä tottakai BASH joutuu tekemisiin ulkomaailman kanssa ja sietähän tulee 64 numeroisia lukuja ja suurempiakin.

Mutta yhtäsuuruutta ei kannatakaan testata matematiikkamoottorissa vaan käyttää tekstijonovertailua sillä se on joustavinta ja nopeinta. Tekstijonovertailussa on lukujen yhtäsuuruuden vertailussa vain vähän asioita jotka voisivat viedä harhaan:
1. etunolla
2. + merkki
sillä kumpikaan ei vaikuta luvun numeeriseen arvoon mutta tekstijonoarvoon kyllä joten ne molemmat poistetaan. Mutta sensijaan kaikki lukujärjestelmät käyvät ja vertailtavissa saa olla mitähyvänsä vakiotekstiäkin. Desimaalipistekään ei ole ongelma.

Asiasta on viisainta tehdä funktio:
Koodia: [Valitse]
function yhtäsuuruustesti () {
luku1=${1//+/}; [[ ${luku1:0:1} == 0 ]] && luku1=${luku1:1} 
luku2=${2//+/}; [[ ${luku2:0:1} == 0 ]] && luku2=${luku2:1}
[[ $luku1 == $luku2 ]] && echo "arvoltaan luvut ovat samoja" || echo "luvut eivät ole samoja" ;}
# ja sen testaus:
a=1234567890123456789012345678901234567890.1234567890123456789012345678901234567890; b=+01234567890123456789012345678901234567890.1234567890123456789012345678901234567890;
yhtäsuuruustesti $a $b

***

Aikoinaan BASH:issa harrastetiin muotoiluohjelmia tulosteiden muotoilemiseen - mutta muotoiluohjelma kykenee muotoilemaan vain koko sivun eikä yksittäistä numeroa. Siitä on menty toiseen äärimmäisyyteen ja muotoillaan ainoastaan yksittäisiä numeroita ja tekstikenttiä ja oletetaan että sehän johtaa siihen että koko sivukin on muotoiltu.

Mutta numeron tulostamiseen ei ole keinoa joka ei joskus pursuisi yli ja silloin joko tulosteen ulkoasu tai numero kärsii. Tieteellinen esitysmuotokin on vain osaratkaisu. Printf tekee hyvää työtä, mutta jotkin asiat ovat sillekin liian vaikeita.

Esimerkiksi lukua tulostettaessa desimaalit on pakko katkaista. Mutta nitä ei saa raa-asti katkaista vaan pyöristää ottaen huomioon mitä numeroita on katkaisukohdan perässä.

printf pyöristäisikin muotoilemisen ohella. Mutta pyöristettäessä sen toiminta tökkii koska laskuissa on usein desimaalipisteenä piste ja Suomalaisen koodisivun desimaalipiste on pilkku. Senvuoksi kun yrittää pyöristää käyttäen printf-käskyä se välillä epäonnistuu. Printf:ää pitää käskeä omalla tavallaan sekä pisteelle että pilkulle.

Kunnolliseen muotoilemiseen tarvitaankin sekä numeroiden muotoilemista tulostettaessa että muotoiluohjelmaa - ja nuo jo unohdetut BASH:in muotoiluohjelmat kyllä toimivat edelleen. "paras" muotoiluohjelma on column -t ja se putkitetaan tarvittaessa tulostukseen. Eiväthän nuo muotoiluohjelmat nykyisiä vaatimuksia täytä - mutta silti niistä on joskus hyötyä. Muotoiluohjelman sijoitaminen oikeaan kohtaan ei ole aina helppoa.

Seuraava tulostusfunktio ei muuta tulostettavan luvun kokonaiosaa mutta pyöristää desimaaliosan niinkuin halutaan - ja sille kelpaa sekä piste että pilkku. Se on lisäksi nopeampikin kuin printf: 
Koodia: [Valitse]
function round_in_decimals () { (( $# == 2 )) && decimals=$2 || decimals=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $decimals == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} >= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$decimals}+$(( ${desimaaliosa:$decimals+1} >= 50)) )) ;}
# kutsu:
round_in_decimals 1.51515773 2  # tulostaa 1.52

# lisätestaaminen:
round_in_decimals .5   # tämän pitää tulostaa 0 # parametria ei tarvitse kirjoittaa kun sen arvo on 0
round_in_decimals .51  # tämän pitää tulostaa 1
round_in_decimals 1.51 # tämän pitää tulostaa 2

Muut viritelmät lukujen tulostamiseksi alkavat nopeudeltaa yleensä 4ms nurkilta - mutta ei voi ihan yksiselitteisesti tuomita niitä huonoiksi sillä esimerkiksi sovellukset jotka käyttävät bc:tä voivat samalla tehdä matemaattisia ihmetekoja.

***

Se että tuo 6-rivinen funktio jossa on monimutkaista toimintaakin voi olla nopeampi kuin prinf-käsky sai jälleen kerran ajattelemaan legendaa BASH:in hitaudesta.

Nopeudessa taitaa olla kysymys seuraavasta: BASH:issa on nopeudeltaan kahdentyyppisiä käskyjä - siitä toki manuaaleissa ohimennen kerrotaan muttei sitä että niiden nopeudet eroavat monikymmenkertaisesti:
 
1. sellaisia käskyjä joiden koodi on BASH:iin liitetty. Tällaiset käskyt ovat tosinopeita - no nopeita BASH-yksiköissä. Näitä saa olla viitisenkymmentäkin peräkkäin ennenkuin suoritus kestää millisekunnin.

2. sellaisia käskyjä jotka täytyy tulkita ennen linkittämistä. Nämä ovat niitä hitaita joista useimmat kestävät luokkaa millisekunnin.

Noissa nopeissa käskyissä on paljon vaikeasti muistettavaa digisotkua jonka kirjoittaminen vaatii lisäksi näppäin-akrobatiaa - ja koska noiden käskyjen nopeudesta ei puhuta niin niitä ei opetella käyttämään vaan totutaan käyttämään yksinomaan helppoja ja hitaita ulkoisia käskyjä - jotka lisäksi kaipaavat apua sed:iltä ja awk:ilta jotka ovat pikkuhommissa vielä hitaampia.

Koskei ole täysin tajuttu että nopeitakin käskyjä löytyy niin on luultu ettei BASH:ista ole ollenkaan numeronmurskaamiseen ilman bc:n apua. Tätä on vahvistanut luulo ettei BASH kykene desimaalilaskentaan. Lisäksi luullaan että matematiikan 19:sta merkkin lukumäärää ei voisi mitenkään lisätä. Mutta pienissä "nelilaskin" laskuissa desimaaliluvuillakin BASH on neljäkertaa nopeampi kuin bc.

BASH:in vastustajia täytyy ihailla: väitetään väärin että BASH ei osaa palauttaa funktioparametreja, eval-käskyn tuomitseminen - mutta vain BASH:issa, kirjasto-osoittimen poistaminen, väärät väitteet ensimmäisenluokan funktioita ja niinpoispäin. Mikähyvänsä näistä riittää tuhoamaan melkein mitavaan. Tuntuu siltä että BASH halutaan tuhota ja nimenomaan niin etteise koskaan palaisi.   

Sehän selvisi jo aikoinaan että sed, awk ja bc täytyy unohtaa kun halutaan nopeutta yksinkertaisiin tehtäviin. Mutta se täytyi yrittää selvittää mitä kaikkea muuta kuin BASH:in nopeita käskyjä voi nopeaan skriptiin ottaa mukaan: ilmeisesti ainakin funktiot ja looppit ovat nopeita - täytyy korjata pahaa puhettani looppien hitaudesta: eivät loopit ole hitaita vaan se minkä ne suorittavat.

Käytännön skripteistä näkee parhaiten mikä kannattaa:
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 09.10.19 - klo:08.50

Ensin rajoittamattoman monen rajoittamattoman moninumeroisten luvun yhteenlaskin jossa on pari funktiota ja neljä looppia. Tätä ei ole suoranaisesti tarkoitettu käytettäväksi vaan se on tehty funktiokutsujen ja looppien nopeuden varmistamiseksi. Lopussa kuvatulla laskulla skriptin läpikäyminen vaatii noin 150:n monipuolisen käskyn suorittamisen elikä noin 3ms/150=20 mikrosekuntia per käsky -> siis kaikki sen käskyt ovat nopeita.
Koodia: [Valitse]
function radd () { 

function desimaalisumma () { for((j=1;j<=$2;j++)); do summa=$((summa+${desimaaliosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0;summa=$((summa+$muistinumero)); muistinumero=0 ;}

function kokonaissumma  () { for((j=1;j<=$2;j++)); do summa=$((summa+${kokonaisosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0; summa=$((summa+$muistinumero)); muistinumero=0 ;}

i=1; maxdesimaalit=0; maxkokonaiset=0; muistinumero=0; for n in $@; do desimaaliosa[i]=${n##*.}; desimaaliosanpituus[i]=${#desimaaliosa[i]}; (( ${desimaaliosanpituus[i]} >= $maxdesimaalit )) && maxdesimaalit=${desimaaliosanpituus[i]}; kokonaisosa[i]=${n%%.*};  kokonaisosanpituus[i]=${#kokonaisosa[i]}; (( ${kokonaisosanpituus[i]} >= $maxkokonaiset )) && maxkokonaiset=${kokonaisosanpituus[i]}; (( i++ )); done

i=1; for n in $@; do desimaaliosa[i]=${n##*.}"000000000000000000"; desimaaliosa[i]=${desimaaliosa[i]:0:$maxdesimaalit};
kokonaisosa[i]="000000000000"${kokonaisosa[i]}; kokonaisosa[i]=${kokonaisosa[i]: -$maxkokonaiset};
echo ${kokonaisosa[i]}.${desimaaliosa[i]}; (( i++ )); done 

desimaalit=''; for((i=maxdesimaalit-1;i>=0;i--)); do summa=0; desimaalisumma i $# ; desimaalit=${summa: -1}$desimaalit; muistinumero=${summa:0: -1}; done; # echo $desimaalit' '$muistinumero; read

kokonaiset=''; for((i=maxkokonaiset-1;i>=0;i--)); do summa=0; kokonaissumma i $# ; kokonaiset=${summa: -1}$kokonaiset; muistinumero=${summa:0: -1}; done; echo $muistinumero$kokonaiset.$desimaalit
}

time radd 1.17 22.222 3.33 4.4

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 11.10.19 - klo:19.27
Skriptin toiminnan tutkimiseksi siihen kannattaa skriptiä tehdessä laittaa tulostus lähes kaikkelle mahdolliselle. Mutta ennenkaikkea niitä tulostuskäskyjä ripotellaan koodiin jonka toimintaa ei omalla logiikallaan hallitse ja skripitin toimintaa korjataan kokoajan. Toiminta on tällaista kaikilla kielillä.

Mutta välillä nuo välitulosteet sotkevat joten ne poistetaan ja hetkenkuluttua taas palautetaan. Melkoinen homma jos ei tee noista tulostamisista ehdollista joten yhdellä muuttujalla voi säätää tulostetaanko kaikissa miljoonassa paikassa vai ei. Mutta ne tulostusehdot ovat kookkaita ja skriptin rakenne vääristyy.   

Mutta BASH:issa ei tarvita ehtoa. Esimerkki kertonee parhaiten kuinka tehdään: skriptin alkuun kirjoitetaan:  tulosta=echo. Ja loppuskriptin echo:t korvataan muuttujalla $tulosta. Esimerkiksi:
tulosta=echo; $tulosta apua -> tulostaa: apua.

- skriptin merkityksellisin echo kyllä jätetään.
- kun ei enää haluta välitulosteita niin muuttujalle: tulosta annetaan arvo : (siis kaksoispiste)
- voi muuttujalle: tulosta  yhtähyvin antaa arvon: 'printf %s\n'
- bacup tai mikä muu hyvänsä hoituu samallatavoin.
- se siitä ensimmäisenluokan funktiosta jota BASH:issa ei ole.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.10.19 - klo:09.42
Windows alkoi muutamavuosisiten liittämään BASH:ia koodiinsa. Olettekos ihmetelleet miksi tehdä hyvin kallis lisäys toiminnasta jota Linux-yhteisössä pidetään surkimuksena?

Johdatteleva kysymys: millä saralla Linux on ehdoton kunkku? Haluaisivatkohan muut osille noissa super-tietokoneissa? - ei se raha vaan maine. Valitettavasti taitavat onnistua kun ottaa huomioon kuinka Linux-virtuoosit BASH:ia kohtelee. Sillä BASH on surkimus ja samalla kuningaspultti.

BASH-skriptit ovat koneen hoitamisessa erinomaisia mutta harva enää nykyään konettaan hoitaa - se on noidankehä joka kokoajan kiihtyy ja päätyy orjan elämään kun kaikessa täytyy luottaa toisiin. Siis nimenomaan täytyy, ei voi valita sitä että tekisi jotain itse.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 20.10.19 - klo:17.23

BASH:issa on useampiakin keinoja laskea e (se luonnollisen järjestelmän kantaluku joka on arvoltaan 2.71828....). Tässä annetuilla työkaluilla sen arvosta saa 16 ensimmäistä numeroa. Ei tällä tietenkään ole muuta käyttötarkoitusta kuin että käyhän tämäkin leikkikalusta ja sainhan tätä tehdessäni hihitellä BASH:ista esitetyille väitteille. Itseasiassa vain tuo viimeinen rivi on uutta - funktiothan on esitetty jo aikaisemmin. Aikaa kuluu e:n arvoa etsittäessä noin 70ms

e:n arvo saadaan laskulla: 1/0!+1/1!+1/2!+1/3!+1/4! ...  Ja sen laskemistahan tuo viimeinen lause ohjaa.

- ja nimenomaan se pännii että tämä olisi toiminut aina, jo silloin aikoinaan kun BASH:ia alettiin kelvottomaksi väittää.

Koodia: [Valitse]
function jaa () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"0000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"0000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
kokonaiset=$kokonaiset.${desimaalit: -9}
# echo $jakojaannos
jakojaannos=$(((100000000*$jakojaannos)%$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=0000000000000000$desimaalit
echo $kokonaiset${desimaalit: -8} ;}     

function yhteenlasku () { # voi käyttää vähennyslaskuunkin
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=$1
luku2=$2
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
(( $m2 +1 )) && luku=$((10#$kokoluku1+10#$kokoluku2)) || luku=$((10#$kokoluku1-10#$kokoluku2))
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

summa=2; for n in {2..18}; do summa=$(yhteenlasku $summa $(jaa 1 $(($(seq -s* $n))))); done; echo $summa
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 26.10.19 - klo:09.44
Desimaalimatematiikan suorittaminen BASH:in omilla käskyillä vaatii runsaasti aputoimintoja. Useat aputoiminnot ovat niin nopeita että kun niistä kasaa funktion niin se ei hidasta havaittavasti. Joten niitä funktioita kannattaa tehdä - jos ei muusta syystä niin virtuooseja kurmuuttaakseeni sillä eihän näillä funktioilla toistaiseksi muuta merkitystä ole. Esimerkiksi:
Koodia: [Valitse]
function desimaaliosanloppunollatpois () {
kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; [ ${#desimaaliosa} -eq ${#1} ] && { echo $1; return ;}; desimaaliosa=.$desimaaliosa; while [[ ${#desimaaliosa} && ${desimaaliosa: -1} == [.0] ]]; do desimaaliosa=${desimaaliosa:0: -1}; done; echo $kokonaisosa$desimaaliosa ;}

luku=10.0102000; desimaaliosanloppunollatpois $luku

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 29.10.19 - klo:06.30
Päivitin neliöjuuren laskemis-skriptin, vaikkei sillä toistaiseksi olekaan muuta tarkoitusta kuin osoitaa että BASH:in matematiikkaa haukutaan osittain väärin perustein. Ja kukatietää mihin päädytään jatkuvien oleellisten parannuksien kanssa - nämäkin parannukset nopeuttivat kaksinkertaisesti. Ehkä BASH:in matematiikastakin tulee edes siedettävää?

Skripti laskee neliöjuuresta vain 8-9 numeroa mutta sen se tekee parhaimmillaan nopeammin kuin jos laskettaisiin neliöjuuri bc:ssä - elikä laskenta kestää 2ms:a.

Mutta neliöjuuren laskemiseen tätä ei ole tehty vaan virtuoosien toimien ihmettelyyn. Sillä nyt kun skriptillä on sujuvampi toiminta on varmaa että ihmettelyyn on aihetta. On mahdotonta uskoa että maailmanluokan virtuoosit eivät olisi tunteneet desimaalilaskennan saloja ikuisesti.

Jossain vaiheessa kiinnitän huomioni logaritmeihin. Niihinkin BASH ilmanmuuta kykenee olkoonkin etteivät BASH:in toistaiseksi selvinneet kyvyt kovin pitkälle riitä.
Koodia: [Valitse]
function sqrt () {
declare -i seed 
declare -i delta # nämä declare-määritelyt mahdollistivat koodimuutoksia ja normaalin-näköisen matematiikan. Koodi nopeutuikin melkein puolella.

function desimaalipiste2oikealle () { # juurrettava on <0
desimaaliosa=${1##*.}; # kokonaisosaahan ei voi ollakaan jos tänne on tultu.
(( ${desimaaliosa:0:1} )) && echo ${desimaaliosa:0:2}.${desimaaliosa:2} || echo ${desimaaliosa:1:1}.${desimaaliosa:2} ;}               

function  desimaalipiste2vasemmalle () { # juurettava on >100
kokonaisosa=${1%%.*}; desimaaliosa=${1##*.}
echo ${kokonaisosa:0: -2}.${kokonaisosa: -2}$desimaaliosa ;}
 
luku=$1 # funktio sqrt:n koodi alkaa
[[ $luku == 1 ]] && echo 1 && return
[[ ${luku:0:1} == . ]] && luku=0$luku
[[ ${luku//./} == $luku ]] && luku=$luku".0" || luku=$luku'0' # luvussa täytyy olla desimaalipiste
desimaalisiirto=0
while (( ${luku%%.*} <= 0 )); do luku=$( desimaalipiste2oikealle $luku) && (( desimaalisiirto-- )); done
# echo $desimaalisiirto' '$luku
while (( ${luku%%.*} >= 100 )); do luku=$(desimaalipiste2vasemmalle $luku) && (( desimaalisiirto++ )) ; done #; echo $desimaalisiirto' '$luku # toiminnantarkastamisessa hyvä vihje
in=${luku//./}"0000000000000000000"
in=${in:0:17}
seed=2010000000  # maksimi seed olisi 4294967295
delta=1005000000
apu=${luku%%.*}
(( ${#apu} == 1)) && {
for (( n=delta; n>=1; n=n/2 )); do [[ $seed*$seed -gt $in ]] && seed=seed-n || seed=seed+n; done ;} || { for (( n=delta; n>=1; n=n/2 )); do [[ $seed*${seed::-1} -gt $in ]] && seed=seed-n || seed=seed+n; done ;}
case  1:${desimaalisiirto:--} in # tulostus
($((desimaalisiirto<0))*) echo ${seed:0:1}.${seed:1}e$desimaalisiirto ;;
($((desimaalisiirto>8))*) echo ${seed:0:1}.${seed:1}e$desimaalisiirto ;;
                       *) echo ${seed:0:desimaalisiirto+1}.${seed:desimaalisiirto+1} ;;
esac ;}


# tarkistukset: sqrt 2.3456   # pitää tulla: 1.53153519
#               sqrt 23.456   # pitää tulla: 4.84313949
#               sqrt 234.56   # pitää tulla: 15.3153519
#               sqrt 0.023456 # pitää tulla: 1.53153519e-1
#               sqrt 1234567800000000000  # pitää tulla: 1.11111105e9
#               sqrt .000000000012345678  # pitää tulla: 3.51364171e-6

***

Desimaalimatematiikan skripteissä on usein monta funktiota. Kun joku funktio höpertää menee koko lasku metsään. Olisi helppo selvittää mikä funktio on syypää mikäli funktiot kirjoittaisivat näytölle välituloksiaan.

Mutta koska parametrit normaalisti siirretään näytön kautta menee vastaanottaja sekaisin kun ei erota mikä on välitulosta ja mikä parametria. Juuri tästä syystä haukutaan ettei BASH osaa parametrejaan palauttaa - mutta haukutaan syyttä kun ei haluta käsittää että parametrien palautustapoja on useampia.

Tässä parametrit siirretää suurinpiirtein samallatavoin kuin muissakin kielissä:
Koodia: [Valitse]
function koe () { apu=$(declare | grep ^$1=); muuttuja=${apu##*=}; muuttuja=$((2*muuttuja)); echo höh; read $1 < <(echo $muuttuja) ;}

# funktiokutsu:
luku=2; koe luku; echo $luku
selvitystä muuttujista:
apu=$(declare | grep ^$1=)  hakee BASH:in muutujalistasta muuttujaa vastaavan declare-lauseen.
                            Muuttujalista on nimittäin sama oltiinpa funktiossa tai pääohjelmassa.
                            Tällätavoin funktioon voidaan toimittaa haluttu määrä parametreja, myös
                            matriiseja. Samanaikaisesti voidaan käyttää sitä vanhaakin tapaa.
muuttuja=${apu##*=}         ottaa muuttujan arvon declare-lauseesta yhtäkuin-merkin perästä.
muuttuja=$((2*muuttuja))    kerrotaan muutuja kahdella jotta pääohjelmassa näkisi muuttujan muuttuneen.
read $1 < <(echo $muuttuja) muutetaan muuttujalistaa. Read, let tai readarray on pakollinen.
echo höh                    osoitus siitä ettei näytölle kirjoittaminen sotke laskua.
apu                         apumuuttuja jolla ei ole myöhemmin mitään merkitystä.
 
- parametri on tyypiltään nimiparametri eikä arvoparametri jollaisia BASH:in funktioissa on totuttu käyttämään. Muutujalle ei pidä laittaa funktiossa local-määrittelyä.
- palautettava parametri saa olla mitä tyyppiä hyvänsä, vaikka matriisi.
- voidaan palauttaakin niin monta parametria kuin halutaan ja mitä tyyppiä halutaan, myös matriiseja.

Hankalaahan tuo on, mutta yleensä tätä parametrien palautustapaa ei tarvitse käyttää.
- tuon declare-kompleksin sijaan voi kirjoittaa: eval apu=\$$parametrinumero. Silloin ei tarvita lausetta: apu=${apu##*=}
- jos pilkkua viilataan niin parametria ei palauteta vaan määritetään sen arvo globaalialueella.
- asia on esitetty aikaisemminkin mutta nyt se on esitetty täysin uudesta näkökulmasta.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 12.11.19 - klo:20.33
BASH:issa on aivan varmaa että kun joku tekee jostain asiasta hitaan satarivisen skripti-hirviön niin joku tekee samasta asiasta muutaman rivinpituisen nopeasti toimivan toteutuksen ja hirviön tekijä saa trauman loppuiäkseen. Mutta jos tätä ei hyväksytä niin BASH:ia ei kukaan uskalla kehittää - sillä niitä lyhyitä ja nopeita versioita ei tehdä ensiksi sillä vain mikäli tuo hirviöskripti toimii uskotaan etteivät puheet BASH:in kyvyttömyydestä pidä paikkaansa ja kannattaa yrittää - tai eihän nykyään enää yritetä sittenkään koska väitteet BASH:in kyvyttömyydestä on omaksuttu liiankin hyvin.

***

Valmistautumista logaritmin laskemiseen - tämä on se hidas hirviö jonka täytyy toimia ennenkuin on mahdollista tehdä se nopea versio - vaikka eihän BASH:ista saa edes kohtuunopeaa matemaattisissa operaatioissa.

Kymmenkantaista logaritmia laskettaessa suurin haaste on logarotmoitavan desimaaliluvun korottaminen potenssiin kymmenen. BASH:issa kymmenpotenssiinkorotus täytyy toteuttaa kertomalla luku itsellään kymmenenkertaa sillä merkkiluku eksponentiaatiossa on paljon pienempi. BASH:in oman kertolaskunkaan numeromäärä ei ole riittävä vaan tarvitsee käyttää kaksinkertaisen numeromäärän desimaalilukujen kertolaskuskriptiä. Kymmenpotenssiin korottaminen tällätavoin kestää noin 65ms ja lopputuloksessa on noin 14 oikeaa numeroa:
Koodia: [Valitse]
#!/bin/bash
# Kertolaskussa kumpikin jäsen saa olla 17numeroinen ja tulokseen voi tulla 34numeroa. Myös etumerkki
# hyväksytään niinkuin myös desimaalit ja silti toimintanopeus on 2ms. Onhan se pitkä aika yhdelle
# kertolaskulle, mutta onnistuu sentään. Tätä versiota on korjailtu runsaasti.

function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} > /tmp/joo || { echo $(( 10#$summa2 ))$summa1 ;} ;}

# Potenssiin korotus on helpointa testata seuraavalla skriptillä: se tulostaa jokaisella kierroksella kertolaskusta saadun tuloksen ja seuraavalla rivillä bc:n varmasti oikean tuloksen. Muista laittaa potenssiin korotettava täysin samanlaisena kahteen paikkaan:
time { echo 1 > /tmp/joo; for n in {1..10}; do kerro18 9.87654321 $(cat /tmp/joo); cat /tmp/joo;
bc -l <<< "9.87654321^$n"; echo; done ;}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 13.11.19 - klo:08.46
petteriIII on ominut tämän säikeen lähes kokonaan, niin laitetaan nyt itsellenikin tarpeellinen scriptin pätkä. En tosin ole sitä testannut kaikilla (ääri)arvoilla.

Tämä tasaa horisontin kuvasta imageMagick:n ja bc:n avulla määrittelemällä kaksi pistettä, jotka ovat horisontin koordinaatissa.
Koodia: [Valitse]
$ cat fixhorizon.sh
#!/bin/bash

if [ "$#" -ne 6 ]
then
echo "use: ./fixhorizon.sh fromImg x1 y1 x2 y2 toImg"
exit
fi
pi=`echo "h=20;4*a(1)" | bc -l`
echo $pi
x=`echo "h=20;$4-$2" |bc -l`
echo x
y=`echo "h=20;$5-$3" |bc -l`
echo y
if [ $x -eq 0 ]
then
if [ $y -gt 0 ]
then
angle=90
else
angle=-90
fi
else
angle=`echo "h=20;-1*a($y/$x)*180/$pi"|bc -l`
fi
echo $angle

convert -rotate "$angle" $1 $6
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 15.11.19 - klo:13.11
Sanallinen esitys kymmenkantaisen logaritmin laskemisesta:
- tässä skriptissä alkuoletus on että logaritmoitava on yli 1.
- kun puhutaan numerosta niin tarkoitetaan yhtä merkkiä: 1, 2, 3, 4, 5, 6, 7, 8, 9 tai 0.
- toiminta ei muutu mitenkään vaikka logaritmoitava olisin kokonaisluku ilman desimaaleja. 
- aluksi logaritmi nollataan. Sitten logaritmin ensimmäiseksi numeroksi tulee "logaritmoitavan kokonaisosan numeroiden lukumäärä -1" ja perään tulee desimaaipiste.

Sitten logaritmiin lisätään numeroita yksi kerrallaan niin monta kertaa kuin tulokseen halutaan desimaaleja: logaritmin seuraava numeron selvittämiseksi lasketaan uusi logaritmoitava siirtämällä desimaalipiste  vanhassa logaritmoitavassa ensimmäisen numeron perään ja korottamalla saatu luku potenssiin kymmenen. Logaritmin seuaava numero on "logaritmoitavan kokonaisosan numeroiden määrä -1". Uudesta logaritmoitavasta tulee vanha logaritmoitava. Palataan kohtaan: Sitten logaritmiin lisätään ...

Koodina tämä on:
Koodia: [Valitse]
#!/bin/bash
function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function getlog ()  { # varsinainen logaritmointi
apu=${1//,/.}
[[ ${apu//[^.]/} ]] && vanhalogaritmoitava=$1 || vanhalogaritmoitava=$1".0"
logaritmoitavankokonaisosa=${vanhalogaritmoitava%%.*}
logaritmi=0; logaritmi=$((${#logaritmoitavankokonaisosa}-1)).
# sitten logaritmiin lisätään numeroita yksi kerrallaan mikäli siinä ei vielä ole tarpeeksimontaa numeroa:
until (( ${#logaritmi} >= 19 )); do
  apu=${vanhalogaritmoitava//./}; apu=${apu:0:1}.${apu:1}; # echo apu:$apu
  uusilogaritmoitava=1.0;
  for n in {1..10}; do uusilogaritmoitava=$(kerro18 $apu $uusilogaritmoitava); done
  # echo uusilogaritmoitava:$uusilogaritmoitava; read
  uudenlogaritmoitavankokonaisosa=${uusilogaritmoitava%%.*}
  logaritmi=$logaritmi$((${#uudenlogaritmoitavankokonaisosa}-1))
  vanhalogaritmoitava=$uusilogaritmoitava
done
echo luvun: $1 '  logaritmi on: '$logaritmi ;}

getlog 2.0000001234


Pitäisi tulla: luvun: 2.0000001234   logaritmi on: 0.301030022459
#                                                           tulee: 0.301030022459 


Onhan tämä suunnattoman hidasta eikä tarkkuuskaan riitä kuin tusinaan numeroita. Mutta se on osoitus siitä mihin BASH kykenee 19-numeron kokonaislukukertolaskullaan ja ennenkaikkea osoitus ettei pidä sanoa: BASH ei osaa .....

Jokaisen skriptin kykyjen huonouteen pätee: kun joku esittää jotakin niin täysin varmasti jokutoinen pistää paremmaksi. Tai nykyäänhän se ei enää pidä paikkaansa koska virtuoosit ovat saaneet kaikki uskomaan että BASH on paitsi kelvoton niin myös kyvytön.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 19.11.19 - klo:09.11
Vaikka juuri koskaan ei saa aikaiseksi BASH-skriptiä joka ei alussa hölmöilisi niin ne hölmöilyt saa aina loppumaan. Olenkin aina kuvaillut tulevaa skriptiä ennenkuin oleellisimpia funktioita on tehty - ja jokakerran BASH saa asiat toimimaan. Toivottoman hidashan BASH on mutta kyllä BASH:illakin koodaten hautaan kerkiää - sitäpaitsi olen huomannut että kun teen uudestaan skriptin joka aikoinaan kesti sekunteja niin nyt se kestää millisekunteja. 

***   

Nyt antilogaritmin kimppuun:
 
"antilogaritmin peukalosääntö": luvun 10 logaritmi on 1; kääntäen: luvun 1 antilogaritmi on 10
- antilogaritmia tarvitaan esimerkiksi kun lasketaan: luku^murtoluku:
esimerkiksi 2^2.1  lasketaan: antilogaritmi $( 2.1*$(logaritmi 2))
- mutta varsinainen syy tämän skriptin tekemiseen on testata desimaalilaskentaa ja oppia sen sääntöjä.

Antilogaritmin laskemisessa erikoisinta on siinä oleva funktio luvun puolittamiseen sillä sen täytyy ottaa nopeasti huomioon:
- puolitettava on aina joko kokonaisluku tai desimaaliluku typiltään: x.xxxxxxxxxxxxxxxxxxxxxx jossa x kuuluu joukkoon 0-9 tai tyhjä.
- joten siinä tulee eteen kummankin tyypin oktaaliansa: kokonaisluku saattaa olla nolla ja oktaaliansa laukeaisi jos sitä ei väistelisi; laskettaessa poistaisi ja tulostettaessa palauttaisi.
- mutta mikäli desimaaliosankin alussa on nollia niin tätä käänteistä oktaaliansaa ei edes voi poistaa. Jotta ei jouduttaisi oktaaliansaan lisätään desimaaliosan eteen luku 2 ja tulostettaessa se poistetaan - silloinhan se on edelleen tulostettavan alussa ja sen arvo on 1.

puolittamisen toteuttava funktio:
Koodia: [Valitse]
#!/bin/bash
function puolita () {
[[ ${1//[^.]/} ]] && luku=${1:0:18} || luku=${1:0:18}".0"
luku=${luku//0./.}
desimaaliosa=${luku##*.}
kokonaisosa=${luku%%.*}
kokonaisosanpituus=${#kokonaisosa}
(( $kokonaisosa )) && etunolla='' || etunolla='0'
kokonaisosa='2'$kokonaisosa # desimaaliosasta on tullut kokonaisosa
lukuilmandesimaalipistetta=$kokonaisosa$desimaaliosa
 
kokonaiset=$(($lukuilmandesimaalipistetta/2))
jakojaannos=$(($lukuilmandesimaalipistetta%2))
desimaalit=$(((1000000000*$jakojaannos)/2))
desimaalit=000000000000000$desimaalit
kokonaiset=$etunolla${kokonaiset:1}${desimaalit: -9}
 
echo $(( 10#${kokonaiset:0:$kokonaisosanpituus} )).${kokonaiset:$kokonaisosanpituus} ;}
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.11.19 - klo:08.33
Puolitus-funktiosta on tullut jo kaksi uutta versiota sillä vanhat versiot toimivat joissain erikoistilanteissa väärin. Desimaalilaskennan osalta en edes yritä laskea versioita. Jokaisessa skriptissä on varaa parantaa elikä tehdä siitä uusi versio. Joskus uusi versio on hitaampi mutta joskus myös nopeampi kuin vanha versio - ja koodi yksinkertaistuu melkein aina.

BASH:in desimaalilaskenta toimi jo ennenkuin bc:tä awk:ia oli. Mutta koska virtuoosit uskottelivat ettei BASH desimaalilaskentaan kykene niin BASH:ia ei enää edes kehitetty "koska matematiikka toimii kelvottoman huonosti". Ja kun kieltä ei kehitetä niin käytännössä se taantuu. Todellisuudessa BASH on "vähänumeroisissa nelilaskintyyppisissä desimaalilaskuissa"  huomattavasti nopeampi kuin mikään ulkoinen ohjelma kutem esimerkiksi bc tai awk.

Kehittyneemmissä laskuissa ja varsinkin "antilogaritmin laskemis-tyyppisissä" BASH on laskennallisesti erittäin hidas. Silti noita laskuhirviöitä kannattaa tehdä sillä ne eivät toimisi jos missään olisi ongelmia: jokaisen funktion ja funktioiden välisen tiedonsiirron tulee toimia moitteetta eivätkä funktiot saa häiritä toisiaan. Siis jos nuo laskuhirviöt toimivat niin se takaa laadun.

Sekin alkaa näkyä että vaikka BASH:in merkinnät ovat tyystin erilaisia kuin muissa kielissä niin ajatukset ovat samoja elikä ongelmat on kyllä tunnettu jo aikoinaan.

***

Antilogaritmin laskemiseksi ei ole samantapaista menetelmää kuin logaritmin laskemiseksi, joten antilogaritmin arvo on etsittävä binäärihaulla vertaamalla lukuarvion logaritmia annettuun lukuun.

Selvitettävää numeroa kohti binäärihaku täytyy suorittaa noin kolmesti joten pyrittäessä tarkkuuteen 14 numeroa binäärihakuja tulee noin 47 elikä homma kestää peruskoneella noin 6-16 sekuntia. Tajuttoman pitkään siis, mutta verrattuna siihen ettei se toimisi ollenkaan on ero valtava. Sitäpaitsi mikäli BASH:iin saisi pieniä kielellisiä parannuksia toisivat ne nopeutta tuhatkertaisesti.

Esimerkkinä antilogaritmista lasketaan minkä luvun logaritmi on: 0.30102999566398119:
Koodia: [Valitse]
  #!/bin/bash
function puolita () {
[[ ${1//[^-]/} ]] && merkki=- || merkki=''
luku=${1//-/}
[[ ${luku//[^.]/} ]] && luku=${luku:0:18} || luku=${luku:0:18}"."
[[ ${luku:0:1} == '0' ]] && luku=${luku:1}
desimaaliosa=${luku##*.}
kokonaisosa=${luku%%.*}
kokonaisosanpituus=${#kokonaisosa}
kokonaisosa='2'$kokonaisosa
lukuilmandesimaalipistetta=$kokonaisosa$desimaaliosa
kokonaiset=$(($lukuilmandesimaalipistetta/2))
jakojaannos=$(($lukuilmandesimaalipistetta%2))
kokonaiset=${kokonaiset:1}$(((10*$jakojaannos)/2))
echo $merkki$(( ${kokonaiset:0:$kokonaisosanpituus} )).${kokonaiset:$kokonaisosanpituus} ;} 

function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function getlog ()  { # varsinainen logaritmointi
apu=${1//,/.}
[[ ${apu//[^.]/} ]] && vanhalogaritmoitava=$1 || vanhalogaritmoitava=$1".0"
logaritmoitavankokonaisosa=${vanhalogaritmoitava%%.*}
logaritmi=0; logaritmi=$((${#logaritmoitavankokonaisosa}-1)).
# sitten logaritmiin lisätään numeroita yksi kerrallaan mikäli siinä ei vielä ole tarpeeksimontaa numeroa:
until (( ${#logaritmi} >= 14 )); do
  apu=${vanhalogaritmoitava//./}; apu=${apu:0:1}.${apu:1}; # echo apu:$apu
  uusilogaritmoitava=1.0;
  for n in {1..10}; do uusilogaritmoitava=$(kerro18 $apu $uusilogaritmoitava); done
  # echo uusilogaritmoitava:$uusilogaritmoitava; read
  uudenlogaritmoitavankokonaisosa=${uusilogaritmoitava%%.*}
  logaritmi=$logaritmi$((${#uudenlogaritmoitavankokonaisosa}-1))
  vanhalogaritmoitava=$uusilogaritmoitava
done
echo $logaritmi ;}

function onkoekasuurempi  () {
[[ ${1//[^-]/} ]] && m1=a || m1=b; [[ ${2//[^-]/} ]] && m2=a || m2=b
luku1=${1//[-+]/}; luku2=${2//[-+]/} # etumerkki omaan muuttujaan ja vertailtavista pois

while [[ ${luku1:0:1} == 0 ]]; do luku1=${luku1:1}; done ; while [[ ${luku2:0:1} == 0 ]]; do luku2=${luku2:1}; done # etunollien poistaminen
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
(( $# )) && luku1=${luku1//[^0-9]/} && luku2=${luku2//[^0-9]/}
kokonaisosa1=${luku1%%[,.]*}; desimaaliosa1=${1##*[,.]}
kokonaisosa2=${luku2%%[,.]*}; desimaaliosa2=${2##*[,.]}
case $m1$m2 in 
  ba )  echo 1 ;;
  ab )  echo 0 ;;
  bb )  [[ $kokonaisosa1 > $kokonaisosa2 ]] && { echo 1 || echo 0 ;return ;} || [[ $desimaaliosa1 > $desimaaliosa2 ]] && echo 1 || echo 0 ;;
  aa )  [[ $kokonaisosa1 > $kokonaisosa2 ]] && { echo 0 || echo 1 ;return ;} || [[ $desimaaliosa1 > $desimaaliosa2 ]] && echo 0 || echo 1 ;;
esac ;}

function yhteenlasku () { # voi käyttää vähennyslaskuunkin
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=$1
luku2=$2
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellainen mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
(( $m2 +1 )) && luku=$((10#$kokoluku1+10#$kokoluku2)) || luku=$((10#$kokoluku1-10#$kokoluku2))
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

# laskennan määrittelyt:
annettuluku=0.30102999566398119 # Kirjoita tähän mistä antilogaritmi lasketaan - mitään muuta ei muuteta
tulosarvio=$((10**$((${annettuluku%%.*}+1))/5))
delta=$(puolita $tulosarvio)

# sitten binäärihaku:
time for n in {1..45}; do
echo 'kierros: '$n'  $tulosarvio ' $(getlog $tulosarvio)'  delta: '$delta       
(( $(onkoekasuurempi $(getlog $tulosarvio) $annettuluku ))) && tulosarvio=$(yhteenlasku $tulosarvio -$delta) || tulosarvio=$(yhteenlasku $tulosarvio $delta)
  delta=$(puolita $delta)
  delta=${delta:0:18}  # onko oikea paikka?   
done
echo $annettuluku on luvun: ${tulosarvio:0:12}  logaritmi 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 10.12.19 - klo:12.40
Useimmille matematiikan funktioille on monia erilaisia sarjakehitelmiä. Ja luonnollista logaritmia ei BASH:issa voi laskea samallatavalla kuin 10-kantainen logaritmi laskettiin vaan se täytyy laskea sarjakehitelmällä koska BASH:issa korottaminen potenssiin 2.71... on vaikeaa. Tässäkäytetty sarjakehitelmä toimii tosin vain kun logaritmoitava on suurempi kuin .5 . Vaikka teoriassa ylärajaa ei ole niin aikaa alkaa kulua kun logaritmoitava on suurempi kuin 100.

Tähänmennessä tehdyillä työkaluilla saa luonnolliseen-logaritmiin vain tusinan oikeita numeroita ja sekin vaati että työkaluihin tehtiin koodiparannuksia - sillä uudentyyppiset tehtävät tuovat näin alussa esiin uusia ongelmia. Koodiparanneltujen työkalujen tulee toimia myös vanhoissa tehtävissä.

Tämä on sellaista numeroleikkiä jota harrastettiin vuosikymmeniä sitten samaanaikaan kun tietoisena BASH:in matemaattisista kyvyistä vakuutettiin silmät sinisinä ettei BASH osaa. Ja koskei alkeita selvitetty silloin aikoinaan täytyy ne yrittää selvittää nyt - tai eihän se enää kannata vaikka mielenkiintoista onkin.

Käytetyn matemaattisen sarjan "sanallinen" kuvaus:
apu=(logaritmoitava-1)/logaritmoitava; lnx=apu+(1/2)*apu^2+(1/3)*apu^3+(1/4)*apu^4 ...
- siis tässä tarvitsee laskea luvun korottamista potenssiin luokkaa sata.

Sanallisesta kuvauksesta tehty koodi: ensiksi lasketaan apu muuttujaan ja apu:n potenssien arvot matriisiin. Sarja suppenee eri luvuilla kovin erinopeasti joten lasketaan matriisiin uusia jäseniä niin kauan että laskuista saadut merkitsevät numerot ovat kaikki nollia - matriisin koosta ei tarvitse välittää sillä BASH:in matriisit kasvavat automaattisesti jäseniä lisättäessä:
Koodia: [Valitse]
function kerro18 () {
tulosta=: # jos halutaan tulostaa niin merkitään: tulosta='echo -e' ja jos ei niin tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1'\r\v'
luku2=${luku2: -36} ; $tulosta $luku2'\r\v'
luku3=${luku3: -36} ; $tulosta $luku3'\r\v'
luku4=${luku4: -36} ; $tulosta $luku4'\r\v'
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12'\r\v'
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22'\r\v'
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32'\r\v'
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42'\r\v'
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1'\r\n'
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1'\r\n'
summa2=000000000000000000$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); summa2=${summa2: -18}; $tulosta summa2:$summa2'\r\n'
#summa2=$(($m1*$m2*10#$summa2))
(( $m1 != $m2 )) && echo -n '-' 
apu=$summa2$summa1; (( $desimaaleja )) && echo $((${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function jaa () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"0000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"0000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}
luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
kokonaiset=$kokonaiset.${desimaalit: -9}
# echo $jakojaannos
jakojaannos=$(((100000000*$jakojaannos)%$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=0000000000000000$desimaalit
(( $m1 != $m2 )) && echo -n '-'
echo $kokonaiset${desimaalit: -8} ;}

function yhteenlasku () { # voi käyttää vähennyslaskuunkin merkki!
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
luku1=${1:0:18}
luku2=${2:0:18}
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellainen mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
case $m1$m2 in 
  +- )  luku=$((10#$kokoluku1-10#$kokoluku2))  ;;
  -+ )  luku=$((-10#$kokoluku1+10#$kokoluku2)) ;;
  ++ )  luku=$((10#$kokoluku1+10#$kokoluku2))  ;;
  -- )  luku=$((-10#$kokoluku1-10#$kokoluku2)) ;;
esac ;
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

unset matriisi
logaritmoitava=4.214 # kirjoita tähän se luku josta haluat luonnollisen logaritmin.
matriisi[1]=$(jaa $(yhteenlasku $logaritmoitava -1) $logaritmoitava); apu=${matriisi[1]}; time for n in {2..10000}; do matriisi[$n]=$(kerro18 $apu ${matriisi[$n-1]}); (( 10#${matriisi[$n]:3:16} == 0 )) && echo $n && break; printf "%s\n" "${matriisi[@]}"; done

# sitten matriisista lasketaan luonnollinen logaritmi:
lnx=0; for (( n=1; n<=${#matriisi[@]}; n++ )); do
apu=$(kerro18 $(jaa 1 $n) ${matriisi[n]})
lnx=$(yhteenlasku $lnx $apu); echo $lnx; done
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 15.01.20 - klo:09.07
Muuttujan julistaminen (=declare) kokonaisluvuksi kannattaa, sillä koodi yksinkertaistuu ja nopeutuukin aikapaljon. Käskyt näiden toteamiseksi:
 
time { declare -i x; x=0; for n in {1..100000}; do x=x+1; done; echo $x ;}
time { unset x; x=0; for n in {1..100000}; do x=$((x+1)); done; echo $x ;}
time { for n in {1..100000}; do : ; done ;} # tällä saa sen ajan minkä itse looppi vie

- käsky: x=x+1 tallettaa muistipaikkaan x tekstijonon x+1 ja tuo "declare -i x" määrää BASH-tulkin tulkkaamaan sen kokonaisluvuksi. Näet tämän kun kirjoitat:
Koodia: [Valitse]
unset x; for n in {1..100000}; do x=x+1; done; echo $x
- se että käskyrivin eteen täytyy kirjoittaa "unset x" johtuu siitä että BASH-tulkki noudattaa samassa päätteessä aikaisemmin annettua määräystä ellei uutta määräystä ole annettu. Käsky "unset x" poistaa määräyksen.
- tämä vanhojen asetuksien muistaminen saattaa joskus olla paha asia silloin kun yrittää opiskella BASH:in saloja. Tämän takia omat skriptit saattavat toimia silloinkin kun ei pitäisi ja toisten tekemät skriptit eivät toimi vaikka pitäisi.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 23.01.20 - klo:21.59
BASH:issa ei ole desimaalilukuja - on vain tekstijonoja joista jotkut voi tulkita desimaaliluvuiksi. Tekstijonoa täytyy usein käsitellä jotenkin ennen tulostamista. Olisi houkuttelevaa käyttää tulostamiseen printf-komentoa sillä se kykenee käsittelemään tulostamisen yhteydessä ja se tulostaa desimaaliluvutkin oikein. Mutta se toimii vain kun kokonaisosan ja desimaaliosan yhteenlaskettu numeromäärä on BASH:issa  alle 19, se vaatii joskus desimaalieroittimeksi sen mikä koodisivulla on määrätty (pilkku tai piste) - ja lisäksi se on pikkuisen hidas. Ei voi olla ajattelematta että prntf on heitetty BASH:in rattaisiin jottei desimaalilaskentaa saisi toimimaan hyvin.

Käytettäessä echo:a tulostettavissa luvuissa saa olla moninkertainen määrä numeroita eikä desimaalieroittimen kanssa joudu tappelemaan. Mutta echo ei kykene käsittelemään tulostettavaa mitenkään vaan se tarvitsee apua BASH-skripteiltä ja nämä ovat usein erittäin kookkaita - mutta senlaatuiset skriptit ovat myös hyvin nopeita.

Desimaalilaskennan skriptit ovat vikaherkkiä senlisäksi että ne ovat runsaskoodisia. Ja sellaisissa tilanteissa käytetään normaalisti kirjastoja koodinteossa mutta virtuoosit ovat suhmuroineet BASH:in kirjastoja niin paljon ettei BASH:in kirjastoja juuri kukaan enää käytä. Sentakia on helppoa uskotella että esimerkiksi BASH ei kykene juuri ollenkaan numeronmurskaukseen ilman ulkoisten ohjelmien apua, joten esimerkiksi desimaalilaskentaa pidetään mahdottomana.

Kirjasto on yksinkertaisimmassa muodossaan tiedosto johon on kirjoitettu funktioita. BASH:issa jonkun toisen tiedoston funktiot voi liittää tehtävän skriptin koodiin käskyllä: . tiedoston nimi. Liitoskäsky on tosiaankin pelkkä piste. Aikoinaan BASH:illa oli moniakin kirjastoja - netistä löytyy vieläkin aikamonta (mutta niiden löytäminen on hankalaa). Mutta tapa liittää kirjasto  tehtävän skriptin koodiin oli alussa toisenlainen kuin nykyään. Kun liitäntätapaa muutettiin niin kirjastoja lakattiin käyttämästä - vaikka ne toimisivat edelleenkin.

Kirjastofunktion testaaminen on huomattavasti isompi työ kuin tavallisen funktion vaikka niillä ei teoriassa juuri eroa ole - mutta kirjastofunktion täytyy varautua paljon paremmin niihin tapauksiin joita pidetään puolimahdottomina. Jokatapauksessa jokaisessa kirjastofunktiossa on alkuunsa useimmiten naurettaviakin puutteita ja virheitäkin - ja läheskaikkien korjaaminen olisi vuosien urakka komppanialta koodaajia.

Esimerkiksi seuraava tosinopeatoiminen funktio jota kehoitetaan katkaisemaan annettu desimaaliluku määrättävän desimaalimäärän jälkeen pyöristäen oikein. Funktion koodi on nopea huolimatta pituudestaan - eikä tällaista funktiota varmaankaan kukaan viitsisi tehdä jos sitä ei löydy kirjastosta:
Koodia: [Valitse]
function pyöristä_desimaaleissa () { (( ${1//./} > 0 )) && { (( $# == 2 )) && desimaalienluku=$2 || desimaalienluku=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $desimaalienluku == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} >= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$desimaalienluku}+$(( ${desimaaliosa:$desimaalienluku+1} >= 50)) )) ;} || { (( $# == 2 )) && desimaalienluku=$2 || desimaalienluku=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $desimaalienluku == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} <= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$desimaalienluku}-$(( ${desimaaliosa:$desimaalienluku+1} <= 50)) )) ;} ;}

# kutsuesimerkit testaamiseen (kokeillessa leikkaa-liimaaa kaikki päätteeseen yhtenä palasena):
pyöristä_desimaaleissa 1.51515773 0   # pitäisi tulostaa 2
pyöristä_desimaaleissa 1.51515773     # pitäisi tulostaa 2
pyöristä_desimaaleissa 1.51515773 2   # pitäisi tulostaa 1.52
pyöristä_desimaaleissa 12345678901234567890123456789.51515773 2 # pitäisi tulostaa
                                                                #12345678901234567890123456789.52
pyöristä_desimaaleissa 1.51515773 3   # pitäisi tulostaa 1.515
pyöristä_desimaaleissa 1.51515773 4   # pitäisi tulostaa 1.5152
pyöristä_desimaaleissa 1.5151577311111151 14 # pitäisi tulostaa 1.51515773111112
pyöristä_desimaaleissa -1.51515773 0  # pitäisi tulostaa -1
pyöristä_desimaaleissa -1.51515773 2  # pitäisi tulostaa -1.51
pyöristä_desimaaleissa -1.51515773 3  # pitäisi tulostaa -1.514
pyöristä_desimaaleissa -1.51515773 4  # pitäisi tulostaa -1.5151
pyöristä_desimaaleissa 1.1 9          # pitäisi tulostaa  1.100000000
:
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Jere Sumell - 04.04.20 - klo:08.55
Mikä olisi hyvä kirja opiskella bash-skriptausta? Varmaan suomeksi ei ole kirjoitettu, mutta kieli ei ole este.

Pitäisi ohjelmoida skripti, joka automatisoi käyttäjälogi-tiedostoista tiedoston poistoa, kun se on vanhenutunt tietyn aikaa, eli aika pitäisi tarkistaa, ja sitä mukaa skripti poistaa käyttäjälogi-tiedostosta rivin tai tietueen paremminkin virallista termiä käyttäen, kun ensin tarkistaa sen syntyajankohdan.

Olisi aikaa ja halukkuutta ottaa Bash-skriptaus tehokäyttöön haltuun, ja perinteinen kirja on paras tietolähteeni.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 04.04.20 - klo:09.23
Pitäisi ohjelmoida skripti, joka automatisoi käyttäjälogi-tiedostoista tiedoston poistoa, kun se on vanhenutunt tietyn aikaa, eli aika pitäisi tarkistaa, ja sitä mukaa skripti poistaa käyttäjälogi-tiedostosta rivin tai tietueen paremminkin virallista termiä käyttäen, kun ensin tarkistaa sen syntyajankohdan.

Tuohon kannattaa käyttää logrotatea
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: jekku - 04.04.20 - klo:09.45
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Jere Sumell - 04.04.20 - klo:10.00
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do

Kiitos vinkistä! Katsoin hakukoneella tuon kirjan hintaa, niin voisi harkita omaan kotikirjastoon tuohon kirjahyllylle muiden tietotekniikka - kirjojen jatkeeksi hommata tuon, kun hinta ei ole paha, kun Suomestakin tilattuna Adlibriksessä näyttäisi olevän hinta alle 40 euroa. Halpa tietotekniikka-kirjaksi, ja Shell - skriptaus kannattaisi ottaa haltuun, kun se on ajatonta tietokoneistusta liennyt jo sitä 1970-luvulta?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 14.04.20 - klo:11.35
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do

Kiitos vinkistä! Katsoin hakukoneella tuon kirjan hintaa, niin voisi harkita omaan kotikirjastoon tuohon kirjahyllylle muiden tietotekniikka - kirjojen jatkeeksi hommata tuon, kun hinta ei ole paha, kun Suomestakin tilattuna Adlibriksessä näyttäisi olevän hinta alle 40 euroa. Halpa tietotekniikka-kirjaksi, ja Shell - skriptaus kannattaisi ottaa haltuun, kun se on ajatonta tietokoneistusta liennyt jo sitä 1970-luvulta?

Näyttäisi olevan myös ilmaiseksi ladattavissa PDF-tiedostona: Poistettu Github-linkki. (--Tomin)
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Tomin - 14.04.20 - klo:13.09
Näyttäisi olevan myös ilmaiseksi ladattavissa PDF-tiedostona: Poistettu Github-linkki. (--Tomin)

Se, että netistä löytää kirjan PDF-tiedostona, vaikka sitten GitHub-repostakin ei vielä takaa, että se olisi ilmaiseksi jaossa. Tässä tapauksessa en löydä yhteyttä kirjoittajan tai kustantajan ja tuon GitHub käyttäjän tai repon väliltä, joten poistin linkin. Tuo kirja näkyy löytyvän netistä monestakin paikkaa, mutta kirjassa tai muussa löytämässäni lähteessä ei sanota, että se olisi vapaasti jaossa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 14.04.20 - klo:19.57
Näyttäisi olevan myös ilmaiseksi ladattavissa PDF-tiedostona: Poistettu Github-linkki. (--Tomin)

Se, että netistä löytää kirjan PDF-tiedostona, vaikka sitten GitHub-repostakin ei vielä takaa, että se olisi ilmaiseksi jaossa. Tässä tapauksessa en löydä yhteyttä kirjoittajan tai kustantajan ja tuon GitHub käyttäjän tai repon väliltä, joten poistin linkin. Tuo kirja näkyy löytyvän netistä monestakin paikkaa, mutta kirjassa tai muussa löytämässäni lähteessä ei sanota, että se olisi vapaasti jaossa.

Ok. Tämä kävi mielessä mutta jostain syystä luotin, että jos on githubissa jaossa niin olisi laillisesti jaossa. Aiheellinen linkin poisto siis ylläpidolta.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 07.05.20 - klo:20.16
Mikä olisi hyvä kirja opiskella bash-skriptausta? Varmaan suomeksi ei ole kirjoitettu, mutta kieli ei ole este.

Pitäisi ohjelmoida skripti, joka automatisoi käyttäjälogi-tiedostoista tiedoston poistoa, kun se on vanhenutunt tietyn aikaa, eli aika pitäisi tarkistaa, ja sitä mukaa skripti poistaa käyttäjälogi-tiedostosta rivin tai tietueen paremminkin virallista termiä käyttäen, kun ensin tarkistaa sen syntyajankohdan.

Olisi aikaa ja halukkuutta ottaa Bash-skriptaus tehokäyttöön haltuun, ja perinteinen kirja on paras tietolähteeni.
Pari hyvää (makuasia tietysti, mutta varsinkin tuota jälkimmäistä tulee välillä selattua esimerkkien takia):
Jos välttämättä haluaa hypistellä kuolleita puita, nuo saa varmaan jotain kautta tilattua tai ainakin printattua. Itelle on riittänyt että
aina tarvittaessa etin sivuilta ao. kohdan ja katon mihin järjestykseen ne välimerkit taas pitikään laittaa tässä tapauksessa.

Oikeet ohjelmat eli vähänkään monimutkaisemmat jutut teen perlillä tai pythonilla (kun meillä jokaisella on kuitenkin elinpäiviä rajallisesti).
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Whig - 17.05.20 - klo:10.49
Yritän putsata turhia tiedostoja NAS:lta pois ja nyt pääsin tylsimään eli .txt tiedostojen läpikäymiseen ja näitä näyttäisi kertyneen yhteen hakemistoon yli 1500kpl joita en ihan jokaista jaksaisi käydä läpi käsin.

Olisiko heittää jotain näppärää scriptin pätkää joka kävisi tiedostot läpi ja siirtäisi vaikka tiedostot joissa on esim. yksi sana/merki/merkkijono johonkin toiseen hakemistoon? Tai jotain muita scriptejä joista voisi olla apua tuon massan läpikäymisessä?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: retu - 26.05.20 - klo:23.25
Olisiko heittää jotain näppärää scriptin pätkää joka kävisi tiedostot läpi ja siirtäisi vaikka tiedostot joissa on esim. yksi sana/merki/merkkijono johonkin toiseen hakemistoon?
Yks mahollisuus:
Koodia: [Valitse]
grep -wilZ 'yksi sana\|merki\|merkkijono' *.txt |xargs -0 -I '{}' mv -v '{}' toinen\ hakemisto/Decryptattuna:
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 18.12.22 - klo:11.32
 Opetetaan päätteelle vähän laskentaa
 =======================
 
Lopetin jo skriptaamisesen. Mutta sitten kinttuni halvaantui ja luonnossa vaeltelu loppui ja täytyi aloittaa skriptaaminen uudelleen sillä onhan skriptaaminen  mielekkäämpää altzheimerin välttelyssä kuin palapelin kokoaminen. Mutta onhan tällä skriptillä vähäsen käytännönkin merkitystä: tämän avulla voi kerrata matikan perustaitoja.
 
Voit kirjoittaa päätteeseen minkä matemaattisen laskun tahansa melkein normaalein säännöin. Kaarisulut täytyy tosin kirjoittaa hakasulkuina. Skripti tuntee funktiot: sin, asin, sinh ... log, ln ... e, pi, ! (=kertoma) ja niinpoispäin. Ja teoriassa uusien funktioiden lisääminen on helppoa.

Kokeillaksesi näpäytä hiirellä oheista liitettä ja näpäytä sitten: pura. Kotikansioosi ilmestyy tiedosto laskija.tar.gz. Nåpäytäsitäja valitse 'pura tänne'. Anna sitten käsky:
. ~/laskija  (huomaa piste alussa, se on käsky)
- näennäisesti mitään ei saa tapahtua - mutta pääte osaa nyt laskea. Kokeile kirjoittamalla: 1+1 ja paina röttöstä. Esimerkkilaskuja ja ohjeita saat näytölle kirjoittamalla: laske. Leikkaa niitä esimerkkilaskuja sieltä ja liimaa kehoitteen perään niin saat esimakua.
- voit nyt poistaa tiedoston laskija.tar.gz
- koneelle ei koskaan tapahdu mitään ja kun sammutat päätteen niin myös laskentataidot katoavat.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 24.12.22 - klo:09.14
Netissä on puolikunnollisia moottoreita useimpien matemaattisten ongelmien ratkaisemiseksi mutta alkukuluvuille ei. Päätinkin kokeilla kuinka BASH tuosta alkulukujen etsinnästä suoriutuu tekemällä todella moninumeroisten alkulukujen etsintään sopivan skriptin - ja tulos olikin uskomattoman hyvä vaikka yli kolmekymmentä vuotta vanhoihin C-kielen toteuksiin tukeutuukin - nimittäin tämä skripti hoitaa BASH:illa vain pöytäkirjaa ja itse toiminta tapahtuu C-ssä. Silti skripti on ihan pelkkää BASH:ia ja toimii useimpien Ubuntu:jen pääteohjelmissa kun sen leikkaa täältä ja liimaa päätteeseen.

Koodia: [Valitse]
alku=123456789012345678901234567890123456789; echo; echo "Etsin alkulukuja luvun:$alku  perästä."; echo "löytösekunti        alkuluku"; pienet_alkuluvut=$(seq 2 2000 | factor | awk {' if ($3 =="") {print $1} '} | sed 's/://'); alkuaika=$(awk 'BEGIN {printf "%s\n", systime()}'); time for n in $(seq $alku $(bc<<<$alku+800)); do for apu in ${pienet_alkuluvut[*]}; do (( $(bc<<<$n%$apu)==0 )) && n=0 && break ; done; (( $n )) && echo -ne '\rTutkittavana: '$n && timeout 20 factor $n | awk -v alkuaika=$alkuaika {' if ($3 =="") {printf "\r%s\n", systime()-alkuaika"                  "$1}'} | sed 's/://'; done

Skripti toimii nopeasti monilla yli 70 numeroisillakin luvuilla mutta esimerkiksi jo niin pienestä luvusta kuin 140771709250109097948956177753846587 kestää puoliminuuttia selvitää onko se alkuluku vaikka luku on vain 119 bittinen. Tutkiakseni kuinka usein toiminta tökkii tein alkulukuja metsästävään skriptiin sellaisen muutoksen että kun laskenta kestää liian kauan niin luku merkitään ja siirrytään käsittelemään seuraavaa lukua - näintehden kaikkia alkulukuja ei saa kiinni mutta toiminta ei koskaan keskeydy kovin pitkäksi aikaa - ja kyllä niitä alkulukuja on myöhemminkin. Joten vain ne tulosteen rivit joilla on pelkkiä numeroita ovat alkulukuja - ne joissa on tekstiä ovat niitä jotka tökkivät eikä niistä voi varmuudella sanoa mitään - on kuitenkin hyvä tietää milloin sellainen on kohdattu. Lukujen suuretessa tökkiviä tulee aina vaan enemmän - laskut vaatisivat todella kovanluokan tietokonetta ja minulla on huono läppäri.

- lukua: alku voi muuttaa kuinka paljon vaan. Samoin sekunneissa olevaa timeout arvoa.
- jos skripti väittää jotakin alkuluvuksi pitää se paikkansa.
- toiminnan aikana tutkittava luku vaihtelee hiljokseen sillointällöin.
- skripti ei voi toimia luvuille alle 2000 toimintaperiaatteensa vuoksi.
- hidashan tämä on, mutta alkulukujen etsintämenetelmät ovat hitaita aina.
- Mersenne prime:ille on oma sivusto mutta meille tavallisille tallaajille se on ilmoitusluonteinen. Mutta Mersenne-primejä on harvassa ja useimmat ovat niin suuria etteivät ne meikäläisten koneisiin mahdu. Mutta se hyöty niistä on että joistain niistä saa alkulukuja etsivän skriptin toiminnalle jonkinlaisen "oikellisuustodistuksen" - esimerkiksi: factor $(bc<<<2^521-1 | tr -d '\\\n') |  tr -cd ' \t' | wc -c
täytyy tulostaa: 1 .
- 2^607-1 olisi seurava Mersenne-prime.

***

Kaikki ohjelmoijat soveltavat BASH:iin niitä menetelmiä joita muissa kielissä ovat oppineet käyttämään. Ei onnistu, sillä BASH on ihan erilainen.

Se kyllä uskotaan nopeasti että BASH:in find-käsky ei looppeja tarvitse vaikka se etsisi monilta kovalevyiltä jokaisen kovalevyn kaikki kolkat. Mutta kaikki BASH:in  käskyt ovat samanlaisia - niillä on sisäiset looppinsa joten ei niitä looppeja hitaassa BASH:issa kannata tehdä - paitsi hyvin harvoin semmoisia lyhyitä.

Esimerkiksi tehokkain silkkaa BASH:ia oleva etsintämenetelmä kun halutaan löytää jotain matriisista tai tekstijonosta: [[ etsintäkohde =~ etsittävä ]] ...   Funktion muotoon kirjoitettuna:

Koodia: [Valitse]
function etsi () { [[ "${@:2}" =~ "$1" ]] && echo löytyi || echo ei löytynyt ;};

Mutta käskyä ei tulla koskaan hyväksymään sillä siinä ei ole näkyviä looppeja ja niitähän toki täytyy kunnollisessa skriptissä olla - ja kun ei noita loopittomia hyväksy niin saa syyn jatkaa BASH:in haukkumista. Sillä kyse ei niinkään ole siitä että se on nopein tapa vaan siitä että se on virheettömin.

Eikä funktiossa tarvita looppia edes silloin kun etsittävä ei ole yksikäsitteinen vaan sekin vaatii käsittelyä jossakin ja lisäksi omissa loopeissaan joita niitäkään ei kirjoiteta. Esimerkiksi:

Koodia: [Valitse]
[[ $(cat /boot/grub/grub.cfg) =~ BEGIN.*[[:digit:]]?root ]] && echo jees

- mikähän tuo kysymysmerkki on? Lisäehto että millähyvänsä rivillä pitää olla sana root ? BASH pursuaa dokumentoimattomia ominaisuuksia

***

Maximin ja minimin etsiminen onnistuu myös melkein aina loopittomilla funktioilla - ja voi etsiä tekstijonosta tai matriisista, numeroita tai tekstiä.
Koodia: [Valitse]
function haemaksimi () { maksimi=$(echo $(sort -n <(echo "$@" | tr " " '\n' )) |  sed 's/.* //') ;}

functifon haeminimi () { minimi=$(echo $(sort -n <(echo "$@" | tr " " '\n' )) |  sed 's/ .*//') ;}

Testaamista: 100.000 matriisista hakuaika: 0.3 sekuntia
Koodia: [Valitse]
function haemaksimi () { maksimi=$(echo $(sort -n <(echo $@ | tr " " '\n' ) | tail -1)) ;}; time matriisi=($(seq 100000 | awk 'BEGIN{srand();}{print 1000000*rand()}')) ; time haemaksimi ${matriisi[*]}; echo $maksimi

function haeminimi () { minimi=$(echo $(sort -n <(echo $@ | tr " " '\n' ) | head -1)) ;}; time matriisi=($(seq 100000 | awk 'BEGIN{srand();}{print 1000000*rand()}')) ; time haeminimi ${matriisi[*]}; echo $minimi


function etsi () { [[ "${@:2}" =~ "$1" ]] && echo löytyi || echo ei löytynyt ;}; time etsi 555 $(seq 1 1000000)


- sorttauksen parametreja joutuu joskus säätämään vaikka yleensä onnistuu jo näin.
- miljoonan numeron matriisilla toiminta-nopeus maximi/minimissä oli 3.8 sekuntia ja etsimisessä 2.0 sekuntia.
- miljoonan muuttujan matriisi siksi että voi olla varma että vain muistimäärä rajoittaa ja muuta omituista ei tule.

***

Teinpä vuoden viimeisenä iltana käskyn: foreach kun se BASH:ista puuttuu. Nimenomaan sen matemaattisen version joka muuttaa muistissa olevan matriisin jokaisen jäsenen arvon niiden ohjeiden mukaan jotka erikseen määrätään.
- tällähetkellä nuo käsittely ohjeet annetaan foreach:in koodissa - mutta voi ne omaan funktioonsakin laittaa.
- matriisin jäsenet tosiaan muuttuvat.
- kun funktion koodin laittaa skriptin alkuun on käsky käytettävissä skriptissä missävain, vaikka montakertaa.
- täytyy tarkentaa: tämä on edelleen BASH:ia.

Käsky on: foreach matriisin_nimi
Koodia: [Valitse]
foreach () { name=$1; name=($(declare -p | grep 'a '$name | grep -Po \".*?\" | tr -d \" | tr '\n' " " )); for n in ${name[@]}; do eval $1[$n-1]=$(($1[$n-1]**3)); done ;}; apu=({1..1000}); time foreach apu; printf "%s\n" "${apu[@]}"

- jos matriisin jäsenissä on desimaalipiste tai käsittely on monimutkaisempaa täytyy käyttää seuraavaa - mutta se on paljon hitaampi.
Koodia: [Valitse]
foreach () { name=$1; name=($(declare -p | grep 'a '$name | grep -Po \".*?\" | tr -d \" | tr '\n' " " )); for n in ${name[@]}; do eval $1[$n-1]=$(bc -l<<<"${name[$n-1]}^-4"); done ;}; apu=({1..100}); time foreach apu ; printf "%s\n" "${apu[@]}"

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 02.01.23 - klo:18.03
Aina tuodaan esiin se että eihän BASH tunne desimaalilukujakaan. Niinhän se tarkkaan ottaen onkin - tai tiedä tuosta tarkkuudesta sillä ihan BASH-koodillakin desimaalimatematiikka on erittäin nopeaa joskin kovin rajoittunutta. Luulossa on kymys halusta ymmärtää väärin - ei desimaalimatematiikka desimaalilukuja kaipaa mutta sitähän ei ole pakko ymmärtää sillä vain BASH laskee desimaalilaskut koodissa - ja se on todella hidasta. Toisaalta oikein valittu BASH-koodi on hirvittävän paljon nopeampaa kuin mitä annetaan ymmärtää.

Eihän näillä matikka-koodeilla enää ole mitään merkitystä paitsi että ne osoittavat että *kaikki* mitä BASH:ista puhutaan on ollut ja on edelleenkin puhetta vastoin parempaa tietoa.

Esimerkiksi lasku: 1233457890123.23 / .123456 kestää BASH:in koodissa 0.4 ms - tosin lasku kolkutteli koodin kykyjen rajoja - toisaalta olisi mahdollista tehdä lisäyksiä lisänumeroiden laskentaan sen vaikuttamatta paljoakaan suoritusaikaan - ja muista matematikka-ohjelmista jo bc:kin vaatii kymmenkertaisen ajan jo herätäkseen toimimaan - ja niin kestää muutkin.

- ei tuota jakolaskua muiksi perustoimituksiksi saa vaan ne vaativat omaa koodia.
- huomioi muuten ettei koodissa ole looppeja.
Koodia: [Valitse]

#!/bin/bash
function jaa () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
# (( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"0000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"0000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}

kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
 
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
kokonaiset=$kokonaiset.${desimaalit: -9}
# echo $jakojaannos

jakojaannos=$(((100000000*$jakojaannos)%$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=0000000000000000$desimaalit
echo $kokonaiset${desimaalit: -8} ;}
   

# tarkistus:
jaa 1233457890123.23 .123456

***

BASH:ia on hidasta kirjoittaa kirjoitus-sääntöjensä takia, se toimiikin hitaasti ja kaikessa on kummallisia rajoituksia. Skriptikielet bc ja awk ovat nopeita kirjoittaa sillä niiden kirjoitus-säännöt ovat samat kuin normaaleillakin kielillä. Lisäksi ne toimivat nopeasti verrattuna mihin hyvänsä, eikä rajoituksia juurikaan ole.
 
Esimerkiksi skripti fibonacci-sarjan 100.000:nennen jäsenen laskemiseksi:
- idioottimaisen mones jäsen hölmöstä sarjasta siksi että näkyisi selvästi kuinka helppoja, nopeita ja rajoituksettomia nämä kielet ovat - lasku kestää hitaimmalla näistä 8 sekuntia ja nopeimmalla .08 sekuntia.  Näiden yli 20.000 numeroisien lukujen laskeminen ei taida onnistua muilla kielillä yhtä nopeasti - tai C:tä nämäkin ovat joten kai C:llä onnistuisi - käyttäen kirjastoa. Tässä ainoastaan tuo gawk käyttää kirjastoa.
Koodia: [Valitse]
echo  "fibo=100000;a[1]=a[2]=1;for (i=3; i<=fibo; i+=1) a[i]=a[i-1]+a[i-2]; print a[fibo]" | bc | tr -d '\\\n'

Sen muuttaminen kymmenen kertaa nopeammaksi gawk-skriptiksi on aivan yksiviivaista:
Koodia: [Valitse]
gawk -M 'BEGIN{fibo=100000;a[1]=a[2]=1;for (i=3; i<=fibo; i+=1) a[i]=a[i-1]+a[i-2]; print a[fibo]'}

Seuraava on jonkun virtuoosin tekemä skripti joka on vielä kymmenen kertaa nopeampi (0.08sek) - mutta tätä ei edes ymmärrä - se menee lisäksi ainakin kymmenmiljoonanteen jäseneen eikä silloinkaan käytä muistia havaittavaa määrää joten ei se taida välituloksia laskeakaan. Mutta olipa kyse mistähyvänsä niin bc kykenee siihen:
Koodia: [Valitse]
echo 'n=100000;m=(n+1)/2;a=0;b=1;i=0;while(m){e[i++]=m%2;m/=2};while(i--){c=a*a;a=c+2*a*b;b=c+b*b;if(e[i]){t=a;a+=b;b=t}};if(n%2)a*a+b*b;if(!n%2)a*(a+2*b)' | bc | tr -d '\\\n'

- silti BASH on erinomainen liimaamaan kaikkia skriptikieliä yhteen joten kyllä sitä kannattaa käyttää.

***

BASH:in muuttujiin voi laittaa mitähyvänsä: muuttujia, tekstiä, tekstijonoja tai matriiseja - kaikkia numeroina tai tekstinä, ilman minkäänsortin tyyppimäärittelyjä - ja kaikkia voi käsitellä joskus numeroina ja samassa yhteydessä välillä myös tekstinä. Ja niihin voi tallettaa myös käskyjä - ja ne toimivat muuttujastakin kuten käskyt. Esimerkiksi:

Kun tekee vaikeaa skriptiä kannattaa koodissa tulostaa runsaasti välituloksia - se on paljon parempi keino kuin mikään debug:gaus voi olla. Ongelmana on että valmiissa skriptissä välituloksien tulostaminen tekee mahdottomaksi tehdä skriptistä kirjasto-funktiota - ja sotkeehan se muutenkin kauniin lopputuloksen. Ja jos välituloksia  tehdään paljon niin niiden poistamisessa valmiista skriptistä on iso työ - ja paljon isompi työ on palauttaa ne jos myöhemmin huomaa ettei koodi aina toimikaan ja täytyy selvittää missä menee pieleen.

BASH:issa voidaan määrätä yhdellä muuttujalla tulostetaanko vai ei - koodin paljoakaan muuttumatta - tulostuksissa vaan korvataan echo sanalla $tulosta. Kun välituloksia halutaan niin koodin alkuun kirjoitetaan: tulosta=echo ja kun tulostusta ei haluta niin vaihdetaan sana echo sanaksi :  (=kaksoispiste). Ja jos myöhemmin havaitsee että kyllä niitä välituloksia taas tarvittaisiin niin se : muutetaan takaisin echo:ksi.

- esimerkki: tulosta=echo; $tulosta 'näin tämä käy' tai: tulosta=: ; $tulosta 'näin tämä käy'
- jos lopputulos täytyy tulostaa niin se tulostetaan aina echo:lla.
- eikä tällainen tulostustapa edes tee koodista yhtään epäselvempää.

***

Kaikki ohjelmointikielet tarvitsevat kirjastoja ja ne ovat BASH:inkin ydin. Mutta tätä ei skriptaajille kerrota - vaan päinvastoin vietiin BASH:ilta kirjasto-osoitin mikä tekee kirjastojen käyttämisen vaikeaksi. Mutta ei tuo kirjastojen "käyttökielto" kehittäjiä koske ja niinpä koneessasikin on kirjaston alku elikä noin 85 funktiota - ne helpottavat heidän hommiaan ja skriptaajat hypätköön sementtiin.

Jokatapauksessa saat nuo 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.

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

Mutta jotkut kirjastofunktiot ovat pitkiä ja loogisestikin monimutkaisia - ja varsinkin BASH tarvitsee niitä sillä BASH:ia ei opeteta - tosiaan ilmeisesti tarkoituksella. Noista kirjastofunktioista saisi edes esimerkin siitä miten skriptit tehdään.

Kirjaston skriptien lukumäärä on viite skriptikielen laadusta. Niinpä funktioita   tarvittaisiin rajattomasti - tosin kirjaston ylläpitäminen vaikeutuu kirjaston koon kasvaessa.

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

***

Vakaan skriptin tekeminen ilman kirjaston apua on melko mahdotonta ja niiden puutteessa BASH kituu. 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. Funktiot ovat usein työläitä kirjoittaa, mutta yleensä ne kirjoittaakin joku toinen - ja jos joudut itse kirjoittamaan funktioita niin ne kirjoitetaan vain kerran 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.

***

Toisten tekemillä funktioilla selviää pitkään mutta olisihan se kiva oppia tekemään niitä itsekin: funktio on muodoltaan: 

function funktion_nimi () { toiminta ;};

- sana function voi jättää poiskin, se on vain muistutus skriptaajalle.
- funktiota käytetään aivan samoin kuin skriptiäkin - elikä mainitsemalla sen nimi - ja myös parametrit toimivat kummillakin samallatavalla.
- funktiot voivat kutsua toisiaan.
- () on merkkinä siitä  että funktiossa tullaan käyttämään parametreja - parametri on tavallinen muuttuja joka siirtää tietoalkion kutsujasta funktioon - joko arvon tai nimen. Yhdessa parametrissa ei voi olle matriisia tai tekstijonoa vaan niiden jokaisesta arvosta tulee oma parametri joista ne voi koota uudelleen. BASH:issa parametrit eivät koskaan palaa.
- { } tarkoittaa sitä että funktion ja pääohjelma toimivat samalla alueella joten esimerkiksi niiden muuttujat ovat yhteiset. Muutettaessa noiden aaltosulkujen tilalle kaarisulut yhteys katkeaa.
- puolipisteet ovat mukana vain nostamassa skriptaajan verenpainetta?
- toiminta on normaalia skriptiä mutta esimerkiksi sen muuttujilla on sääntönsä:
  - funktio ja sen kutsuja tuntevat toistensa muuttujat.
  - funktio  voi määrätä omia muuttujiaan yksityiseksi määreellä: local eikä kutsuja silloin tunne niitä.
  - kovalevyn tiedostojärjestelmä on funktiossa ihan sama kuin kutsujassakin ja viestejä voi vaihtaa myös kovalevyn kautta.
  - funktiot tuntevat myös toisten funktioiden muuttujat. Esimerkiksi:
   
   function koe1 () {  a=33 ;}; function koe2 () { echo $a ;} ; koe1; koe2
   
  - kovalevyn tiedostojärjestelmä on funktiossa ihan sama kuin kutsujassakin joten viestejä voi vaihtaa myös kovalevyn kautta.
 
***   
     
- rekursio on funktio joka kutsuu itseään. Jottei rekursio kutsuisi itseään loputtomasti täytyy rekursion alussa olla käsky milloin lopetetaan. Esimerkiksi kertoma:
Koodia: [Valitse]
function kertoma () { i=$1; (( $i > 1 ))  && { kapu=$kapu$i'*'; i=$(($i-1)); kertoma $i ;} || { echo $kapu'1' | bc | tr -d '\\\n' ;} ;}; read -p 'mistä luvusta se kertoma lasketaan: ' x; kertoma $x
   - rekursio on resurssisyöppö ja hidas ohjelmarakenne eikä varsinkaan pitkiä rekursioita kannata käyttää.

***

Funktiota kutsuttaessa siis siirretään funktioon myös ne muuttujat joita funktiossa olisi määrä käsitellä. Funktio ei tiedä mitä sille siirretään vaan numeroi kaiken tulojärjestyksessä ja laittaa jokaisen numeron eteen merkin $

Muuttujista voidaan siirtää joko:
1. arvo joka ei ole sidoksissa mihinkään nimeen - eikä arvoa enää siinävaiheessa voikaan sitoa mihinkään nimeen. Matriisin jokainen alkio siirtyy erikseen - samoin tekstijono jaetaan aina välilyönnin kohdalta - siten eri matriisit ja tekstijonot vaativat aina tapauskohtaisesti erilaiset määrät parametreja. Senvuoksi tekstijonoja ja matriiseja voidaan siirtää vain yksi ja se täytyy siirtää viimeiseksi.
 
Tällaisen parametrin nimi on arvoparametri. Parametreja ei voi palauttaa ja arvoparametrien suhteen parametri täytyisi joskus palauttaa. Käytetyin keino palauttaa funktiosta jotakin on se että funktio kirjoittaa palautettavan näytölle ja pääohjelma lukee sen näytöltä ohjelmarakenteella: $( funktiokutsu parametreineen) - silloin se tulostettu ei muuten koskaan fyysiselle näytölle ilmestykään. Koko parametrijoukkoon voidaan viitata: $@. Koska matriisin tai tekstijonon edessä tulee yleensä pari tavallisen muuttujan parametria niin niiden jäseniin viitataan: ${@:2}

2. nimi joka ei ole sidoksissa mihinkään arvoon tai arvojoukkoon. Tällaisen parametrin nimi on nimiparametri. Nimi voidaan sitoa siihen arvoon joka sillä nimellä on - monellakin tavalla:
a. Eval-käskyllä. Itseasiassa tämä toimii aivan samoin kuin seuraava, eval on siitä tehty funktio?
b. BASH antaa osan kirjanpidostaan skriptaajan käyttöön käskyllä: declare -p . Kirjanpidosta etsitään kohta jossa saadusta muuttuja-nimestä puhutaan ja liitetään sieltä arvot nimeen.
c. aritmeettinen muuttuja let-käskyllä: function koe () { let $1=5 ;}; a=1; koe a; echo $a
d. tekstimuuttuja: function koe () { read $1<<<"kissa kuumalla katolla" ;}; a=d; koe a; echo "$a"
- toki tämä liittäminen on lisähomma eikä kaunista koodia mutta ei se aina ole yksinomaan haitta.
- nimiparametreja ei tarvitse palauttaa sillä on muokattu sitä alkuperäistä muuttujaa joten se on muuttunut jo.
- semmoinen pikkujuttu tässä tosin on että oletetaan ettei skriptissä ole toista  samannimistä muuttujaa - jopa toisten skriptien muuttujat voivat teoriassa sotkea.

Matriiseja ja tekstijonoja siirrettäessä on suuri merkitys sillä millainen parametrin tyyppi on:
1. suurilla matriiseilla ja tekstijonoilla arvojen siirtäminen vie aikaa.
2. nimellä voi siirtää niin monta matriisia tai tekstijonoa kun haluaa ja ihan siinä järjestyksessä kuin haluaa - ja siirron nopeus on aina suuri.
 
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 16.01.23 - klo:02.54
Muuttujat ja lyhyet tekstijonot kannattaa yleensä siirtää entiseen malliin arvoparametreina - mutta tässä esimerkit että kyllä nekin toimivat nimiparametreina:
Koodia: [Valitse]
koe () { let $1=$((2*$(declare -p $1 | cut -f2 -d '"'))) ;}; a=1; koe a; echo $a

koe () { eval $1=$((2*$1+55)) ;};a=1; koe a; echo $a

***

Pitemmät tekstijonot ja matriisit kannattaa yleensä siirtää nimiparametreina:
Koodia: [Valitse]
function jonomax (){ declare -p $1 | cut -f2 -d '"' | tr " " '\n' | sort -n | tail -1  ;}; jono="1 2 3 4 5 4 3 2 1"; jonomax jono

Tai jos välttämättä haluaa käyttää eval-käskyä:
function jonomax (){ eval echo \${$1} | tr ' ' '\n' | sort -n | tail -1 ;}; jono="1 2 3 4 5 4 3 2 1"; jonomax jono

***

matriisi esimerkkejä:
Koodia: [Valitse]
function matmax () { declare -p $1 | tr = '\n' | sed 's/\[.*//g' | tr -d \" | tr -d \) | sed 1,2d | sort -n | tail -1 ;}; unset mat; mat=({1..999999}); time matmax mat
- muuten etsiminen joukosta:mat=({9..0}{0..9}{9..0}{9..0}{0..9}{9..0}) kestää suurinpiirtein yhtäkauan vaikka siinä  totisesti on sorttaamista - mutta itse sorttaus tehdäänkin C-kielisissä rutiineissa eikä se koskaan kauaa kestä. Se mikä kestää on siirtää BASH:in muuttujat sort-käskyn matriiseihin.
- silti sorttaus saattaa kestää ensimmäisellä kerralla kauan koska Linux pistää buffereitaan kuntoon?

Seuraavaksi koodi joka säilyttää BASH:in matriisin sparse ominaisuuden:
- sparse -> matriisin jokaisella jäsenellä on arvo ja osoite. Niillä matriiseilla joita normaalisti käytetään osoitteet alkavat nollasta tai ykkösestä ja siitä eteenpäin osoite kasvaa aina yhdellä. Mutta sparse-tyyppisellä matriisilla osoite voi olla mikävaan kokonaisluku. Assosiatiivisella matriisilla osoite voi olla myös teksti - siis vaikkapa desimaalinumero.
Koodia: [Valitse]

function matmax (){ name=$1; eval name='('$(declare -p $name  | sed -s 's/declare.*=(//'); echo -e ${name[*]/#/\\n} ;};mat=({999..1}); matmax mat

tai maximin etsintä eval-käskyn kanssa:
Koodia: [Valitse]
function matmax (){ eval echo \${$1[*]} | tr ' ' '\n' | sort -n | tail -1 ;}; mat=({999..1}); matmax mat

Tai siirto kovalevyn kautta:
Koodia: [Valitse]
function matmin (){ echo $(cat $1 | sort -n | tail -1)>$1 ;}; a=({1..999}); echo -e "${a[@]/#/\\n}" > ~/delme; time matmin ~/delme; cat ~/delme
- ramdisk kyllä nopeuttaisi ... mutta edellyttäisi sudoa.  luonti:
mount -t tmpfs -o size=500m tmpfs /mountpoint
     
Tai awk-versio. Esim. kun tarvitaan nopeutta mutta esitysmuoto on joskus tieteellinen:
Koodia: [Valitse]
function matmax () { cat $1 | tr " " '\n' | awk 'BEGIN {maksimi=-9e99} { if ($1>maksimi) maksimi=$1 } END { print maksimi }' ;}; a=({999999..1}); echo -e ${a[*]/#/\\n}  > delme; time matmax delme

Tai bc versio joka saa selvää ratkaisemattomista sineistä ja mitä nyt bc osaakaan:
Koodia: [Valitse]
function keskiarvo() { count=$(echo $1 | wc -w); echo $(echo '('$1')/'$count | sed 's/[eE]/*10^/g' | tr '[] ' '()+' | bc -l);}; keskiarvo 'c[1] s[1] 1e3'

- hakasulkuja on pakko käyttää sillä BASH tulkki menee kaarisuluista solmuun.

***

Tässävaiheessa alkaa funktioiden merkitys korostua: eihän erkkikään tämmöisiä litanioita viitsi kirjoittaa - kyllä nuo sotkut täytyy lykätä kirjastoon niinkuin kaikki muutkin kielet ovat tehneet. Ja pahemmaksi muuttuu. Vai mitäpä sanoisit seuraavasta funktiosta:
Koodia: [Valitse]
function onkoekasuurempi () { ((10#0${1%.*} ^ 10#0${2%.*})) || (( ${1//./} < ${2//./} )) && echo 0 || echo 1 ;}; onkoekasuurempi 8.127 8.126
Tällä funktiolla on etunsa mutta se on kaukana moitteettomasta. Lisää kummallista risuaitaa ja alkaa toimia paremmin. Pahimmillaan neljä riviä samankaltaista merkkien sotkua olevia nopeita funktioita on tehty moniakin mutta on täysin varmaa on ettei niitä käytetä jos ne eivät ole kirjastossa funktioina - ja kirjastoa jaettu muille jottei se häviäisi kun tekijä häviää. Koska kirjastojen käyttöä ei harkitakaan niin voi edelleen kertoa vakaana mielipiteenään että BASH on suunnattoman hidas eikä osaakaan mitään.

Kunnollisempi mutta hidas reaalilukujen testaus on kahdesta funktiosta tehty - ehkäpä kummastakin saisi tehtyä nopean risuaita-verion?:
Koodia: [Valitse]
function onkoekasuurempi () { kokonaiset1=$(echo $1 | cut -d. -f1 ); kokonaiset2=$(echo $2 | cut -d. -f1 ); (( $kokonaiset1==$kokonaiset2 )) && onkoekandesimaalitsuuremmat $1 $2  || { (( $kokonaiset1<=$kokonaiset2 )) && echo 0 || echo 1 ;};};

function onkoekandesimaalitsuuremmat () { desimaalit1=$(echo $1 | cut -d. -f2 ); desimaalit2=$(echo $2 | cut -d. -f2 ); (( $desimaalit1<=$desimaalit2 )) && echo 0 || echo 1 ;}; time onkoekasuurempi 8.3 8.2
Ja mitähän kaikkea vielä joutuisi lisäämään ennenkuin toiminta olisi moitteeton?

***

- muuten ei näistä esimerkeistäni ole yhdestäkään tarvinnut tehdä skriptiä - riittää kun leikkaa-liimaa ne täältä foorumilta päätteeseen.

Funktiokutsuista on tehty nopeita. Nopeuden saavuttamiseksi funktio suoritetaan samassa prosessissa kuin kutsujakin - joten kaikki on yhteistä. Näin tehdään sillä uuden prosessin luominen on hidasta. Toinen juttu on että onhan BASH muuten hidas.

Ja kaikenlaisiin asioihin voi tehdä funktion. Jopa loopin askelista voi muodostaa funktion avulla levytiedoston:
Koodia: [Valitse]
function looppimatriisi () { bc<<<"for (i=1.000000; i<=10; i+=0.02) i">delme ;}; looppimatriisi
- siis looppimatriisissa voi olla myös desimaalilukuja eikä lukujen desimaalien määrällä ole mitään rajaa. Eikä muuten ole rajoituksia kokonaisosallakaan. Tieteellisestä esitysmuodostakaan ei ole pelkoa.
- vain pieni muutos niin steppaus on exponentiaalinen: 'i+=' muutetaan: 'i*=', huomioi vaan silloin että kertoimen pitää aina olla suurempi kuin yksi.
- mikähyvänsä muukin muutotapa on mahdollinen - neliöjuurellinen, logaritminen ...
- alku- , loppu- ja askel-arvot voidaan siirtää looppimatriisiin parametreina.
  esimerkiksi:
Koodia: [Valitse]
function looppimatriisi () { bc -l<<<"for (i=$1; i<=$2; i*=$3) i" | cut -c -$4 ;}
looppimatriisi 1 1000000 1.05 8  # matriisin jäseniin tulee korkeintaan 8 numeroa

***
 
Matemaattisen kaavan ratkaiseminen muuttujan vaihdellessa onnistuu sekin - esimerkiksi voi helposti muodostaa matriisin funktion graafista kuvausta varten. Esimerkkinä päässälasku jotta toiminta selviäisi:
Koodia: [Valitse]
function ratkaisija () { echo $1 | tr '[]' '()' | sed 's/x/'$2'/g' | bc -l ;};
a=x^2+x+1; ratkaisija $a 2   # kaavaksi voi vaihtaa mitä lystäät
- siis ratkaistava kaava kirjoitetaan muuttujaan ja funktion avulla ratkaistaan sen arvo tuntemattoman annetulla arvolla. Koska mukana on bc ja sen matematiikkakirjasto onnistuvat monimutkaisetkin laskut - tosin bc:n funktiot merkitään vähän kummallisesti.

***

- koska seuraavassa piiretään niin saattaa joutua lataamaan: sudo apt install gnuplot

Laitetaanpa pari edellisistä skripteistä yhteen ja esitetään tulos graafisesti - skriptin suorituksen jälkeen näytölle pitää tulla pieni ikkuna nimeltään gnuplot ja siinä jakso sinikäyrää - jotta varmistuisi että kaikki pelaa (kopioi kaikki rivit kerralla):
Koodia: [Valitse]
function ratkaisija () { echo $1 | tr '[]' '()' | sed 's/x/'$2'/g' | bc -l ;}
a=s[x]  # bc laskee sinin näin - radiaaneissa muuten. Sini on valittu koska jokainen tietää mitä pitäisi tulla. Mutta voit muuttaa kaavan haluamaksesi sillä gnuplot muuttaa asetuksensa oikeiksi automaattisesti - jollei erikseen toisin määrätä.

function looppimatriisi () { apu=$(bc<<<"for (i=0; i<=6.29; i+=0.01) i") ;}; looppimatriisi

echo''>/tmp/delme; for n in ${apu[@]}; do echo $n' '$(ratkaisija $a $n)>>/tmp/delme; done

gnuplot -p -e 'set terminal postscript eps color enhanced; set xlabel "muuttujan arvo"; set ylabel "funktion arvo"; set terminal x11; set output "/tmp/transpose.eps"; plot "/tmp/delme"'

***

Funktion nimen voi passata parametrina - ja silläkin voi olla parametrinsa mutta parametrinumerot kyllä muutuvat määränpäässä (ihan normaali epäsuora viittaus?):
Koodia: [Valitse]
function luuppi () { apu=$(bc<<<"for (i=$1; i<=$2; i+=$3) i") ;};

function koe () { echo "täältä mennään funktioon: $1"; $1 $2 $3 $4 ;}

koe luuppi 1 10 1; echo $apu

***

Itse funktionkin voi passata parametrina? - kunhan siinä ei ole kovia lainausmerkkejä tai välilyöntejä joita ei voi pehmeillä lainausmerkeillä suojata (toimii silloinkin jos alkaa leikkiä IFS:n kanssa):
Koodia: [Valitse]
function loppi () { apu=$($1) ;}
loppi bc<<<"for (i=2; i<=20; i+=2) i"; echo $apu 

- bc<<<"for (i=2; i<=20; i+=2) i" siirtyy tosiaan semmoisenaan sillä ratkaistunahan siinä olisi välilyöntejä ja silloin siirtyisi vain ensimmäinen numero?
- ja: function loppi () { echo "$1"; apu=$($1) ;} tulostaa lisäksi: bc niinkuin: echo bc<<<"for (i=2; i<=20; i+=2) i" tulostaakin.

***

- BASH:issa on jonkinlainen 'käsky kerrallaan askellus' - eihän se kovin hyvä ole, mutta sen saa skriptissä päälle ja pois: ennen kohtaa jossa arvelee vian olevan lisää koodiin:
set -x; trap "echo paina: return;read x" DEBUG
jolloin se alkaa askeltamaan käsky kerrallaan kirjoittaen muuttuneiden muuttujien arvot.
Vikakohdan tutkimisen jälkeen täytyy lisätä koodiin:
set +x; trap "" DEBUG
jolloin toiminta palaa nomaaliksi.

***

- enpä tiedä onko se hyvä tai paha mutta BASH totisesti pitää varpaillaan - kun loppusiloittelet skriptiäsi niin ei tosiaan tiedä mihin päätyy - pieni moka ja korjaat sitä loppupäivän - koska aina muulloin tallettaa jokaisen välituloksen mutta siinävaiheessa ahneus iskee ja siistimisyritys tuhoaa koko homman.

- aikoinaan havaitessani että BASH:ista halutaan eroon aloin tuskailla siitä toimiiko BASH enää seuraavassa versiossa. Eipä taida olla pelkoa BASH:in menettämisestä - se toimii uudessa UBUNTU:ssakin - ja tulevissakin. Jopa hiljokseen kehittyenkin - tällähetkellä on meneillään versio 5.2.

- enpä tiedä kuinka epäsuosittua BASH on maailmalla - lukuunottamatta sitä ettei se henkiheitto vielä ole ja että BASH-skriptaajia palkataan vielä. Mutta jostainsyystä käyttäjät eivät BASH:ia arvosta.

- sensijaan Wintoosa lisäsi BASH:in itseensä - sillä paha vihollinen tuhotaan sisältäpäin? Myös hakkerit käyttävät BASH:ia - sitä ei ole huomattu että BASH:illa ne pahikset voisi tuhotakin.

***

Aikoinaan kun BASH tehtiin sen merkintätavat olivat aluksi toisesta maailmasta mutta se oli paljonkin nopeampi kuin nykyinen. Mutta sen merkintätavat olivat liian kummallisia joten käskykantan tehtiin uusia paljon käyttäjäystävällisempiä käskyjä. Mutta siinä menetettiin nopeutta.
 
Mutta ei siihenaikaan nopeutta arvostettukaan yli kaiken joten oltiin tilanteeseen tyytyväisiä ja hiljokseen ne vanhat risuaidat unohdettiin - mutta kyllä ne edelleen toimivat jos niitä tietää käyttää. Nyt kun on tullut tarve nopeuteen niin sitä risuaitojen nopeutta kaivattaisiin mutta niistä ei oikein tiedetä paljoakaan - tai kerrotaanhan niistä vieläkin joillain verkkosivuilla kummallisina muinaismuistoina.

***

BASH on tulkattu kieli joten sen hitaus on osin todellista - mutta osaksi tehtyä. Esimerkiksi käytetään sellaisia käskyjä että ne pakottavat tulkkin tulkkaamaan jatkuvasti ja sehän on tosihidasta.

Nimittäin BASH:illa on suuri joukko käskyjä joiden kirjoitusasu on niin kummallinen että ne kieltämättä nyrjäytävät aivot koska ei sellaisia ole tottunut kirjoittamaan. Ne ovat kuitenkin nopeita mutta ennenkaikkea ne ovat tulkki-ystävällisiä: kääntämisessä on käytössä tismalleen 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.

Tai asian voi esittää näinkin: ne alkuperäiset käskyt olivat tarkoitettu matematiikkaan ja tekstin sana kerrallaan käsittelemiseen. Sitten aloitettiin tiedostojen käsittely ja sehän vatii käskyihin sisäisiä matriiseja ja looppeja - joten käskyjen koko kasvoi paljon, tulkkaaminen hidastui eivätkä ne cache:ihinkaan oikein mahtuneet. Ja matematiikka unohdettiin melkein kokonaan.

Joten taas kerran muistelin noiden risuaita-käskyjen nopeutta ja matemaattisia kykyjä ja muutin erään surkean hitaan normaaleilla käskyillä tehdyn vertailu-funktion noilla vanhoilla risuaidoilla tehtyyn ja siitä tuli huonoimmillaankin yli viisikertaa nopeampi kuin mitä millään muulla menetelmällä aikaansaa - ja normaaleja pieniä lukuja verratessaan 50 kertaa nopeampi.

***

Sitten itse skripti:

- ei tällaisilla yksittäisillä  nopeilla skripteillä juurikaan ole käyttöä ennenkuin saa sen kavereiksi toisia nopeita - mutta kenties noista matematiikka-skripteistä saa kavereita ajankanssa. Sitäpaitsi nämä risuaitakäskyt osaavat tekstinkäsittelyäkin - tosin vai sanoilla. Toimitaanko kaikessa juuri niin kuin ei pitäisi?

- kone toteaa luvut teoriassa samoiksi vain mikäli ne kirjoitetaan samoin. Mutta ulkonäkö voi voi olla erilainen vaikka luvut ovat samoja. Skriptin tätyykin ymmärtää että esimerkiksi(2 on vain esimerkkinä):
   2.0 on sama kuin 2
  +2.0 on sama kuin 2
  .2 on sama kuin 0.2
 -.2 on sama kuin -0.2
 
- BASH-tulkki hyväksyy matemaattisessa vertailussa rajattomasti numeroita mutta leikkaa edestä pois ne numerot jotka menevät 19 merkin yli - kohtelu on sama niin kokonais-kuin desimaaliosassakin.

- desimaaliluku on sama kuin kaksi kokonaislukua joiden välissä on piste. Siis vertaillaan ensin kokonaisosia ja jos ne eivät ole samoja niin vertailu suoritetaan heti - ja desimaaleista välittämättä.

Mutta jos kokonaisosat ovat samat niin  sitten vertaillaan desimaaliosia. Mutta desimaaliosia ei voi vertailla samoin kuin kokonaislukuja elleivät ne ole yhtäpitkiä. Tässä tuo 'yhtäpituus' on saavutettu liittämällä vertailtavat yhteen: lukuarvot menevät poskelleen mutta eivät niin että se vertailua haittaisi. Samoin täytyy huomioida kokonaisosien etumerkki:

***

Koodia: [Valitse]
function looppimatriisi () { apu=$(bc<<<"for (i=1; i<=10000; i+=1) i") ;}; looppimatriisi

function onkoekasuurempi () { koko1=${1%%.*};koko1=${koko1//-/-0};koko1=${koko1:=0};koko2=${2%%.*};koko2=${koko2//-/-0};koko2=${koko2:=0}; (( $koko1==$koko2 )) && onkoekandesimaalitsuuremmat $1 $2  || { (( $koko1<=$koko2 )) && echo 0 || echo 1 ;};}
                                           
function onkoekandesimaalitsuuremmat () { 
[[ $1 =~ \. ]] && desi1=$1'00' || desi1=$1.00; [[ $2 =~ \. ]] && desi2=$2'00' || desi2=$2.00; #echo $desi1' '$desi2

desi1=${1//[^-]/}$desi1; desi2=${2//[^-]/}$desi2; #echo $desi1' '$desi2
desi1=${1//[^-]/}${desi1##*.}; des1=$desi1; desi2=${2//[^-]/}${desi2##*.}; des2=$desi2
#echo $desi1' '$desi2

# kummankin perään liitetään toinen
desi1=$desi1$des2; desi2=$desi2$des1; #echo $desi1' '$desi2

(( $des1>$des2 )) && echo 1 || echo 0 ;}    # kestoaika: ~60 mikrosekuntia/vertailu

# ja koekutsu
time { for n in ${apu[@]}; do onkoekasuurempi -2223 -2222.99999 ; done ;}


- kun nopeuden mittaa tuollatavoin saa varmasti mielikuvan liian suuresta nopeudesta, mutta saa sentään jonkinlaisen mielikuvan.

***

Tämä BASH on sikälikin mukava ettei yksikään skripti ole koskaan valmis ja virheetön - se ei tosiaankaan ole masentavaa koska se on jo etukäteen täysin varmaa ja niin käy kaikkien tekemille skripteille. Joten kun aivoni eivät tuottaneet viikkoon mitään niin vanhoissa skripteissä oli korjattavaa. Ja nopeasti kertolaskun kaksois-tarkkuuden skriptin tarkkuus nousi 33 numerosta 38:aan ja logaritmilaskuissa numeroita alkoi tulla kymmenen sijaan 19.

***

Aloin kokeilla saanko tehtyä moitteetonta skriptiä yhteen ja vähennyslaskulle. Saapa nähdä kumpi on itsepäisempi: läppäri vai minä. Skripti valmistuu kun valmistuu ja jos kiirehtii niin alkaa sotkea jo tehtyäkin. Mutta läärään muuta skriptin valmistumista odotellessa:

- BASH on tehty opetusta varten. Siksi sen data-tyyppeihin ei kuulu desimalilukuja koska tarkoituksena on että desimaali-laskujen suorittamiseen tehdään funktiot. Tautisen hidasta semmoinen laskenta on mutta hautaan kerkiää kyllä silläkin vauhdilla.

- desimaaliluvut ovat niin tärkeitä laskennassa että väite ettei BASH desimaalilukuihin kykene on törkeä teko joka osaltaan on tuhonnut BASH:in.

- opetuskielen luonteeseen kuuluu että mitähyvänsä voi tehdä lukemattomilla täysin erilaisilla tavoilla - ja yritys löytää niistä vähiten järjetön on ikuisuusprojekti jossa kyykytetään ihan jokaista.

- koska BASH on opetuskieli niin se ei myöskään ilmoita koska laskettavat luvut ovat liian suuria BASH:in matematiikalle vaan käyttäjän olisi tarkistettava se itse - sillä sehän on oiva opetettava.

- ja lista jatkuu ikuisesti ... ja kokoajan tulee uusille tutkimattomille alueille. Korjaus: aikoinaan BASH:in harrastajia riitti melkein kaikkialle.

***

Skriptit ajetaan yleensä päätteessä ja vasta kun pääte on jo avattu. Tiedostoselaimella skriptien ajaminen on vihollisen keksintö BASH:in osoittamiseksi surkeaksi - se toimii pienillä sripteillä ihan hyvin mutta sitten tulee katto vastaan.

Pääte olettaa että mitä sinne syötetäänkin on BASH:ia ellei toisin ilmoiteta joten BASH-skripteissä ei kaivata riviä: #!/bin/bash.

Ei skriptin tarvitse tiedostossa olla vaan esimerkiksi kun kohtaat jollain verkkosivulla lyhyen skriptin niin voit leikata-liimata koko skriptin kerralla päätteeseesi ja painettuasi return skripti suoritetaan.

- yksirivisen skriptin voi kutsua nuoli-näppäimellä takaisin näytölle editointia varten ja return:illa ajaa uudelleen - nimitys yksirivinen on hieman harhaanjohtava sillä kyseisessä skriptityypissä kaikki on vain kirjoitettu ilman rivinvaihtoja ja skripti voi ihan hyvin olla monen rivin pituinen. 

- tämä on yksi syy miksi skripti kannattaa kasata funktioista sillä onhan se paljon helpompaa kokeilla skriptiä muistista kuin tiedostosta. Mikäli skripti on oikein kasattu niin viimeinen rivi on se rivi jolla skriptin parametrit määrätään ja skripti käynnistetään joten vaikka skripti olisikin monirivinen niin tämän viimeisen rivin ainakin voi kutsua takaisin, editoida parametreja ja ajaa skripti uudelleen. Sillä tosiaan on ehdoton totuus ettei kukaan kykene tekemään skriptiä joka toimii aina vaan sitä täytyy testata ziljoonilla eri parametreilla ja korjailla jatkuvasti.

- mutta mikäli skripti on tiedostossa niin se ajetaan aina menemällä ensin päätteeseen ja kirjoittamalla sinne päätteeseen: . skriptin_nimi_tiedostopolkuineen    (huomio piste skriptin_nimen edessä. Se on käsky liittää osoitetun tiedoston koodi päätteessä jo toimivan skriptin rinnalle - pääte on itsekin tavallaan skripti joten ei siellä aikaisemmin mitään tarvitse olla.)

- skriptin tiedostolle ei tarvitse antaa suoritusoikeutta - paitsi jos ajaa sitä tiedostoselaimella. Kummallinen juttu muuten: kaikki BASH:in viholliset horisevat että BASH on tietoturvaton ja sitten itse opettavat todellista tietoturva ongelmaa joka on lisäksi tarpeeton.

- '. skriptin_nimi' voidaan kirjoittaa skriptiinkin ja toiminta on silloinkin sama - puhutaan kirjaston liittämisestä skriptiin. Tuollaisten rivien tulee sijaita skriptin alussa.

- samoin skriptin tiedostonimen lopussa oleva pääte .sh on vain tieto käyttäjälle että BASH:ina tämä tullaan ajamaan - eikä peräliitettä siis ole pakko kirjoittaa. Eri kielten skriptit ovat täysin ei kansioissa eikä niitä voi sotkea.
 
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 02.02.23 - klo:02.38
Koodia: [Valitse]
Elämässäni ensimmäinen kerta kun en uskalla jotain testata kovin nopeasti. Mutta kaiken kieron mitä olen uudelle yhteen- ja vähennyslaskulle keksinytkin on se ratkaissut sen oikein. Toisaalta myös lasku 1+1 onnistui. Mutta jossain muussa helpossa se tietty mokaa.

***

Desimaali-jakolaskuun löytyi uusi menetelmä - se on aina nopea mutta vaikka joissakin laskuissa saa 48 oikeaa desimaalia niin joissakin ei saa kuin muutaman. Ja muutenkin se on toistaitoinen menetelmä. Pitääpä katsoa saako primadonnan tanssimaan.

Esitänpä laskemisen periaatteen:

pitäisi laskea mitä on: 1233457890123.23/.123456
kokonaisosa: $((1233457890123230000/123456)) -> 9991072852864

aletaan laskea desimaaleja: $((1233457890123230000%123456)) -> 52016
desimaalit ovat: $((5201600000000000/123456)) -> 42133229652
 siis vastaus on näin alkuunsa:
9991072852864.42133229652           
haetaanpa oikea vastaus: bc -l<<<"1233457890123230000/123456" -> 
9991072852864.42133229652669777086

***

Edellisestä periaatteesta tehty skripti - joka samantien laskee pari lasku-kierrosta lisää:
Koodia: [Valitse]
# tämä on uusi ja korjattu versio joka on vielä aika raakile ja muuttuu vielä paljon. Keskeneräisyyden
# osoitus on runsas välitulosten tulostaminen
 

function siisti () { apu=$1; merkki='';[[ ${apu//[^-]/} ]] && merkki=- && apu=${apu:1}; [[ ${apu//[^.]/} ]]  || apu=$apu"." ; apu=${apu%00000000000000};apu=${apu%0000};apu=${apu%00};apu=${apu%0};apu=${apu#00000000000000};apu=${apu#0000};apu=${apu#00};apu=${apu#0};apu=${apu%.}; echo $merkki$apu ;}

function jaa () { # muutos
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."

desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
kokonaisosa1=${luku1%%.*}; echo x$kokonaisosa1
kokonaisosa2=${luku2%%.*}; echo y$kokonaisosa2
# apu=$((${kokonaisosa1:0:18}/${kokonaisosa2:0:18})).
kokonaisiatulosteessa=0
nolliatulosteessa=''

apu=$((${#kokonaisosa2}-${#kokonaisosa1}-1));echo $apu
case $apu in
-1) kokonaisiatulosteessa=1 ;;
0) kokonaisiatulosteessa=0 ;; # apu=85; printf "%${apu}s" | tr " " 0
1) nolliatulosteessa='' ;;
2) nolliatulosteessa=0;;
3) nolliatulosteessa=00 ;;
4) nolliatulosteessa=000 ;;
5) nolliatulosteessa=00000 ;;
6) nolliatulosteessa=000000 ;;
7) nolliatulosteessa=0000000 ;;
8) nolliatulosteessa=00000000 ;;
9) nolliatulosteessa=000000000 ;;
10)nolliatulosteessa=0000000000 ;;
*) kokonaisiatulosteessa=$((-1*$apu-1 )) ;;
esac
echo nolliatulosteessa:$nolliatulosteessa'  kokonaisiatulosteessa:'$kokonaisiatulosteessa

luku1=$kokonaisosa1$desimaaliosa1
luku2=$kokonaisosa2$desimaaliosa2 

echo xxx$luku1' '$luku2

unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
for n in {1..6}; do # muodostetaan tulos-palasia 9 merkkiä kerrallaan
apu=$((10#$luku1/10#$luku2)); (( ${#apu} ==8 )) && apu=$apu'0'; tulos[$n]=${apu} ;echo a$luku1' '$luku2' '$apu
luku1=$(($luku1%$luku2))'0000000000000000000'; luku1=${luku1:0:18} ;echo z$luku1
done

for n in {1..6}; do # kootaan tulosta matriisin palasista
tulos=$tulos${tulos[$n]}
done

# tulos=$( siisti $tulos)

echo "oikea tulos 54 desimaalilla esitetynä on päällä ja alla tulos tästä laskusta:"
bc<<<"scale=54; $1/$2" | tr -d '\\\n'; echo ' tämä rivi on bc:stä'
[[ $nolliatulosteessa ]] && echo .$nolliatulosteessa${tulos:0} || echo ${tulos:0:$kokonaisiatulosteessa}.${tulos:$kokonaisiatulosteessa} ;}
           
jaa 1233457890123.23 .1234567


----skripti tulostaa:
oikea tulos 54 desimaalilla esitetynä on päällä ja alla tulos tästä laskusta:
9991016203439.991511193803171476315177710079728358201701487242085686 tämä rivi on bc:stä
9991016203439.991511193803171476315177710079728358201701487242085686

- tämä osoittaa ainakin että periaate on oikea - ongelmana näyttää olevan muunmuassa desimaalipisteen paikan laskeminen ja laskujen etunollat. Kai ne aikanaan saa korjatua.
- miinus-merkkiset ei vielä toimi
- tämäkin skripti on niin kasattu että sen  voi helposti ajaa uudestaan - paina vai nappin nuoli-ylös jolloin funktiokutsu palaa näytölle edtoitavaksi ja kun painaa enter niin se ajetaan uudestaan editoiduilla parametreilla.
- kaikki tämän tyyppiset skriptit ovat nopeita ja sillä on vain pieni merkitys kuinka kookkaita nämä tällaiset skriptit ovat - ja esimrkiksi voi laskea niin monta desimaalia kuin sielu sietää - nopeus on aina siellä millisekunnin nurkilla.
- muuten nuo kummalliset käskyt ovat tekstinkäsittely-käskyjä eivätkä matematiikka-käskyjä - BASH on yksittäisten sanojen käsittelyssä ziljoonakertaa parempi kuin sed - ja sed on ehdottomasti paras isojen tekstien käsittelemisessä. Tai enpä ole tuosta ihan varma - sed on pajon parempi kuin "korkeantason" käskyillä invalidisoitu BASH mutta en tiedä kuinka nuo matalan tason käskyt isoissatekstinkäsittelyssä toimivat, en ole kokeillut. Paitsi silloin kerran kun puhuttiin Pythonin ylivertaisuudesta ja todettiin että kyllä sed-kin jää toiseksi ja naureskeltiin BASH:in kustannuksella - mutta itseasiassa eräs BASH:in matalan tason käsky oli melkein yhtähyvä - mutta luulin silloin että ihan yksittäinen tapaus se oli, vaan eipä tainnut ollakaan.

***

Oli pahantahtoinen teko viedä BASH:ilta kirjasto-osoitin - kirjastojen toimintaan se ei vaikuta mutta tekee kirjastojen käyttämisestä hankalaa.

Tästä hommasta on väännetty ikuisesti - esimerkiksi väittämällä että kirjastot ovat tietoturvariski. Mutta jokaisessa Ubuntussa on 85 funktion kirjasto joten jos ne ovat tietoturvariski niin se tietoturva on mennyt jo ja se osoittimen vieminen oli pelkkää kiusantekoa.

***

Aivan kaikesta voi ja myös pitää tehdä funktio kirjastoon sillä kukaan ei voi muistaa kaikkia pieniä kikkoja - tai ainakaan viitsi kirjoittaa. Esimerkiksi käsky joka etsii jostakin jotakin - tekstinpalasia lauseista tai tiedostoista, numeroita ja vaikka mitä. Pienissä hommissa se on paljon parempi kuin grep:
Koodia: [Valitse]
function onkoosa () { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ] && echo joo || echo ei ;};}
- kutsu etsittäessä tiedostosta: onkoosa BEGIN "$(cat /boot/grub/grub.cfg)"
- kutsu etsittäessa tekstijonosta:onkoosa öp <muutuujan nimi>

matriisista haku sitten toisella tavalla:
Koodia: [Valitse]
function onkomatriisissa () { [[ " ${@:2} " =~ $1 ]] && echo joo || echo ei ;};
esimerkiksi: matriisi=({1..100000}); onkomatriisissa 55555 ${matriisi[*]}

Nyt sain ajatuspähkinän että toimiiko tämä aina:
Koodia: [Valitse]
function onkomatriisissa () { [[ $(declare -p $2 ) =~ \"$1\" ]] && echo joo || echo ei ;}; matriisi=({1..100000}); onkomatriisissa 55555 matriisi
- siis matriisi passataan nimiparametrina - siis semmoisena jota BASH ei muka tunne.

***

Kun tuommoisia alkaa kerätä kirjastoonsa niin samalla voi nimetä ne uudestaan että funktiolla on sellainen nimi jonka muistat - ja voi niitä muutenkin hieman räätälöidä.

Kyllä niitä funktioita googlaamalla löytää. Ongelmana on ettei yksittäisestä funktiosta ole mitään iloa vaan niitä täytyy löytyä tuhansia - lajiteltuina ryhmiin, kaikkien toiminta pitäisi olla santapaista kuin muillakin samassa ryhmässä - ja ennenkaikkea omituiset omassa ryhmässään silla kaikessa on kyllä jotakin eikä niitä omituisia roskikseen pidä laittaa.

Olisikin tarve siihen että olisi paikka jossa olisi funktioita todella paljon. Ja kyllä niitä onkin - mutta nuo varastot on kaikki pilattu. Joko käyttäminen on ihan liian monimutkaista, vaikeaa ja tarkoitettu virtuooseille  - tai funktioissa on muutamia hyviä mutta käsittämättömän huonoja niin runsaasti ettei niitä kultajyviä tahdo millään  löytää.

******

Tietokoneen omienkin matematiikka-kirjastojen kehittäminen on vaatinut lukemattomia mies-työvuosia kymmeniltä loistavilta matemaatikoilta - nuo kehitystiedot on sijoitettu prosessorien matematiikka-yksikköiden langoitukseen ja mikrokoodiin, käyttöjärjestelmien kirjastoihin ja kielijärjestelmien kirjastoihin. BASH on ihan tietoisesti hankkinut vain rajoitetun pääsyn integer-kirjastoihin.   

Sillä kaikki laskenta on jossain vaiheessa kokonaisluku laskentaa ja kirjaston tekeminen kokonaisluku-laskentana ihan mahdollista - ja koska BASH on opetuskieli niin oli opetuksen takia tarkoitus pistää käyttäjät tekemään itse desimaalikirjastonsa. Ja senaikaisilla käskyillä noista kirjastoista olisi tullut kohtuullisen nopeitakin - siellä millisekunnin nopeus-luokassa isotkin matalantason käskyistä tehdyt skriptit useimmiten ovat. Lisäksi noiden skriptien nopeus laskee vain vähän koodimäärän kasvaessa. 

Mutta jostainsyystä kukaan ei silloin kauan sitten noita kirjastoja tehnyt ja kun BASH:in käskykantaa "parannettiin" olisi kirjastoista tullut uuslla käskyillä niin hitaita ettei niiden tekemisessä ollut mieltä - toimivathan ne vanhat käskyt edelleen mutta tietoisuus niiden olemassaolosta hiipui nopeasti.

Nyt on tilanne BASH:in kannalta niin toivoton ettei noiden kirjastojen kehittäminen enää paljoa kannata - BASH:in kyvyt kasvavat paljon mutta käyttäjäthän ovat jo kaikonneet eivätkä takaisin tule. Mutta saihan näistä yritelmistä ainakin sen tiedon että tässäkin asiassa BASH on telottu hengiltä levittämällä epätotuuksia - kovin on kirjastojen poissaolo omituista - niitä muuten oli aikoinaan mutta ne tuhottiin jollain ilveellä - jäljellä on tosin muutama omituinen. Ja on syytä muistaa se jokaisessa Ubuntussa oleva 85 funktion kirjasto jota vain käyttöjärjestelmä tietää käyttää. Varmaankin tuo kirjasto muistakin Linukseista löytyy käskyllä: declare -f. Sieltä muuten näkee senkin etteivät kehittäjät vierasta käskyä: eval.

***

Teoriassa laskut desimaaliluvuilla ovat yksinkertaisia silloinkin kun voi laskea ainoastaan  kokonaisluvuilla: ennen laskua poistetaan se demaalipiste, sitten lasketaan ihan normaalisti jäljelle jääneellä kokonaisluvulla ja lopuksi se desimaalipiste palautetaan. Mutta vaikka tämä toimii käytännössäkin niin eteen tulee monia vaikeuksia:

Esimerkiksi desimaalipisteen palauttaminen oikeaan paikkaan on todellinen ongelma - lisäksi se on vielä pientä jos laskusi heittää kymmenertaisesti. Seuraavankaltaisissa pikkuhommassakin desimaalipisteen paauttaminen oikealle paikalle aiheuttaa päänsärkyä:
Koodia: [Valitse]
function tuplaa () {
luku1=$1"." # jos luvussa on tässävaiheessa kaksi desimaalipistettä ei se haitaa
kokonaisosa1=${luku1%%.*}
kokonaisosanpituus=${#kokonaisosa1}
luku1=${luku1//./};luku1=${luku1:0:18} # poistetaan ne desimaalipisteet olipa niitä 1 tai 2
tulo=$(($luku1*2))
[[ ${#tulo} -gt ${#luku1} ]] && kokonaisosanpituus=$(($kokonaisosanpituus+1))
tuloste=${tulo:0:$kokonaisosanpituus}.${tulo:$kokonaisosanpituus}
echo ${tuloste%*.} # ei tulosteta desimaalipistettä jos se on tuloksen viimeinen merkki
}

tuplaa 9.65438761111111112
Desimaalipisteen paikan kanssa joutuu kamppailemaan vaikka tekisi tuplaamisem tai puolittanisen bittisiirtoina  (tuo: let "luku <<=1"):
Koodia: [Valitse]
function tuplaa () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku <<=1";  apu=${luku: -$desimaaliosanpituus:$desimaaliosanpituus-1}; (( $apu )) && echo ${luku:0: -$desimaaliosanpituus}.$apu || echo ${luku:0: -$desimaaliosanpituus} ;}

function puolita () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku >>=1"; echo ${luku:0: -$desimaaliosanpituus}.${luku: -$desimaaliosanpituus:desimaaliosanpituus-$((${luku: -1} == 0)) } ;} 

# Tässä testaus:
puolita $(tuplaa 5555555555555.5555)

***

Mutta mikäli laskettavien joukkoon tulee toinenkin luku niin eteen tulee lukemattomia ongelmia lisää - esimerkiksi yhteenlasku onnistuu näin vain mikäli lukujen desimaaliosat ovat yhtäpitkät - jos ne eivät ole niin lyhyempään täytyy lisätä perään nollia niin monta että ne ovat yhtäpitkiä - sillä desimaalien perässä nollat eivät muuta mitään. Samallatavoin täytyyy tehdä jos desimaalilukuja vertailee. On yli- ja alivuodot - ja ongelmat niiden kanssa laajenevat paljon pyrittäessä suurempaan numeromäärään jolloin lukuja aletaan jakaa osiin ja niiden laskentatuloksia aletaan koota. Silloin on myös pahemmat merkkiongelmat ... 

BASH tosiaan opettaa olemaan riemuitsematta kovin nopeasti - usein myöhemmin selviää ettei skripti ihan niin hyvä ollutkaan kuin oli luulo. Aina on parantamisen varaa - toisaalta se on katkeraa ja toisaalta elämän suola.

- kaikkein katkerinta on tieto siitä että jotkut ovat tienneet ikuisesti desimaalilaskujen onnnistuvan BASH:issakin oikein hyvin sillä kokonaisluvuilla desimaalilaskut aina lasketaan - siellä näyttämön takana jonne käytännössä harva kurkkii - mutta matematiikan teoreetikot ovat tienneet tämän aina - se on asiaan kuulumatonta että se on todellinen miinakenttä. BASH:issa kyky laskea desimaaleilla on nykyään  merkityksetöntä mutta aikoinaan se olisi ollut erittäin merkittävää ja ihan yhtä mahdollista kuin tänäänkin.

***

Desimaalilukujen kanssa toimiessa olisi usein tarpeen poistaa merkityksettömät etunollat kokonaisosasta ja takanollat desimaaliosasta - ja poistaa myös desimaalipiste mikäli se jäisi luvun viimeiseksi merkiksi. Yksi kammottava funktio tekee sen nopeasti ja varmasti:
Koodia: [Valitse]
function siisti () { apu=$1; merkki='';[[ ${apu//[^-]/} ]] && merkki=- && apu=${apu:1}; apu=${apu%00000000000000};apu=${apu%0000};apu=${apu%00};apu=${apu%0};apu=${apu#00000000000000};apu=${apu#0000};apu=${apu#00};apu=${apu#0};apu=${apu%.}; echo $merkki$apu ;}

# käsky kokeilemiseksi:
apu="-000120340.0400";echo -n "Luku alunperin: $apu  . Ja käsittelyn jälkeen:   "; siisti $apu

***

Aloin tutkia Taylorin sarjoja ja kestää tovi ennenkuin tuloksia tulee - teorioiden toimivuus on kyllä tarkistettu jo muttaa kerkiänkö remontin takia tekemään toimivaa skriptiä jää nähtäväksi. Silläaikaa täytyy puhua pehmoisia:


BASH:issa on kyllä samantapaiset muuttujien määrittelyt kuin muissakin kielissä mutta eipä niitä näissä pikkuhommissa tarvita - miksi suotta sotkea koodia? Mutta joidenkin mieletä ne päinvastoin selkeyttävät koodia ja ovathan ne joskus ehdottoman tarpeellisiakin. Joten on syytä  tietää että niitäkin on:

1. Funktiossa olevat muuttujat näkyvät pääohjelmassakin. Joskus funktio silloin muuttaa tarkoittamattaan pääohjelman muuttujia. Tällöin funktiossa niille härikkö-muuttujille määrätään: local muuttujan_nimi
- joskus täytyy peräti siirtyä funktiossa omaan prosessiin jolloin mikään ei varmasti vaikuta pääohjelmaan ellei nimenomaan käsketä. Tämä tapahtuu muuttamalla funktiokutsun aaltosulut kaarisuluiksi.
2. Joskus halutaan painottaa sitä että muuttuja on kokonaisluku. Tällöin määrätään:
declare -i muuttujan_nimi   (=integer).
3. joskus on tarpeen määrätä että muuttuja onkin vakio: declare -r muuttujan_nimi=arvo  (=readonly)
- siis arvo täytyy antaa määrittelyn yhteydessä.
4. joskus halutaan painottaa sitä että muutuja on matriisi-> declare -a muuttujan_nimi
5. jos matriisi on assosiatiivinen jolloin se täytyy määritellä: declare -A muuttujan_nimi (Assosiatiivisen matriisin osoite on tekstiä - jos siinä on numeroita niin tekstiksi nekin käsitetään).

. ja on määreitä muitakin: https://linuxcommand.org/lc3_man_pages/declareh.html

Onhan BASH:in muuttujat mukavia käyttää kun ainoastaan assosiaiatiivinen matriisi täytyy määritellä. Vaan on sillä varjopuolensakin - esimerkiksi muuttuja on aina myös saman-nimisen matriisin ensimmäinen jäsen ja usein kun luulet toimivasi numeroilla matriisin kanssa toimitkin itseasiassa tekstijono-muuttujan kanssa.

Voit toki määritellä interger-matriisinkin. Silloin ongelmat ovat toisenlaisia - käskepä:
declare -ai apu; apu[5]=kattokassinen; echo ${apu[5]} -> tulostuu 0 - ellei sattumalta muualla skriptissä ole muuttujaa nimeltä kattokassinen ja sillä numero-arvo.

Samoin mukavaahan se on kun ei tarvitse välittää onko muuttuja tekstiä vai numero - lukuunottamatta tilannetta jossa nimenomaan määräätään muuttujan olevan numero. Noilla integer- muutujilla on muuten automaattinen laskenta - käskepä: declare -i apu; apu=1+2; echo $apu -> tulostuu 3 - mutta RAM-muistissa se silti on:1+2

Ja mukavaa on sekin ettei välttämättä tarvitse antaa muutujalle arvoa ennenkuin sitä käyttää - sen arvo on silloin 0. Mutta toisaalta tämä tekee sen että jos esimerkiksi toisessa skriptissä onkin annettu saman-nimiselle muuttujalle jokin arvo pysyy määrittely voimassa ja yhtäkkiä tämänhetkisen skriptin muuttujalla onkin joku kummallinen alkuarvo. Tämän takia skriptin alkuun kehoiteaan laittamaan: set -u

***

BASH olettaa että asiat on määritelty siinävaiheessa kuin niitä yritetään käyttää - elikä eteenpäin viittauksia ei sallita. Tämän kiertämiseksi on lukemattomia konsteja mutta koodi pysyy yksinkertaisempana kun ei konsteja suotta harrasta - konsteista täytyy saada jotain pätevää hyötyä - ja sitä ei voi sanoa hyödyksi että haluaa BASH:in toimivan kuin C.

BASH on tuomittu ohjelmointikielenä vaika eihän BASH:ia ole ohjelmointiin tarkoitettukaan vaan ulkoisten ohjelmien yhteen-nivomiseen - BASH:in toimiminen opetuskielenäkin on tavallaan sivujuonne sillä kyllä me kaikki oppia kaipaamme. On BASH:illa ohjelmoiminen silti mielestäni mukava harraste ja nuo desimaalilaskuni olivat vain yksi osoitus siitä että BASH:ista puhutaan suupielet ruskeina kaikenlaista.

Enkä usko etteivät virtuoosit ole tienneet että BASH osaa kaikenlaista - esimerkiksi funktiot ovat tosiaan BASH:in toimimisen perusta ja kun funktioita ei käytetä ja peräti väitetään ettei funktio-nimiprametreja tunneta niin kyllä siinä kieli hiljokseen tikahtuu. Joten ei BASH toisten puheisiin ja luuloihin tainnut kuolla vaan kyllä syy oli sisäsyntyinen - virtuoosit tappoivat oman kielensä.

Sillä on aika rakentaa ja on aika rikkoa. Se ei ole meidän oma valintamme vaan meidän jokaisen elämä tapahtuu noin halusimme tai emme. Nuorena alamme jossain vaiheessa rakentaa ja jossain vaiheessa vanhuutta alamme taas rikkoa sitä mitä olemme rakentaneet - rikkomistamme yleensä edes tajuamatta.

***

Yksi asia mitä virtuoosit eivät painota enkä minäkään ole huomannut painottaa vaikka se kuuluisi kertoa jo ennen alkeita: BASH:in tietorakenteet ovat dynaamisia elikä niiden koko määräytyy vasta käytönaikana - esimerkiksi matriisia ei tarvitse edes ilmoittaa ja silti voidaan määritellä ensin matriisin miljoonas jäsen ja seuraavaksi jäsen 222 ja jättää kaikki muut määrittelemättä ja silti se on täysin kelvollinen matriisi. Sama on tekstijonon kanssa - sen koolla ei ole ylärajaa jaa se voikin kasvaa ikuisesti vaikka sen olemassaolosta ole koskaaan ede kerrottu - se vain on olemassa ja alussa sen arvo on tyhjä.

Voit siis aivan rauhassa tulostaa jotakin josta ei ole koskaan puhuttu - sillä ei vaan ole arvoa joten tulostuu tyhjää. Samoin jos käytät määrittelemätönta niin se ei ole varsinaisesti virhe vaan tuon määrittelemän matemattiseksi arvoksi oletetaan nolla ja tekstuaaliseksi arvoksi tyhjä. Tottakai tuommoisella on myös varjopuolensa ja sen virtuoosit kyllä muistavat kertoa.






Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 28.02.23 - klo:10.52
BASH tulkkaa koodinsa C:lle joten muodostettu koodi on nopeaa. Lisäksi toimintanopeuden kannalta merkittävin aika on se aika joka kuluu tulkin lataamisen aloittamisesta siihen että tulkki alkaa toimia ja BASH:illa se on ehdottomasti paras. Kääntäjän kyvyillä ja kääntäjän tekemän koodin laadulla on vain vähäinen merkitys. Näiden perusteella voi sanoa ettei yhdelläkään skriptikielellä ole mahdollisuuksia kipailla BASH:in kanssa nopeudessa.

Silti BASH on sangen hidas. Väite ettei BASH osaa sitä ja tätä ei oikeastaan pidä paikkaansa vaan kyllä se osaa mutta paljon pidemmillä ja kummallisemmilla koodeilla. Esimerkiksi Pythonia päivitetään monta kertaa viikossa mutta BASH:ia vain vuosittain joten tottakai Python kykenee nykyään kuvaamaan toiminnan lyhyemmin ja selvemmin - sitäpaiti se oli jo alkuunsakin käyttäjäystävällisempi. Mutta se ei vielä riitä selvittämään BASH:in hitautta.

Tietokoneessa koodin nopeus riippuu paljon siitäkin kuinka sille myönnetaan toiminta-aikaa ja millaisin 'etuajo' oikeuksin - siis suureltaosin käyttöjärjestelmä päättää kuka on nopein.

***

Kohtasinpa muutaman skriptin joissa sanan function paikalla lukee alias ja niitä alun sulkuja  ei ollut - mutta käyttämisessä ei ole eroa. Skriptit olivat pitkiä ja monimutkaisia. Kiinnostuin asiasta ja aloin esiä selityksiä. Maailmalta löytyikin paljon teoreettista pohdiskelua suuntaa ja toiseen  mutta pääasiaa ne eivät kertoneet: jos funktiolla ei ole parametreja niin silloin selviää vähemmällä kirjoittamisella kun käyttää alias:ta.

Siis tämä on ihan sama alias kuin se mitä opetetaan helpottamaan tekstin kirjoittamista ja joka laitetaan tiedostoon ~/.bashrc . Ja se toimiikin tismalleen samalla tavalla - se kirjoitetaan tiedestoon vain siksi että halutaan että se tulee aina voimaan kun menee päätteeseen. Toisaata funktion voi kirjoittaa ~/.bashrc:hen jolloin sitä voi kutsus aina.

***

Kokeilin juuri kuinka alle 18 numeroiset desimaalilaskut onnistuvat - ja kyllähän ne onnistuvat mutta rutiinien tekemisessä olisi kauheasti töitä ja tuloksena kamalan rajoittunut laskenta - sillä esimerkiksi jakolaskuissa saisi luvuissa olla vain 4 numeroa kokonaisosaa ja 4 numeroa desimaaliosaa - siinä tulee 18 numeroa tulokseen - ja tulos olisi varmasti oikea ja toiminta lähes salaman-nopeaa. Mutta en jaksa edes aloittaa koska eihän näillä enää merkitystä ole.

***

Kannattaa muuten käydä verkkosivulla: https://www.commandlinefu.com - siellä on suuri joukko funktioita. Sieltä saa mielikuvan siitä mitä kaikkea kummallista löytyy - ja sielläkin on vain pieni näyte - tosin ne pitäisi koota ja laittaa järjestykseen jotta löytäisi sen mitä etsii. Tai tarjota käyttöön todella hyvät etsintätyökalut - BASH:issa itsessään muuten on, ihan eriluokan etsintätyökalut kuin muilla.

Sieltä ja muualtakin netistä löydät paljon lyhyitä funktioita jotka ratkaisevat ongelmia joita normaalisti ratkotaan paljon isommilla ohjelmilla - ja ne ovat lisäksi melkein aina nopeita. Tosiaan jokaiseen ongelmaan joku tietää ratkaisun ja usein laittaa ratkaisunsa nettiin - ainoastaan sen ratkaisun löytäminen on ongelma - saa muuten nähdä kuinka tuo ChatGPT-tekoäly alkaa löytää niitä ratkaisuja.

Ja onhan niitä paikkoja muitakin - esimerkiksi: https://www.regexbuddy.com/

BASH kehittyy vieläkin kammottavan nopeasti vaikka hyljeksitty onkin - eikä sitä kukaan edes uskalla tosimielellä opettaa BASH:ia sillä kovin usein noviisit ja lapsetkin huomaavat jotain sellaista jota edes virtuoosit eivät ole huomanneet - ja eihän sitä yksikään virtuoosi kestä vaan kyllä semmoinen kieli täytyy hävittää.

Ja kyse ei ole pikkujutuista sillä oikealla tavalla kasattu BASH-skripti toimii kymmeniä kertoja nopeammin ja varmemmin kuin ne kouluesimerkit joita on totuttu käyttämään.

Luepas BASH 5.2:n uusista ominaisuuksista verkkosivulta: http://tiswww.case.edu/php/chet/bash/NEWS

Ihan alkuun tulee ihmettelemistä: BASH ja malloc? Kyse taitaa olla siitä että BASH:in tietorakenteet ovat aina dynaamisia - koko määräytyy vasta käytön aikana - joten kun tulkki tekee BASH-skriptistä koodia C:lle niin C tarvitsee malloc:ia - joten BASH-tulkin käyttämään malloc:iin on tehty muutoksia sillä segmentointi aiheutti vanhalla mallocilla skriptien kaatumisia? Mutta eihän virtuoosit kerro tämmöisiä heidänmielestään itsestäänselvyyksistä.

Jo ajatuskin tökkii siinä kuvattujen ominaisuuksien soveltamisesta - eihän niillä ole mitään tekemistä noissa guideissa esitettyjen asioiden kanssa? Ja ilman oppaita ei pääse skriptaamisessa alkuunkaan ja mitäpä teet kun oppaat kertovat aivan toisesta aikakaudesta kuin todellisuus?

***

BASH on niin nutistettu ettei suuria BASH-ohjelmia ole tehty vuosikymmeniin - aikoinaan jokapaikka pullisteli niitä - henkistä pääomaa on hukattu surutta. Sillä kyllä BASH:illa on ihan samat kyvyt kuin muillakin kielillä - usein tosin hankalakäyttöisinä koskei BASH:ia paljoakaan kehitetä. Ja mikäli tekee asiat oikein on BASH siedettävän nopeakin - ja joissain hommissa sen kyvyt ovat ylivertaiset, esimerkiksi järjestelmän hoitamisessa.

Kaikki skriptin muuttujatkin voidaan tulostaa skriptin jokaisessa kohdassa esimerkiksi seuraavilla funktioilla:

Koodia: [Valitse]
function xref () {
function tulostaMuuttuja () { echo -n muuttuja $1':n arvo: '; eval echo \$$1 ;}

function tulostaMatriisi () { echo -n matriisin $1' arvot    : ';  eval echo \${$1[*]}
echo -n matriisin $1' osoitteet: ';  eval echo \${!$1[*]} ;}

function testaaMuuttuja () { 
echo; [[ $( eval echo "\${!$1[*]}") = 0 ]] && tulostaMuuttuja $1 || tulostaMatriisi $1 ;}

muuttuja=$(echo ${!a*}' '${!b*}' '${!c*}' '${!d*}' '${!e*}' '${!f*}' '${!g*}' '${!h*}' '${!i*}' '${!j*}' '${!k*}' '${!l*}' '${!m*}' '${!n*}' '${!o*}' '${!p*}' '${!q*}' '${!r*}' '${!s*}' '${!t*}' '${!u*}' '${!v*}' '${!w*}' '${!x*}' '${!z*}' '${!y*}' '${!A*}' '${!B*}' '${!C*}' '${!D*}' '${!E*}' '${!F*}' '${!G*}' '${!H*}' '${!I*}' '${!J*}' '${!K*}' '${!L*}' '${!M*}' '${!N*}' '${!O*}' '${!P*}' '${!Q*}' '${!R*}' '${!S*}' '${!T*}' '${!U*}' '${!V*}' '${!W*}' '${!X*}' '${!Z*}' '${!Y*})

for muuttuja in  ${muuttuja%%BASH*}; do [[ $muuttuja = muuttuja ]] && : || testaaMuuttuja $muuttuja; done ;}

# xref voidaan laittaa skriptiin mihinkä kohtaa hyvänsä. Esimerkkikutsu:
kaveri=kamu
a=55
A7=2
mat=(7 8 9); mat[15]=555
xref

***

Muuttujien talletukseen ja lukemiseen ei kannatakaan tehdä funktiota vaan kirjoittaa sen lyhyt koodi suoraan skriptiin.
Koodia: [Valitse]
Talletus:  declare -p muuttujan_nimi > ~/delme 
Lukeminen: . ~/delme

Samaan tiedostoon voidaan tallettaa niin monta muuttujaa kuin halutaan. Käsky muuttuu silloin vähän:
declare -p muuttujan_nimi >> ~/delme

- kaikki ne latautuvat muistiin yhdellä kertaa kun käskee: . ~/delme

- mikäli luettu muuttuja on matriisi tulostetaan sen arvot:
Koodia: [Valitse]
echo arvot"${nimi[*]}"osoitteet:"${!nimi[*]}"

- luettaessa ei ole mielekästä yrittää määrätä minkäniminen muuttuja halutaan lukea sillä tiedostossa on senniminen muuttuja kuin siellä on eikä sitä nimeä voi muuttaa.

- yleensä declare:lla määritellään muuttujaa. Mutta 'declare -p' pytää BASH:ia tulostamaan kuinka muutujia kuvataan BASH:in taulukoissa. Parasta kuvata tämä esimerkillä: luo matriisi: n=(1 2 3) ja katso miten BASH pitää siitä kirjaa: käske:  declare -p n . Tulos:
declare -a n=([0]="1" [1]="2" [2]="3") . Siis tulos on tekstijono - ja se kelpaa käskyksi yhtähyvin kuin tavallinenkin muuttuja - siis kun tekstijonon nimi mainitaan yksinään niin tekstijonossa mainittu muuttuja määritellään -  esimerkiksi tässä tuo -a merkitsee että ennen arvojen antamista tehdään tavallinen matriisi ilman lisämääreitä.

Ja tekstijonoilla ei ole pituusrajaa joten siihen mahtuu miljoonia parametreja: esimerkiksi matriisin kaikki jäsenet ja niiden osoitteet - ja matriisissahan voi olla miljoonia jäseniä. Muuttujan kaikki ominaisuudet säilyvät. Myös matriisin sparse-ominaisuus säilyy.

Myös assosiatiiviset matriisit siirtyvät eikä edes erillistä määräystä matriisin assosiatiivisuudesta tarvita.

Ja semmoisenkin nippelitiedon saa että BASH:in tavalliset numerot voi kirjoittaa lainausmerkkien väliin - esimerkiksi: echo $((2*"55")) näyttää tulokseksi 110.

Siis ainoastaa funktiolle: talletamuuttuja  kerrotaan mikä muuttuja halutaan tallettaa - se annetaan nimiparametrina: <talletamuuttuja muuttujan_nimi> - ja sillä ei siis ole väliä millainen se muuttuja on.

Ja luemuuttuja lukee tiedoston delme ja palauttaa siellä olevan muuttujan arvon - määritellen ensin  millainen muuttuja se on ja määräten sitten sen arvon.

***

Käskyt: declare -p > ~/delme   ja: . ~/delme siirtävät kyllä kaikki muuttujat mutta käsky: . ~/delme urputtaa inhottavasti - ei se virheitä tee vaan aiheuttaa  huomautuksia - mutta muutenkin alkaa epäillä että mitä kaikkea se tekeekään.

Moitteettomampi menetelmä järjestelmän kaikkien muuttujien tallettamiseksi on:
Koodia: [Valitse]
function snapshotLevylle () {
function talletamuuttuja () { echo $(declare -p $1) >> ~/delme ;}

echo '' > ~/delme
muuttuja=$(echo ${!a*}' '${!b*}' '${!c*}' '${!d*}' '${!e*}' '${!f*}' '${!g*}' '${!h*}' '${!i*}' '${!j*}' '${!k*}' '${!l*}' '${!m*}' '${!n*}' '${!o*}' '${!p*}' '${!q*}' '${!r*}' '${!s*}' '${!t*}' '${!u*}' '${!v*}' '${!w*}' '${!x*}' '${!z*}' '${!y*}' '${!A*}' '${!B*}' '${!C*}' '${!D*}' '${!E*}' '${!F*}' '${!G*}' '${!H*}' '${!I*}' '${!J*}' '${!K*}' '${!L*}' '${!M*}' '${!N*}' '${!O*}' '${!P*}' '${!Q*}' '${!R*}' '${!S*}' '${!T*}' '${!U*}' '${!V*}' '${!W*}' '${!X*}' '${!Z*}' '${!Y*})

for muuttuja in  ${muuttuja%%BASH*}; do talletamuuttuja $muuttuja; done ;}

# Koekeilua - kuvitellaan että seuraava on skripti ja sen muuttujat halutaan tallettaa:
kaveri=kamu
a=55
A7=2
mat=(7 8 9 'heipparallaa helsinki'); mat[15]=555
snapshotLevylle

# koe toimiko:

unset kaveri # tämä nollaa muuttujan
unset a
unset A7
unset mat
# katsotaaan ovatko ne varmasti tyhjiä - tulostuu viisi tyhjää riviä
echo $kaveri
echo $a
echo $A7
echo; echo ${mat[*]}; echo ${!mat[*]}; echo

. ~/delme 

echo $kaveri
echo $a
echo $A7
echo; echo ${mat[*]}; echo ${!mat[*]}; echo

***

Jos tulee tarve tallettaa funktiot niin sen voi tehdä näin ( jos vaikka haluaa siirtää kaikki muistissa olevat funktionsa kaverin koneeseen niin siirretäänkin tiedosto ~/delme2):
kirjoitus: declare -f > ~/delme2     ja luku: . ~/delme2
- tällätavoin siirretyt funktiot katoavat bootattaessa.

***

Myös aliakset siirretään toiseen koneeseen samantapaisesti:
Lähdekone: alias > ~/delme3 ja kopioidaan se sitten muisttikulle nimellle: delme3
Kohdekone: tikku kiinni ja kopioidaan delme3 nimelle ~/delme3. sitten käsketään: . ~/delme3
- tällätavoin siirretyt aliakset katoavat bootattaessa.

***

Elin viikon remonttimiesten pitämässä mekkalassa ja se palautti mieleeni kuinka moitteeton desimaalilukujen vertaaminen tulee suorittaa - tai vertaileehan tämä kaikkea muutakin.

Sillä lukujen matemaattisten arvojen vertaamista ei kannata tehdä matemaattisesti vaan suorittaaa vertailu tekstijonoilla - sillä matemaattinen vertaaminen rajoittaa merkkilukua erittäin pahasti, lukujärjestelmästä toiseen siirtyminen on aina ongelma - mutta jos molemmat luvut on esitetty samassa lukujärjestemässä ei tekstijonovertailussa tarvitse edes tietää mikä lukujärjestelmä on käytössä. Ja lopullinen niitti tulee jos vertailtavissa luvuissa on tekstiäkin - pelkän tekstin kunnollinen vertaaminen on kylläkin mahdotonta mutta sanottaisiinko että kyllä se usein onnistuukin ja ainakin tekstiä siedetään.

Kaikissa nykyisissä lukujärjestelmissä tunnetaan desimaalipiste ja sen merkityskin on sama - esimerkiksi luku: 10101010.101010 on 170.656250 - onhan tuommoisen muuntaminen vaikeaa ja siksi muiden
 lukujärjestelmien lukuja ei yleensä esitetä desimaalisina.

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

Ja ennenkaikkea tälle skriptille olisi helppo opettaa uusia temppuja, esimerkiksi tietellisesti esitettujen lukujen vertailu, merkkimuutoksia tai matematiikkan suorittaminen ennen vertailuja ... Mutta tämänhetken toiminta:

1. luvut jaetaan kokonaisosaan ja desimaaliosaan. Jos luku on kokonaisluku niin desimaaleiksi määrätään 0.
2. desimaalien vertaamisessa on yksi lisäsääntö: kummankin desimaaliosan tulee olla yhtäpitkä. Desimaaliosat tehdään yhtäpitkiksi lisäämällä kummankin desimaaliosan perään nollia niin monta kuin toisen desimaaliosassa on merkkejä.
3. verrattavien etumerkit huomioidaan.
4. desimaalipiste jätetään pois kun kokonaisosa ja desimaaliosa yhdistetään. Näitä lukuja sitten verrataan
5. ensiksi täytyy testata yhtäsuuruus ja vasta senjäkeen suuremmuus - kaikki testit tehdään tekstijonovertailuna joten vertailtavien lukujen merkkimäärä on täysin rajoittamaton - yli tuhat numeroa tosin hidastaa jo vähän siitä normaalista 0.1 millisekunnista - siis tämä on melkein yhtänopea kuin erittäin rajoittunut matemaatinen vertailu BASH:in matematiikalla - ja paljon nopeampi kuin vertailu bc:llä.
 
Koodia: [Valitse]
function kumpionsuurempi () {
[[ $1 =~ .*\..* ]] && luku1=$1 || luku1=$1".0" # on tarpeen että luvussa on yksi desimaalipiste -
[[ $2 =~ .*\..* ]] && luku2=$2 || luku2=$2".0" # joten piste lisätään sellaista jos ei vielä ole
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
 
koko1=${luku1%%.*};koko2=${luku2%%.*};koko1=${koko1:=0} ;koko2=${koko2:=0}

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

apu=$desi2;desi2=$desi2$(echo $desi1 | tr [0-9] 0); desi1=$desi1$(echo $apu | tr [0-9] 0) #; echo desi1korjattuna:$desi1; echo desi2korjattuna:$desi2

luku1=$koko1$desi1; luku2=$koko2$desi2 # echo luku1:$luku1; echo luku2:$luku2

[[ $luku1 = $luku2 ]] && echo 'luvut ovat yhtäsuuria' || {
case $m1$m2 in
-+) echo 'toka on suurempi' ;;
+-) echo 'eka on suurempi' ;;
++) [[ $luku1>$luku2 ]] && echo 'eka on suurempi' || echo 'toka on suurempi' ;;
--) [[ $luku1>$luku2 ]] && echo 'toka on suurempi' || echo 'eka on suurempi' ;;
esac ;};}

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

# tai:
kumpionsuurempi 12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567892 12345678901234567890123456789012345678901234567890.12345678901234567890123456789012345678901234567891
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.03.23 - klo:15.18
BASH:ille on jo kauan sitten kehitetty menetelmä jolla voi 'esikääntää' funktioita - siis tavallaan tehdä uusia komentoja. Niiden avulla olisi siis saatu BASH:illekin  esimerkiksi salaman-mopea desimaalimatematiikka ja vaikka mitä muitakin kykyjä. Se oli jo toimiva menetelmä - muutamat tekivätkin uusia käskyjä - mutta nyt se on jo melkein kuoliaaksi vaiettu ominaisuus mutta todella kovanluokan asia. Kumpikohan BASH:in suur-guruille on miellyttävämpi mainesana: typerys vai liero?
 
- modulien kääntämisestä: https://mywiki.wooledge.org/BashLoadableBuiltins
- tottakai modulin kääntäminen on melkoisen vaikeaa 'ei-ammattilaiselle'. Mutta niinhän alkuunsa kaikki - ja mihinkähän kehitys olisi johtanut mikäli modulien tekemistä olisi jatkuvasti harrastettu? Sitäpaitsi toistenkin käännökset toimivat kunhan koneissa ei ole liikaa eroja.
- eväthän puolivillaiset kyvyt ja jonkinlainen nopeus vielä riitä mihinkään. Mutta mitähän virtuoosit saisivat aikaan jos vain jatkuvasti kääntäisivät moduleita?

***

Täytyyhän ihmisten oppinsa jostain saada mutta BASH:in suhteen en tosiaan käsitä mistä. Onhan noita 'Advanced guide':ja niinkuin BASH-raamattujakin - kyllä ne perusteet hyvin kertovat eikä niitä virheistä uskalla syyttää mutta niissä on aikapaljon osatotuuksia - ne estävät kehityksen.   

Ja kaikkia oppikirjoja koskee se että esitetään vain sellaista mitä kaikki muutkin esittävät - aivankuin kellään ei olisi omia mielipiteitä - eivätkä ne edes taida välitää. Sillä BASH on niin laaja ettei kenenkään oikeastaan tarvitsisi kulkea samoja polkuja kuin toiset. Alkeet toki ovat samat kaikilla - miksi siis nimi: Advanced guide?

Tosin erilaisia Linux:eja on lukemattomia, niissä erilaisia pääte-ohjelmia ja vaikka mitä muutakin ihan erilaista ja vallankumouksellista. Miksei ihmiset usko että distron tulee olla super-suosittu, sen perusversio eikä siinä saa olla mitään mikä ei ole valtavirtaa? Jopa lisäpakettien lataaminen on vähän arveluttavaa - voi sitä vähän harrastaa mutta pelkästää virallisista varastoista.

Mutta loppujenlopuksi se mikä merkitsee on oppi siitä kuinka saat apua ongelmissasi - se on nopein tie oppimiseen. Ja taito luntata ja kyky oppia omista ja toisten kömmähdyksistä ja saavutuksista on korvaamaton. Mutta harva meistä kuuntelee edes itseään - puhuu yhtä ja tekee toista - ihmiset huijaavat ennekaikkea itseään.

Eikä tuo opetuksen puute yksinomaan BASH:ia koske vaan myös kieliä bc, awk ja sed jotka ovat todella kovanluokan funktioita - niille sopivissa tehtävissä ne ovat paljon-paljon parempia kuin BASH - ja ne ovat aina käytettävissä tekemättä mitään. Voi noita skriptikieliä käyttää itsenäisinäkin skriptikielinä - awk-skriptejä näkeekin sillointällöin - mutta melkein aina BASH on se joka liimaa kaiken yhteen.

***

Kaikki BASH:ista esitetty kuvastaa sitä että siitä halutaan eroon - erinomaisia työkaluja on vaikka mihin - mutta niitä ei opeteta käyttämään.

Ja kaikki on niin epäjärjestyksessä että se taitaa olla tahallista - monikin on yrittänyt luoda järjestystä mutta toiset ovat samaanaikaan pitäneet huolen siitä että kaikkea uutta tulee lisää niin nopeasti että pakka pysyy sekaisin - tullessaan kaikki on muka vallankumouksellista mutta häviää niin nopeasti ettei sitä kaikki edes havaitse. Kieltämättä paljonkin loistavia hommia tehdään - mutta kymmeneessä vuodessa ne rappeutuvat melkein käyttökelvottomiksi.

Silloin aikoinaan kun yhteisöt pullistelivat skriptaajia saattoi halutessaan vaikka mennä keskustelemaan virtuoosi-joukon kanssa joita oli kaikkialla joten ei tämmöinen asioita täysin turmellut. Mutta nykyään kun esimerkiksi kohtaa käskyissä ongelmia niin eipä siinä sillähetkellä muuta keinoa ole kuin lukea käskyn man-sivu - muuten man-sivujakin on erilaisiin tarkoituksiin ja voit vain toivoa että saat eteesi sellaisen josta ymmärrät jotakin. Ja vaikka man-sivut ovatkin käyttökelpoisia pitempään skriptanneelle niin noviisille ne ovat melkolailla käyttökelvottomat sillä niissä on hyvin vähän esimerkkejä. Sillä vaikka kaiken selittäminen esimerkeillä on mahdottoman hidasta niin jo muutama esimerkki kertoo erittäin paljon. Ja minkätakia kaikki pitää tehdä hyvin vaikeaksi - kuvaannollisesti viisaudenhammas poistetaan peräreiän kautta ja se esitetään esimerkkinä.

Info-sivut ovat hieman parempia - kirjoita päätteessä esimerkiksi: info ls - mutta moniko niitä info-sivuja lukee? Sitäpaitsi kaikesta on niin paljon sanottavaa että jos ei aseta haluilleen rajaa tulee jokaisesta ohjeesta niin laaja ettei sitä kukaan viitsi lukea.

BASH:issa on myös työkalu jolla löytää niitä käskyjä jotka saattavat sopia ongelman ratkaisemiseen - siis mitä man-sivuja pitäisi lukea. Moniko on tuosta apropos-käskystä edes kuullut?

Samoin: 'whatis komento' kertoo mitä komento tekee - noin teoriassa. Siis esimerkiksi: whatis xargs

Ja kaikissa noista tulee esiin kieliongelmia - pahin kieliongelma on se että jotakin on suomennettu ja valtaosaa ei.

Siis neuvoja ja työkaluja on vaikka kuinkapaljon, joten heti alkuunsa saa niin monta teoreettista, epäjärjestyksessa esitettyä, ristiriitaista ja eri aikakausilta olevaa neuvoa että niitä joutuu pureskelemaan loppuelämänsä. Joten luojankiitos että meillä on tämä foorumi - täällä saa neuvoa ongelmiinsa eikä saa maailmankaikkeudesta kahta teoreettista esitelmää.

Googlaamalla saa kyllä neuvoja vaikka mihin - sillä olipa ongelmasi mikähyvänsä niin monet ovat kohdanneet saman ongelman ja todennäköisesti monikin on ratkaissut sen ja laittanut ratkaisun nettiin. Mutta koska netissä on niinpaljon roskaa kestää jonkinaikaa oppia löytämään ne kultajyvät. Vähänhän niitä kultajyviä on ja niiden kerääminen isosta joukosta on hidasta - ja lopputuloksesta on viisainta kirjoittaa itselleen selostus. Joten itse on nykyään ohjeensakin tehtävä.

Skriptaaminen on jokaiselle erilaista - toiselle tärkeä ei toiselle merkitse mitään. Ja oppimisessa tämä tarkoittaa sitä että tärkeän opit kerrasta ja mielestäsi merkityksetöntä et opi koskaan kunnolla - ja kerrasta oppinuthan ei ratkenneista ongelmista puhu mitään sillä hänen mielestään niiden ratkaisu on itsestäänselvää. Mutta se epävarma puhuu sitäkin enemmän.

***
 
Aivot toimivat parhaiten kun niitä ei hiillosta - ei tosiaankaan kannata yrittää parannella väärin toimivaa skriptiä kovin kauaa kerrallaan sillä jos homma ei onnistu melko nopeasti niin se merkitsee sitä että yrittää väärällä tavalla - eikå yksikään ihminen osaa muuttaa uskomuksiaan kovinkaan nopeasti - vaan välillä pitää tehdä jotain muuta - ja aivot toimivat samalla taustalla siinä alkuperäisessäkin tehtävässä   

***

Jos puhuu paljon ja varsinkin BASH:ista niin ilmanmuuta puhuu myös aikapaljon potaskaa - ja omien virheiden myöntäminen on jokaiselle vaikeaa. Samoin meidän jokaisen työnjälki jättää toivomisen varaa. Mutta ei sillä ole merkitystä - vain yrittämisellä on.

Eikä kukaan pysty tekemään yhtään skriptiä jota ei joku toinen pystyisi parantamaan - usein jopa kymmeniä kertoja nopeammaksi samalla poistaen virheitä.

Ne jotka eivät koodaa eivät koskaan tule ymmärtämään sitä että koodarin on pakko olla koodatessaaan toisessa maailmassa ja että täydellinen työrauha on suotava. Mutta ehkä häiriöt ovat hyväksikin koska ei ihmisen ole hyvä hukuttautua unelmiinsa kovin pitkäksi aikaa - se vain on syytä muistaa että toiset eivät tule kykenemäänkään ymmärtämään joten ei häiriöistä kannata perhesopua rikkoa.

***

Muutin neliöjuuren laskemisen toimimaan kaikilla reaaliluvuilla - kyllä se edelleen laskee vain yhdeksän desimaalia mutta uudessa skriptissä sille syötetty luku saa olla kuinka suuri tai pieni tahansa ja kuinka moninumeroinen tahansa ja vaikka tieteellisessä esitysmuodossa. Tuloste on aina tieteellisessä esitysmuodossa.

Mutta taas kertaalleen ihmettelin sitä että emme voi tunnustaa että keskimäärin olemme keskinkertaisia - eivätkä edes huippuälykkäät ole kovin älykkäitä.

Harkitsinkin kauan ennenkuin aloitin tekemään tätä skriptiä luullessani että se on tosi-iso tehtävä. Sillä en edelleenkään oikein tajua kuinka yksinkertaisia kaikki ratkaisut BASH:ille ovat - vaikeaa on vain löytää se yksinkertainen ja moitteeton tapa. Sillä meillä kaikilla on sama synnynnäinen vika: kun alamme tehdä jotakin niin emme koskaan usko että edes voimme yrittää väärällä tavalla joten kaikki jo tehty täytyy heittää roskikseen ja aloittaa ihan alusta.

Hakiessani ratkaisuja muutamiin ongelmiin tehdessäni tätä skriptiä etsin ratkaisuja lukemattomilta verkkosivuilta. Oletetaanpa että johonkin ongelmaan annetaan jonkun keskustelukerhon verkkosivulla sata erilaista ratkaisua - ensimmäiset niistä ovat usein melko vaatimattomia mutta usein noin kymmenes ratkaisunesittäjä esittää täysin erilaisia ja loistavia ratkaisuja. Mutta ei se mitään vaikuta sillä niitä tuhatkertaa huonompia ratkaisuja esitetään edelleen - ja jopa runsaammin kuin ennen. Onneksi olen oppinut kuinka ne timantit erotellaan siitä isosta joukosta.

- nyt myöhemmin havahduin siihen miltä tuo LANG=C vaikuttaa - mutta se siis tekee vain sen että käsky: printf käyttää desimaalipistettä eikä pilkkua. Nuo aakkostuksen asetukset muuten vaikuttaa vaikka missä - jopa sorttaaminen nopeutuu usein.

- samassa lauseessa käsky: (( $apu & 1 )) testaa parillisuutta.

Koodia: [Valitse]
function sqrt () {
apu=$(LANG=C printf "%.17e\n" $1); mant=${apu%%e*};apu=${apu##*e}; (( $apu & 1 )) && kerroin=10 || kerroin=1; expo=$(($apu/2));   
in=${mant//./}"000000000000000000"; in=${in:0:17}
sqrt=2110000000; delta=1005000000
for ((i=0;i<31;i++)); do
   x=$(($sqrt*$sqrt/$kerroin))
   (( $x>=$in )) && sqrt=$(($sqrt-$delta)) || sqrt=$(($sqrt+$delta))
   delta=$(($delta/2))
done 
echo ${sqrt:0:1}.${sqrt:1}e$expo ;}

echo -e 'ylempänä tulos kalkulaattorista ja alla tästä skriptistä\n'; juurrettava=27.345; LANG=C printf "%.17e\n" $(bc -l <<< "sqrt($juurrettava)");time sqrt $juurrettava

***


Oikealla tavalla tehtyjen skriptien kokeileminen on tosihelppoa - leikkaa liimaa skripti verkkosivulta päätteeseen ja siinä kaikki - paina vain return. Ja skriptien ajaminen uudestaan uusilla paramerteilla on vielä helppompaa sillä kaikki tarvittava on jo koneessasi - painat vain näppäintä nuoli-ylös niin skriptin ajava käsky palaa näytölle editoitavaksi - ja funktiot ovat jo muistissa.
- nuoli-ylös nappulassa on yleensä ylöspäin osoittava kolmio.

***

Sain aikaiseksi luotettavan version desimaalilukujen kertolaskusta. Kerrottavat voivat liikkua alueella: .000000001 - 999999999.99999999 - joko positiivisina tai negatiivisina.  Kyllä kokonaisluvutkin hyväksytään.

Skripti toimii alle millisekunnissa jos laskinohjelman nimeltä bc:n jättää pois - sillä tässävaiheessa skriptin oikeellisen toiminnan osoittamiseksi on tulosteessa hyvä olla mukana varmasti oikea tulos - ja sen saa esimerkiksi matematiikka-ohjelmasta bc joten nykymuodossa skripti kestää yli 10 ms - bc:tä on pakko käyttää  sillä hyväkään normaali kalkulaattori ei kaikkiin näihin laskuihin kelpaa. Ylitäpä sallittu lukualue ja kyllä tekemäni skripti alkaa höpertää mutta bc ei.

Esitänpä skriptin toimintaa esimerkillä - Käske: kerro18 -999999999.99999999 999999999.99999999 . Tulostuu neljä riviä:
 
Lasku:-999999999.99999999*999999999.99999999  . Ylempi tulosterivi on matematiikkaohjelmasta ja alarivi tästä skriptistä:
-999999999999999980.0000000000000001
-999999999999999980.0000000000000001
Koodia: [Valitse]
function kerro18 () {
tulosta=: # päätös tulostetaanko välituloksia. Vaihtoehdot:tulosta=echo ja tulosta=:
[[ ${1:0:1} = - || ${2:0:1} = - ]]  && merkki=- || merkki=''
[[ ${1:0:1} = - && ${2:0:1} = - ]]  && merkki=''
apu1=${1//\-/}; apu2=${2//\-/}
[[ ${apu1//[^.]/} ]] && luku1=${apu1:0:18} || luku1=${apu1:0:18}".0"
[[ ${apu2//[^.]/} ]] && luku2=${apu2:0:18} || luku2=${apu2:0:18}".0"
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
echo; echo tulos laskusta: $1 \* $2'  . Ylärivi on bc:stä ja alarivi tästä skriptistä:'
bc<<<"scale=20; $1*$2"
apu=$summa2$summa1; [[ ${apu: -2} = '00' ]] && echo $merkki$((10#${apu:0: -$desimaaleja})) || echo $merkki$((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} ;} 

kerro18 -999999999.99999999 999999999.99999999

***

Olin jo pitkällä pätevän desimaalilukujen jakolaskun kehittämisessä kun alkoi edellinen kertolasku kiinnostaa uudestaan sillä sehän on 18+18 numeroinen desimaalikertolasku ja tekemällä teoriassa yksinkertaisen muutoksen saa sen desimaalipisteen-paikan liukuvaksi jolloin se itseasiassa olisi kunnollinen 36-numeroisten desimaalilukujen kertolasku. Ja muutoksen seuraukset olisivat arvaamattoman suuret - kyse ei ole nopeudesta sillä BASH on niin hidas ettei suurikaan nopeutus tunnu missään vaan kyse on siitä onnistuuko asia ollenkaan - ja 32 desimaalinen kertolasku tekisi monista asioista mahdollisia.

Puhumattakaan uudesta 9+9 kertolaskusta josta saisi tehtyä nopean 18-desimaalisen kertolaskun. Mutta koetetaan palata maanpinnalle ja jatkaa jakolaskua.

****

Matematemaattisen toiminnon virheet paljastuvat varmuudella vasta kun sitä käytetään paljon. Se että laskut virheilevät alkuunsa on normaalia mutta jos niitä virheittä ei saa nopeasti korjattua niin se on paha - ja jos ei saa korjattua ollenkaan niin roskikseenhan menetelmä joutuu.

Ja nopeasti korjaamisessa tulee esiin se että eihän se vikojen löytäminen onnistu ellei kokeilijoita ole paljon. Seuraavasta desimaalilukujen jakolaskusta tämä näkyykin erittäin selvästi - sillä sen toiminta täytyy tällähetkellä luokitella kuuluvaksi henkimaailman kummallisuuksiin. Ja koska virtuoosit ovat saattaneet BASH:in tilaan jossa sitä ei kukaan halua käyttää niin tilanne taitaa olla lukossa.

Kumpa virtuoosit olisivat alkaneet kehittää tämmöisiä desimaali-lasketaan sopivia skriptejä jo 80-luvulla - sillä nämä olisivat toimineet jo silloin. Eiköhän kymmenessä vuodessa olisi saatu desimaali-funktiotkin joko virheettömiksi tai roskikseen. Nyt ei sitä runsasta käyttöä voi enää tulla sillä yhteisöhän on siirtynyt jo Pythoniin.

Nähtävästi virtuoosit ovat silloin kauan siten halunneetkin siirtyä Pythoniin - ja sehän tarkoittaa sitä että BASH laitetaan eläkkeelle. Ihan oikeinkin se olisikin jos tosiaan se uusi kuningas on jo sillähetkellä parempi.

Ei muuten ollut kuten aikaisemmista jutuistani saat luettua - ja desimaali-laskennan kieltäminen on muuten vasta kolmanneksi merkittävin syy - puhun kieltämisestä sillä virtuoosit ovat ilmanmuuta tienneet desimaalilaskennan toimivan. Tällähetkellä Python on mielettömästi parempi sillä sitähän pataljoona ukkoja korjailee monesti joka viikko ja BASH:ia vain yksi ukko kerran vuodessa.

Elikä taisi olla niin etteivät virtuoosit halunneet BASH:ia käyttää sillä se on kovin liukas käsiteltävä heillekin. Sillä mitä BASH:ista olen kaivannutkin niin sen olen löytänyt - tai itseasiassa muut ovat löytäneet sillä olipahan ongelmani mikähyvänsä niin joku on ratkaissut sen - ja ongelmaksi jääkin enää vain tuon ratkaisun löytäminen. Olenkin nykyään lakannut yrittämästä kehittää ongelmiini ratkaisua itse, sillä maailmalla joku on varmasti kehitänyt jo  paremmankin ratkaisun mitä minä itse pystyisin keksimään.

Mielenkiintoinen homma muuten tuo kultajyvien hyljeksiminen - varmaankin koska virtuoosit eivät oikestaan koskaan keksi niitä itse ja joutuisivat myöntämään surkimusten olevan parempia. 

Ulkonäöltään nämä desimaalimatematiikan funktiot ovat kamalia. Mutta niin ovat vastaavat toiminnot kaikissa muissakin kielissä - esimerkiksi monet kielet hakevat matematiikassa apua prosessorin matematiikka-osiolta ja noiden matematiikkaosioiden rautaan langoitetut ohjeet ovat vielä kamalampia.

Ja mikäli jokin kieli suorittaa matematiikkansa ohjelmallisesti ovat laskujen tarvitsemien funktioiden  inhoittavuudet kätketty kirjastoihin joista niitä ei koskaan näe. Mutta BASH on opetuskieli ja alkuperäinen tarkoitus oli että käyttäjät tekisivät itse kirjastonsa.



Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 08.04.23 - klo:06.48
Ei jakolaskun koodi vielä moitteeton ole, mutta kelvannee alkuunsa:

Koodia: [Valitse]
function jaa () { (( ! $# )) && echo funktion ajokäsky on esimerkiksi: jaa 1 2   .Siitä pitää tulla: .500000000000 && sleep 2 && return
 
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."

desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

desi1=${luku1##*.}; desi2=${luku2##*.}
(( ${#desi2} >= ${#desi1} )) &&
{ apu=$desi1"000000000000000000"; desi1=${apu:0:${#desi2}} ;} || { apu=$desi2"000000000000000000"; desi2=${apu:0:${#desi1}} ;}; apu=''

apu=$(($kokonaisosa1$desi1/$kokonaisosa2$desi2)); tulosteessakokonaisia=$((${#apu})); [[ $apu -eq 0 ]] && tulosteessakokonaisia=0
apu=$(($kokonaisosa2$desi2/$kokonaisosa1$desi1)); apu=${#apu} && tulosteessaetunollia=$(printf "%0.s0" $(seq $apu)) || tulosteessaetunollia=''

luku1=$kokonaisosa1$desimaaliosa1
luku2=$kokonaisosa2$desimaaliosa2

unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta

for n in {1..5}; do # muodostetaan tulos-palasia oli:18 esimerkiksi 9 merkkiä kerrallaan
luku1=$luku1'0000000000000000000' && luku1=${luku1:0:17}; apu=$(($luku1/$luku2)); tulos[$n]=${apu}; echo -n $luku1' '$luku2' '$apu' ';
luku1=$(($luku1%$luku2)); echo $luku1
done

for n in {1..5}; do tulos=$tulos${tulos[$n]}; done

echo "tulos jakolaskusta: "$1"/"$2" . Vertailua varten ylempi tulos laskentaohjelmasta ja alempi tästä skriptistä"
bc<<<"scale=60; $1/$2" | tr -d '\\\n'; echo
[[ ${tulosteessaetunollia:1}  ]] && echo .${tulosteessaetunollia:1}$tulos || echo ${tulos:0:$tulosteessakokonaisia}.${tulos:$tulosteessakokonaisia} ;}
 
jaa 123456789.1234567 1234.23

   

***

Esimerkkituloste:

tulos jakolaskusta: 123456789.1234567/1234.23 . Vertailua varten ylempi tulos laskentaohjelmasta ja alempi tästä skriptistä
100027.376682998063569999108756066535410741920063521385803294361666
100027.3766829980635699991087560665354107419200635213858032

***

tai otetaan toinen laskettava, muutetaan koodia niin että se laskee lisää desimaaleja joten se kuluttaa  laskussa hieman lisää aikaa - näin lasku kestää peräti 7ms:

tulos jakolaskusta: .1234567/1234.23 . Vertailua varten ylempi tulos laskentaohjelmasta ja alempi tästä skriptistä
.000100027304473234324234542994417572089480890919844761511225622452865349246088654464726995778744642408627
2412759372240182137851129854241105790654902246744934088460011505148959270
.000100027304473234324234542994417572089480890919844761511225622452865349246088654464726995778744642408627
2412759372240182137851129854241105790654902246744934088460011505148959270

***

Desimaaliluku on tekstijono - BASH:issa numeron erottaa tekstijonosta vain se että numeroissa ei voi olla kirjaimia. Jos desimaalilukua kuvaavasta tekstijonosta poistaa desimaalipisteen niin sen voi ihan hyvin kertoa-jakaa-ynnätä ihan normaalisti sillä numeroiden merkitys on sama demaalipisteen kummallakin puolella - ongelmana on vain palauttaa desimaalipiste oikealle paikalle.

Desimaalimatematiikka alkoi seuraavasta yksinkertaisesta skriptistä:

Koodia: [Valitse]
function kerro9 () {
luku1=$1
luku2=$2
kokonaisosa1=${luku1%%.*};
kokonaisosa2=${luku2%%.*};

apu=$(($kokonaisosa1*$kokonaisosa2))  # näimä desimaalilaskut suoritetaan aina normaalilla matematiikkalla.

kokonaisia=${#apu}
tulos=$((${luku1//./}*${luku2//./}))
bc<<<"scale=18; $1*$2"
echo ${tulos:0:$kokonaisia}.${tulos:$kokonaisia} ;}

kerro9 15.12345 -1.22222222222



Tulos:
-18.4842166666330590
-18.4842166666330590

Huomautuksia:
- skriptissä mukana oleva bc antaa vain varmasti oikean tuloksen mihin verrata - eikä se laskuihin mitenkään sekaannu - mutta onhan se mukava nähdä heti toimiiko skripti oikein - ja bc:n kutsun voi vaikka kommentoida poiskin. Ilman bc:tä skripti nopeutuukin 2ms:ta lähes nollille.
- BASH:in mollaaminen oli alussa sitä että väitettiin ettei se desimaaleja hanskaa ja lisäksi se on sikahidas. Mutta kyllä lähes 0ms on BASH-skriptiltä hyvä suoritusaika ja onhan skripti kieltämättä myös desimaalilaskentaa. 

***

Yhteenvetona desimaalilaskennasta:

- BASH:ssa on toteutettuna vain perus-laskutoimitusten kokonaisluku laskenta.
- desimaalilaskuja varten täytyy tehdä funktioita. BASH:in mukana toimitetaan runsaasti funktioita ja miksei joitain desimaalilaskennam funktiota ole toimitettu myös voi olla ainoastaan pahantahtoisuutta. Ohjelmallinen ratkaisu on kylläkin kymmenenkertaa hitaampaa kuin kielen oma mutta tällä tasolla toiminta on niin nopeaa ettei sitä aina edes huomaa.

Ohjelmallisestikin toteutetuissa on eritasoisia - kaikille laskutyypeille useita omiaan - ei edes hyväksyttävää mutta kuka tietää mihin tämä alku johtaisi? Tässä on esimerkkinä kertolasku:
- kerro9 on nopein - ei kovin tarkka mutta jo ihan käyttökelpoinen.
- kerro18 on neljäkertaa hitaampi mutta tarkkuutta on jo enemmän kuin useimmissa kielissä.
- taustalla luuraa kerro36 - tai itseasiassa rajoittamaton tarkkuus.
- myös tieteellisen esitystavan osaavan voisi tehdä - ihan ajankulunaan, ei sillä merkitystä olisi. Mutta se olisi vielä hitaampi - toisaalta se hakkaisi tarkkuudessa useimmat.


- Neliöjuuri, logaritmi, anti-logaritmi ja semmoiset ovat toistaiseksi niin hitaita ettei niitä kannata BASH:illa laskea. Mutta ne onnistuvat kyllä. Vaikka seitsemännen asteen elliptiset integraali-funktiot onnistuvat.

***

Nämä desimaalilaskut toimivat aivan niin kuin teoriat ovat aina sanoneet. Sitä ei alussa voinut tietää onko BASH:issa kaikki tarvittavat toiminnot mutta osoittautui että kyllä on - mutta ne ovat niissä iänikuisen vanhoissa ja salamannopeissa 'risuaita-käskyisssä'. Mitään uutta ei tarvinnut kehittää - nämä ovat toimineet aina. Monikin on ilmanmuuta tehnyt samat funktiot - sitä en käsitä minkätakia tieto ei ole liikkunut - onko syynä naurunalaiseksi joutumisen pelko? - sillä onhan tämä näurettavaa väittää professorien iskeneen kätensä leppään.

Lisäksi desimaali-laskuista saa jatkuvasti kehitettyä parempia. Juuri valmistui 32 numeroisten desimaali- tai kokonaislukjen numeroiden kertolasku joka kestää 2ms. Ja se avaa helpon tien 72 numeroisten nopeaan kertolaskuun. Tietenkään näillä ei ole enää muuta merkitystä kuin sen painottamisessa että BASH on pilattu uusilla käskyillä ja niiden toimintatavoilla.
- muuten desimaalimatematiikan useimmat käskyt ovat tekstinkäsittelyn käskyjä ja matematiikkaa on vain nimeksi. Joten varmaankin tekstinkäsittely sujuu vielä paremmin.

Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 13.04.23 - klo:12.31
Käyttökelpoisen desimaalilaskennan myötä tuli tarve desimaalilaskennan apu-funktioille - kokonaisluku laskennassa apu-funktioiden merkitys on pieni - ja jos ei käytä 'risuaita-käskyjä' niin funktioista tulee todella hitaita. Mutta tässä niitä nopeita apu-funktioita on:
Koodia: [Valitse]

function floor () {
[[ ${1//[^.]/} ]] && luku=$1'.0' || { echo $1; return ;}
kokonaisosa=${luku%%.*}
[[ ${1//[^-]/} ]] && echo $(( $kokonaisosa-1 )) || echo $kokonaisosa 
# mikäli desimaalipistettä ei ole niin palautetaan luku sellaisenaan ja lopetetaan heti
# mikäli desimaalipiste on niin palautetaan positiiviselle luvulle sen kokonaisosa ja jos se on negatiivinen niin palautetaan kokonaisosa -1
}

floor 1.5

###

function ceil () {
[[ ${1//[^.]/} ]] && luku=$1'.0' || { echo $1; return ;}
kokonaisosa=${luku%%.*}
[[ ${1//[^-]/} ]] && echo $kokonaisosa || echo $(( $kokonaisosa +1 ))
# mikäli desimaalipistettä ei ole niin palautetaan luku ja lopetetaan heti
# mikäli desimaalipiste on niin palautetaan positiiviselle luvulle kokonaisosa+1 ja jos se on negatiivinen niin palautetaan kokonaisosa.
}

ceil 1.5

###

function abs () { echo ${1//-/} ;}  # echo ${1#[-+]} toimii myös ja + merkkikin on joskus pahis.

abs 1.5

###

function int () {  echo ${1%%.*} ;}

int 1.5

###

function fract () { [[ ${1:0:1} = - ]]  && merkki=-; echo $merkki.${1##*.} ;}

fract -1.5

###

function getexp { echo ${1##*[eE]};}

getexp 123.456e789

###

function sci_to_normal () { luku='00000000000000'${1%%e*}; luku=${luku//./}; apu=${1%%.*}; despoint=${#apu}; exp=${1##*e}; decimals=$(($despoint+$exp+14)); tulos=${luku:0:$decimals}.${luku:$decimals}; echo ${tulos/*(0)/} ;} # poistetaan turhat etunollat

sci_to_normal 1.234567890123456e-5

###

function normal_to_sci () { mant=${1%%.*}; exp=${#mant}; luku=${1//./}; echo ${mant:0:1}.${luku:1}e$exp ;}

normal_to_sci  123456789012356789012345678901234567890.123456789

###

function poistaetunollat () { echo ${1/*(0)/} ;}

poistaetunollat 0000010203.40600

***
 
function poistatakanollat () { apu2=${1##*[1-9]}; echo ${1:0: -${#apu2}} ;}

poistatakanollat 000102.0708000

###

function poistatakapiste () { [[ ${1: -1} = . ]] && echo ${1:0: -1} ;} # turha desimaalipiste

poistatakapiste 1234.

###

function siisti () { local apu ;local apu2; local apu4 ; apu=$( echo ${1/*(0)/}); apu2=${apu##*[1-9]}; apu4=${apu:0: -${#apu2}}; [[ ${apu4: -1} = . ]] && echo ${apu4:0: -1} || echo $apu4 ;}

siisti 000102304.0200

- siis tuossa viimeisessä poistetaan turhat etu- ja takanollat ja desimaalipistekin jos se jää viimeiseksi.
- sen koodissa on  muutakin selitystä kaipaavaa: koska funktioiden halutaan toimivan nopeasti toimivat ne samassa prosessissa kuin kutsujakin. Kun funktiossa muutetaan jonkun muuttujan arvoa niin se muuttaa muuttujan avoa kutsujassakin. Joskus tämä kielletään kun halutaan varmistaa ettei funktiolla ole tämänkaltaisia sivuvaikutuksia - edellisessä funktiossa kielto on: local apu ;local apu2; local apu4 - siis että nuo muuttujat tunnetaan yksinomaan funktiossa ja funktiosta palattaessa ne tuhotaan. Local-määrittelyä saa käyttää vain funktiossa. Mikäli funktiossa kutsutaan jotain toista funktiota niin sinne se arvo kyllä siirtyy - sen taas voi estää kirjoittamalla local sinnekin.
- nopeuteen määrittely ei paljoakaan vaikuta.
- toimii local niinkin päin ettei pääohjelman samannimiset muuttujat pääse häiriköimään funktiossa.
- 'kaikenkielto' tapahtuu kun prosessi siirretään omaan prosessiinsa. Tämä tapahtuu muuttamalla funktiomäärittelyssä uloimmat aaltosulut kaarisuluksi. Mutta omaan prosessiin siirtyminen on hidasta ja funktion suhteen johtaa vaikeuksiin.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 15.04.23 - klo:22.05

BASH:ia pidetään surkean hitaana kielenä. Mutta se vanha 'risuaita-BASH' toimii yhtänopeasti kuin muutkin kielet mutta kärsii siitä, ettei 'risuaitatoimintaa' ole päivitetty miesmuistiin vaan tehty täysin epäonnistunut uusi BASH joka tosiaan tökkii - eikä virtuooseissa ole peruutusvaihdetta, kykyä palata vanhaan kun uusi osoittautuu epäonnistuneeksi.

Mutta kumpikin BASH toimii aivan toisin kuin muut kielet. Esimerkiksi BASH:issa ei lasketa tyyliin: x=1+2 vaan x=$(ynnää 1 2). Kieltämättä hankalaa ensialkuun mutta hämmästyttävän nopeasti siihen tottuu - eikä se myöhemmin edes hidasta.

On muissakin kielissä omituisuuksia, esimerkiksi matematiikan reverse-polish: abc-de+*+ :stä on kiva arvella mitä se tekee. Ja BASH:issa bc:n edeltäjä nimeltään dc oli BASH:inkin käyttäjille niin omituinen että sitä dc:tä nykyään kutsuu kuori-ohjelma bc. Siis  myös funktioiden käyttämiseen voisi tehdä kuori-ohjelman - mutta luotettavan kuori-ohjelman tekeminen vaatii parempaa ukkoa kuin minä.

***

Joissain tapauksissa myös sed, awk ja bc romahdutavat nopeuden. Ihmettelin aikoinaan kun eräs virtuoosi sanoi että niitä sopii välttää kun todellista nopeutta etsitään. Ihmettelin koska nuo ohjelmat loistavat nopeudellaan, monipuolisuudellaan ja sillä että ne helpottavat suunnattomasti myös BASH-skriptin tekemistä. Mutta 'risuaidoilla' toteutettuja skriptejä ne tosiaan hidastavat paljon - tuli siis virtuoosi paljastaneeksi että hänkin käyttää 'risuaita-käskyjä'.

Mutta on kovin vaikeaa muistaa mikä 'risuaita-käskyjen' kirjainryteikkö toimii mitenkin - ja paljon tietoa on kadonnutkin - ja erilaisissa linuxeissa hommia on toteutettu hieman erilailla joten käskyn toimiminen eri koneissa on pikkuisen onnenkauppaa.

'risuaita-käskyjen' paremmuutta ei huomaa ennenkuin skriptin jokainen käsky on 'risuaita-käsky' sillä yksikin 'normaali-käsky' romahdutta nopeuden - esimerkiksi noissa laskenta-skripteissä on 'risuaita-käskyjä' kymmeniä ja ne vievät aika vähemmän kuin yksi 'normaali-käsky'. Muuten 'risuaita-käsky' ei välttämättä ole ulkonäöltään risuaita - esimerkiksi echo on 'risuaita-käsky'.

Jos BASH:ia käytettäisiin oikein niin sen toiminta olisi ihan hyvää. Mutta se edellyttäisi jo tehtyjen ominaisuuksien kasaamista järkeväksi kokonaisuudeksi ja siinä olisi suunnaton työ - niin suunnaton että virtuoosien joukko kalpeni kauhusta, siirtyi Pythoniin ja senjälkeen alkoi tylyttämään BASH:ia - varsinkin kun eivät uskaltaneet tunnustaa tyrineensä BASH:in tärviölle.

Mutta itseasiassa ei merkitse mitään onko kieli hyvä tai huono kunhan se osaa kutsua mitähyvänsä avukseen - ja BASH osaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 16.04.23 - klo:13.43
kerro18 - kertollasku-skripti (floating point) jolle voi antaa kerrottavaksi 18 desimaalisia desimaalilukuja  jolloin 36 desimaalinen luku tulee ulos.

bc tässä sitä aikaa kuluttaa. Bc ei osallistu laskemimieen  ollenkaan - se vain antaa varmasti oikean arvon johon verrata. Voit ihan hyvin vaikka poistaa sen rivin jolla bc on.

Kaikki seuraavat toimivat (tässäolevat ovat niitä vaikeita paikkoja - joten kaiken muunkin ptäisi toimia):

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


time kerro18  .00900007567899123  900.07000012345678
time kerro18  10 10
time kerro18  999999999999999999  999999999999999999
time kerro18 .9 2
time kerro18 .99999999999999999 .99999999999999999
time kerro18 .00000000000000001 .00000000000000001   
time kerro18  10000000000000000 .0000000000000001
time kerro18  .0000000000000001 10000000000000000   
time kerro18  1 1
time kerro18  01 .1
time kerro18  -1 1
time kerro18  1 -1
time kerro18  -1 -1


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 20.04.23 - klo:12.40
Olen jatkuvasti ihmetellyt miksi kun käskyjä tulee lisää näihin desimaalimatematiikan skripteihin niin tuntuu siltä ettei se vaikuta suoritusaikaan ollenkaan. Mutta kyllä se vaikuttaa: C-koodissa kuluu aikaa kymmeniä mikrosekunteja kauemmin - eikä BASH sitä edes huomaa.

En varmasti osaa käyttää oikeita määritelmiä joten sanon vain että mikäli koko skripti on pelkkää 'risuaita'-käskyä käännetään se kerralla kaikki joten BASH toimii senaikaa C:ssä olipa homma kuinka monimutkainen hyvänsä - yksikään tulkattu kieli ei voi pistää paremmaksi mutta tottakai pääsee samaan. Ja yhtävarmasti Python-fanit teilaavat ajatuksen - mutta vaikeaa se on sillä nämähän toimii ja jos skriptaaja olisi hyvä niin ne saisi virhettömiksikin.

'risuaita-käskyjen' lukumäärä on suuri ja niiden rakenteet noudattavat tiettyjä sääntöjä joten niitä 'löytää' itse lisää - oppii kuinka ne on tehty. Niitä on sekä käskyjonoille että matriiseille - tekstijonoja ne numerot ovat.

Onhan 'risuaita-käskyissä' murheellisiakin piirteitä: kyllä se ikä painaa niitäkin eikä niitä nykyään pystytä edes päivittämään - nimenomaan ei pystytä vaikka oikein käytettyinä ne ovat erittäin nopeita.

***

Esimerkiksi ollaan totuttu siihen että BASH:issa oltaessa ainoastaan sed kykenee muuttamaan inhimillisessä ajassa ison asiakirjan jonkun sanan toiseksi. Mutta siihen pystyvät risuaidatkin - ja melkein yhtänopeasti. Tosin tässäkin asiassa käskyt ovat jostainsyystä vaikeita muistaa joten niitä pitäisi ehdottomasti kyetä noutamaan kirjastosta.

Mutta kirjastojahan ei enää pidetä suotavina - kiitos virtuoosit - onkohan kyseessä virtuoosien keino kätkeä oma hölmöilynsä? Mutta jokatapauksessa tämmöinen se koko tiedostossa jonkun sanan muuttava käskyryhmä on (mutta muutokset tehdään ainoastaan näytölle eikä niiyä voi vahingossa tallettaa - se vaatisi todella monta muutosta. Tuo tiedosto on valittu siksi että se on jokaisessa koneessa ja rakenteeltaan se on tuttu):
Koodia: [Valitse]
sana1=BEGIN; sana2=ALOITA; < /boot/grub/grub.cfg readarray doku; printf "%s" "${doku[@]//$sana1/$sana2}"
Tai otetaanpa käsky joka poistaa pari merkkiä dokumentin jokaiselta riviltä - ensimmäinen rivien alusta ja toinen rivien lopusta:
Koodia: [Valitse]
< /boot/grub/grub.cfg  readarray array; printf "%s" "${array[@]/#??/}"
< /boot/grub/grub.cfg  readarray array; printf "%s\n" "${array[@]/%???/}"

Tai poistetaan jokaisen tekstirivin lopusta muuttujan määräämä määrä merkkejä:
Koodia: [Valitse]
montako=5; < /boot/grub/grub.cfg  readarray array; printf "%s\n" "${array[@]/%$(printf "%0.s?" $(seq $montako))/}".


Käskyryhmiin voi lisätä ominaisuuksia useampiakin. Kyllä uudetkin käskyt joukkoon sopivat, ja mikäli ne laittaa oikeisiin kohtiin niin nopeuskin säilyy.

Tämmöisiä koko asiakirjaa käsitteleviä käskyryhmiä on lukemattomia, mutta yksittäisiä lauseita käsitteleviä vielä useampia. Harvoinhan niitä tarvitaan, mutta jos niitä kasaisi kirjastoon riittävän monta niin kyllä niille käyttöä löytyy.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 22.04.23 - klo:15.45
Toisetkin ovat kertoneet että BASH on joskus omituisen nopea. Esimerkiksi jonkun virtuoosin tekemä lasku jonka tarkoitus ei oikeastaan ole laskea mielipuolista arvoa vaan kertoa nopeudesta:
Koodia: [Valitse]
echo 'n=100000;m=(n+1)/2;a=0;b=1;i=0;while(m){e[i++]=m%2;m/=2};while(i--){c=a*a;a=c+2*a*b;b=c+b*b;if(e[i]){t=a;a+=b;b=t}};if(n%2)a*a+b*b;if(!n%2)a*(a+2*b)' | bc | tr -d '\\\n'
Käskyssä bc laskee Fibonacci-sarjan 100000:nennen jäsenen jossa on yli 20.000 numeroa. Ja ne se laskee 0.1 sekunnissa huonolla läppärillä - elikä 5 mikrosekuntia per uusi jäsen - tosin uuden jäsenen laskeminen edellyttää vain jonkinverran muistiliikennettä ja yhtä usein erittäin moni-numeroista yhteelaskua. Yhteenlasku suoritetaan bc:ssä sillä bc:ssä on aina mukana rajoittamattoman tarkkuuden kirjasto. Bc kuuluu BASH:in perusasennukseen.

Gawk:iin saa myös rajoittamattoman tarkkuuden kirjaston ja sen avulla gawk laskee saman tuloksen suorittaen laskut normaaleilla kaavoilla mutta kymmenenkertaa hitaammin. Gawk on awk:in yksi versio - synapticista se löytyy ja sen voi asentaa helposti.

Paljonkohan aikaa laskuun kuluisi jos se annettaisiin samassa huonossa läppärissä suoraan C:lle?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 22.04.23 - klo:17.50
Bc kuuluu BASH:in perusasennukseen.

Bash ja GNU bc kuuluvat useimpiin GNU/Linux-jakeluihin, mutta eivät ole keskenään riippuvaisia. Lisäksi bc kuuluu POSIX-standardin vaatimiin perussovelluksiin, mutta bashin sijaan kelpaavat muutkin POSIX-yhteensopivat shellit.


Paljonkohan aikaa laskuun kuluisi jos se annettaisiin samassa huonossa läppärissä suoraan C:lle?

bc:

Koodia: [Valitse]
time echo 'n=100000;m=(n+1)/2;a=0;b=1;i=0;while(m){e[i++]=m%2;m/=2};while(i--){c=a*a;a=c+2*a*b;b=c+b*b;if(e[i]){t=a;a+=b;b=t}};if(n%2)a*a+b*b;if(!n%2)a*(a+2*b)' | bc | tr -d '\\\n' > /dev/null
real    0m0.056s
user    0m0.053s
sys     0m0.001s


C + GMP (GNU Multiple Precision Arithmetic Library):

Koodia: [Valitse]
#include <stdio.h>
#include <stdlib.h>
#include "math.h"
#include "gmp.h"

int main() {
    int i, m, n;
    int *e;
    mpz_t a, b, c, t;
    mpz_inits(a, b, c, t, (mpz_ptr) NULL);

    n = 100000;
    m = (n + 1) / 2;
    e = (int *) malloc(sizeof(int) * (1 + (int) log2((double)m)));

    mpz_set_ui(a, 0);
    mpz_set_ui(b, 1);
    i = 0;
    while (m) {
        e[i++] = m % 2;
        m /= 2;
    }
    while (i--) {
        mpz_mul(c, a, a);
        mpz_mul(a, a, b);
        mpz_mul_ui(a, a, 2);
        mpz_add(a, a, c);
        mpz_addmul(c, b, b);
        if (e[i]) {
            mpz_set(b, a);
            mpz_add(a, a, c);
        } else {
            mpz_set(b, c);
        }
    }
    if (n % 2) {
        mpz_mul(a, a, a);
        mpz_addmul(a, b, b);
    } else {
        mpz_mul_ui(b, b, 2);
        mpz_add(b, a, b);
        mpz_mul(a, a, b);
    }
    gmp_printf("%Zd", a);
  return 0;
}

Koodia: [Valitse]
gcc -Wall -std=c99 -O3 -march=native fib.c -lgmp -o fib
Koodia: [Valitse]
time ./fib > /dev/null
real    0m0.002s
user    0m0.001s
sys     0m0.000s

Myös Python 3 on tässä tehtävässä nopeampi kuin bc, joskin suurilla n:n arvoilla ero kapenee. Isommilla n:n arvoilla C-versio on 100 - 200 kertaa nopeampi kuin bc-koodi. C-versio pystyi laskemaan vielä Fib(1000000000):n eli miljardinnen Fibonaccin luvun 66 sekunnissa. bc:llä ja Pythonilla laskenta kestäisi luultavasti useita tunteja.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: AimoE - 22.04.23 - klo:18.19
Voisko joku valaista miksi ketju "Ohjeita shell-skriptaukseen (bash)" täyttyy näistä laskunopeuskilpailuista? Eikö se olisi erillisen ketjun arvoinen aihe?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 25.04.23 - klo:21.07
Kyse ei tosiaan ole nopeuskilpailusta, sillä BASH on niin hidas että lopputulos on selvä jo ennen kuin aloitetaan.

***

Tattista nm - tämä selvitti minulle paljon siitä missä mennään.

***

BASH-guideissa esitetään niin monia kummallisia väitteitä ettei väitteiden esittäjistä varmaankaan yksikään ole kokeillut useimpia väitteitään käytännössä vaan ainoastaan väittävät sitä mitä muutkin väittävät - samoilla todistuksilla - silloin ei tarvitse tehdä kokeiluja siitä pitääkö väite paikkaansa. Ja jos hölmöilee niin se on paljon helpompaa kun joukossa jossa on paljon muitakin.

Yksi väite on se ettei BASH:issa ole desimaalilukuja. Mutta nehän ovat vain tekstijonoja, kyllähän ne tunnetaan. Ja peräti niin ettei niillä ole tuota rajoitusta että ne saisivat olla korkeintaan 18 merkkisiä - BASH:in desimaaliluvuissa saavatkin olla vaikka miljoona merkkiä. Ne vaativat kyllä oman skriptinsä laskemista varten mutta sellaisiahan on jo - ja BASH-skripteiksi vieläpä super-nopeita.

Sangen omituista on sekin ettei kirjastojen käyttämistä opeteta sillä kyllä BASH:issa kirjastot toimivat ihan hyvin - vaikka kun virtuoosit kymmenkuntavuotta sitten huomasivat ettei BASH kuole yrityksistä huolimatta niin he poistivat siltä kirjasto-osoittimen jotta rappio syvenisi - tosin kirjastojen toimintaa ei voi kokonaan estää sillä kirjastot kuuluvat BASH:in perusrakenteisiin - mutta niiden käyttöä kyllä hankaloitettiin.
Kirjastot ovatkin jokaisen kielen menestyksen perusehto: jos kirjastoja ei voi käyttää niin kieli ei voi menestyä vaan on tuomittu kitumaan. Sitäpaitsi jokaisessa Ubuntussa on jo virtuoosien itsensä sinne laittama kirjasto - virtuoosit näemmä saavat tehdä kirjastoja mutta muut eivät.

Mutta opetetaan kirjastojen käyttäminen nyt: kirjasto on vain tiedosto jossa on yksi tai useampi funktio -  tiedostolla ei ole suoritusoikeutta eikä shebangia - eikä kirjastotiedostolla yleensä ole mitään muuta tehtävää. Kaikki kirjastossa olevat funktiot saa skripteissä käyttäväksi käsky-rivillä:
. kirjasto_tiedoston_nimi_polkuineen
- siis edessä on piste. Käsky on hyvä kirjoittaa ihan skriptin alkuun - niinkuin muissakin kielissä.

Skriptit täytyy ehdottomasti kirjoittaa funktiomuotoon ja funktioista täytyy tehdä kirjastoja - BASH:issa voi itsekin  tehdä kirjastoja ja sitä aikoinaan jopa suositeltiin. Eikätoistenkaan kirjastoje käyttäminen kiellettyäole. Mutta koska kirastojen tekemistä eikä edes käyttämistä nykyään  opeteta eikä oikeastaan edes sallita niin itsekunkin skriptinteko on junnaamista melkein paikallaan - koko ikäsi joudut keksimään pyörää uudestaan - joskus keksit paremman tavan mutta useammin huonomman - ja aikaa kuluu varmasti.

Aikoinaan yleisiäkin kirjastoja oli jonkunverran ja vieläkin netistä löytyy pari kummallista kirjastoa. Kunnollinen kirjasto nopeuttaisi skriptintekoa 'tuhatkertaisesti', tekisi skriptien toiminnasta yleensä paljon nopeampaa ja vakaata - ja mahdollistaisi paljon isommat skriptit. Mikä olisi virtuooseille kauhistus - eihän BASH saa toisten skripti-kieten kanssa kilpailla - ja entäpä sitten jos se alkaaa taas menestyä? 

Myös tuo väite siitä ettei BASH osaa palauttaa funktioista niiden parametreja on outo sillä arvoparametrin palauttamisessa ei ole mieltä - eikä muuten nimiparametrienkaan arvojen palauttamisessa ole mieltä vaan se on vain näennäinen tapahtuma sillä käytännösä mitään ei tapahdu - minkätakia hukata aikaa sellaiseen joka on jo tehty? Ja kyllä BASH nimiparametrienkin kanssa osaa toimia - vaikka virtuoosit muuta väittävätkin. Nimiparametrien ongelmana on saada parametrin nimestä johdettua parametrin arvo ja sen BASH osaa. Virtuoosit olivat pahassa välikädessä - he eivät uskaltaneet myöntää että eval-käskyllähän se arvo selviää nimestä ja niinpä he kiirehtivät levittämääm uskomusta että eval-käsky on BASH:issa tietoturvariski. Mutta OK - ei käytetä eval-käskyä sillä keinoja on monta muutakin, yksi niistä esitettynä esimerkin muodossa:
function koe () { let $1=$RANDOM ;}; koe apu; koe kipu; echo $apu; echo $kipu 
- nimestä saa arvon myös declare-käskyn avulla tai read-käskyn avulla tai ... 

BASH:issa oli aikoinaan ainoastaan 'matalantason käskyjä' - ne olivat erittäin nopeita ja tulkki kääntää niitä yhdessä palasessa vaikka kuinka monta - joten skripti hidastuu vain vähän kun käskyjä tulee lisää - toisaalta 'matalantason käskyt' ovat hitaita kirjoittaa ja vaikeita muistaa sillä ne ovat osittain merkki-sotkua. Joten virtuoosit kiirehtivät apuun ja tekivät uuden käskykannan jossa on helposti muistettavat ja mielekkäät nimet käskyille - käskyissa alettiin myös käyttää kytkimiä jotka ovatkin erittäin hyödyllisiä. Mutta nuo uudet käskyt ovat hitaita kuin synti - ja skripti lisäksi hidastuu nopeasti kun käskyjä tulee lisää. Mikäli skriptinsä kasaa yksinomaan 'matalantason käskyistä' saa kymmeniä kertoja nopeamman ja kyvykkäämmän skriptin - ja muuten tämä koskee kaikkia skriptejä, nuo matematiikkaskriptit vain sattuivat tulemaan ensiksi. 'Matalantason käskyt' muuten toimivat niinkuin pitää eivätkä aiheuta käyttäjän sieluun näppylöitä - uudet käskyt aiheuttavat.

BASH:ille on jo kauan sitten kehitetty menetelmä jolla voi 'esikääntää' käskyjä - siis tehdä uusista käskyistä samanlaisia kuin 'matalantason käskyt' - nopeita ja sellaisia että tulkki kääntäisi niitäkin samalla kerralla vaikka kuinkamonta - jolloin kaikista skripteistä tulisi nopeita. Se oli jo toimiva menetelmä - muutamat tekivätkin uusia käskyjä suurella vaivalla - mutta nyt se on jo melkein kuoliaaksi vaiettu ominaisuus mutta aikanaan se oli todella kovanluokan asia.

Muutenkin opetetaan tekemään tehotonta koodia. Esimerkiksi loopeilla on aivan sama tehtävä kuin muissakin kielissä - mutta BASH:issa niihin ei isoissa tehtävissä turvauduta koskaan, sillä käskyissä on usein sisäänrakennettuja nopeita C-kielisiä looppeja - eivät grep, find ja sellaisett ole ainoat vaan useimmissa käskyissä niitä on. Käytännössä kohdattavien mega-luokkaa olevien tiedostojen kanssa hommat kestäisivät itsetehdyillä loopeilla minuutti-tolkkua. Eivät itsetehdyt loopit pienissäkään hommissa autuutta tuo mutta kyllä niitä joskus käyttää kannattaaa. Ja koska looppeja ei kielletä käyttämästä isoissa tehtävissä niin taas saa BASH:in maine tahran hidastelusta. Esimerkiksi tehokkain silkkaa BASH:ia oleva etsintämenetelmä kun halutaan löytää jotain matriisista tai tekstijonosta - tai melkein mistähyvänsä: 
Koodia: [Valitse]
function etsi () { [[ "${@:2}" =~ "$1" ]] && echo löytyi || echo ei löytynyt ;}
function haemaksimi () { maksimi=$(echo $(sort -n <(echo "$@" | tr " " '\n' )) |  sed 's/.* //') ;}
function haeminimi () { minimi=$(echo $(sort -n <(echo "$@" | tr " " '\n' )) |  sed 's/ .*//') ;}

- saa esimerkiksi maksimin etsimisen paljon nopeammaksikin, lähes awk:in luokkaan - mutta jo tämmöisetkin edellyttävät ehdottomasti kirjaston käyttämistä:
function haemaksimi () { maksimi=$(echo $(sort -n <(declare -p $1 | tr ' ' '\n' | tr -dc '\n'[0-9])) |  sed 's/.* //') ;} ; apu=""; for n in {1..10000}; do apu=$apu" "$RANDOM$RANDOM; done; time haemaksimi apu; echo $maksimi

- katsohan muuten suoritusaikaa: user on aina lähes vakio kun skriptien suoritusajat muuten heittelevät kymmeninä prosentteja.Se on aika joka kuluu maksimin etsimis funktiossa - tutkittavan tekstijonon muodostaminen se tässä tuon 5 sekuntia kestää.
- nimiparametrin käyttäminen osaltaan mahdollistaa skriptin nopeuden. Muta onhan se ihan järjetöntä etsiä maksimia sorttaamalla joten eiköhän jotenkin saa vielä nopeutettua?

BASH:in tylyttäminen on kaikilla keinoillaon jatkuvaa: esimerkiksi regex:ien kätevyyttä ei juurikaan mainita - ovathan ne niin vaikeita etteivät useimmat niitä voi tehdä mutta kirjastosta niitä voisi jokainevn  käyttää  . Ja vähälle maininnalle jää sekin että BASH ymmärtää regex:iä itsekin - joten ei regex:ien käyttäminen aina edellytä niitä hyödyntävää käskyä, esimerkiksi grep-käskyä. Muuten myös ne 'matalantason käskyt' ymmärtävät regexiä.
- ei voi kuin huuli pöreänä katsoa regex:ien tuntijoiden super-nopeita regexiä.

Kun BASH:iin tuli mahdollisuus käyttää assosiatiivisia matriiseja niin kiirehdittiin vakuuttamaan että ne ovat hitaita. Mutta niinhän BASH on muutenkin hidas eikä siinä täydy assosiatiivisia matriiseja erikseen mainita. Mutta nuo vakuuttelut tekivät sen että nykyisin assosiatiivisia matriiseja ei koskaan käytetä vaikka joissain tehtävissä ne päinvastoin nopeuttavat skriptiä paljon. 

Vaikka nyt on meneillään BASH:in versio 5.2 niin silti ollaan jämähdetty BASH 3:een - tai vielä aikaisempaan. Uusia käskyjä ei opeteta - mutta opetetaanko vanhojakaan kunnolla? Esimerkkejä kehiin - Chet Ramey kehittää BASH:ia ja tekee uusia versioita joissa on paljon uutta ja ihmellistä - silti käytännössä kaikki muuttuu hyvin hitaasti sillä esimerkkejä löytyy todella vähän - en minä eivätkä useimmat muutkaan saa kuvauksista mitään irti vaan esimerkkejä siihen tarvitaan - jotain minkä voi skriptiinsö leikata-liimata - niin ne tiedot hiljokseen karttuuja oppii samanlaisia tekemään itsekin.
 
BASH:ista on tehty väärillä väitteillä epämiellyttävä käyttääkin - tässä oikeita ohjeita: ei skripteistä täydy tiedostoja tehdä - mutta jos välttämättä haluaa niin saa niitä toki tehdä. Mutta jos ei tee niin ei tule houkutustakaan myöntää skripteille suoritus-oikeutta sillä sitä ei tarvita.  Kaikki skriptit voi kirjoittaa samaan tiedostoon - tiedosto kyllä kasvaa joka päivä mutta kyllä linux siitä selviää. Pidetän vain jotenkin huoli siitä että löydtää aina sen mitä etsii - yleensä ctrl-f riittää etsimiseen mutta linuxin muutkin hakutyökalut ovat ihan poikkeuksellisen hyviä vaikka ne ovat jo vuosikymmeniä lahonneet. Tästä isosta tiedostosta vain leikataan-liimataan se skripti päätteeseen - tai vaikka leikataan-liimataan skripti verkkosivulta sinne päätteeseen - sillä päätteellä on jo suoritusoikeus eivätkä siihen liitetyt lapset sitä suoritusoikeutta enää tarvitse. Myös shebang lauseen: !#/bin/bash voi unohtaa - kaikki muut skriptikielet tarvitsevat oman shebanginsa, mutta ei BASH.

Kaikki johtuu samasta ilmiöstä: jokainen haluaa olla suuri kala pienessä lammessa eikä pieni kala isossa järvessä - joten kielen kuristuminen mittömäksi on mukavampaa kuin sen tuleminen hyvin suosituksi. Ja ne 'guidet' ovat isoja kaloja - ja lisäksi niiden kertomuksia kukaan ei uskalla kyseenalaistaa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 14.05.23 - klo:14.25

Kirjoitin tämän alkulukujen etsinnän uusiksi saatuani skriptin toiminnasta kokemusta - skripti on sama kuin ennenkin mutta teksti on täysin uutta. Toiminta perustuu merkityksellisimmiltä osiltaan iänikuisen vanhaan C-kieliseen ohjelmaan joten:

1. Tämänpäivän koneille ja ohjelmille tämä on aikalailla vaatimatonta mutta silloin kolmekymmentävuotta sitten ja varsinkin huonoissa laitteessa tämä olisi ollut täysin ihmeellistä - nimittäin tämän kaikki osat ovat noilta ajoilta ja skriptin olisi voinut kasata silloinkin.

2. mikä on BASH:in merkitys sillä se pistää tuon C-kielisen ohjelman toimimaan aikalailla toisin kuin ohjelman tekijät aikoinaan tarkoittivat?

Jättimäisten alkulukujen etsimisessä peruslaskutoimituksetkin vaativat rajoittamatonta tarkkuutta ja usein erittäin edistynyttä matematiikkaa - meidän kannaltamme silkkaa henkimaailman toimintaa ja siihen täytyy löytää valmisohjelma.

Tekijöihin jakavan ohjelman kaltaistakaan ei kukaan pysty tekemään yksin. Ja maailmassa on vain muutama ukko joka saa kasattua toisten tekosia yhteen tarpeeksi monta - siis yksi äänekäs päälläpäsmäri ja useita hiljaisia neroja.

BASH:illakin on merkityksellisiä tehtäviä: se poistaa haettavien joukosta kaikki pienillä alkuluvuilla jaolliset jolloin etsittävien joukko kutistuu kymmenenteen osaansa mikä nopeuttaa toimintaa lähes vastaavasti - nämäkin laskut edellytävät että käytettävissä on rajaton tarkkuus ja laskujen tekeminen kestää silloin kauemmin. BASH myös päättää kuinka kauan yhtä lukua selvitellään sillä tässä käytetty valmisohjelma ei itse tajua sitä tehdä.

Jättikokoisia alkulukuja etsitään tässä ohjelmassa aivan samalla tavalla kuin pieniäkin: luku jaetaan alkutekijöihinsä ja alkulukuja ovat ne joilla luku itse on luvun ainoa tekijä. Normaalisti tämmöinen menetelmä on täysin kahjo etsittäessä tekijöitä jättikokoisista luvuista sillä melkein aina yritys epäonnistuu surkeasti koska useimpien tosisuurten lukujen tekijöihin jakaminen kestää iäisyyksiä.

Mutta tuon ohjelman avulla kannattaaa tehdä tällätavoin sillä ohjelma muodostaa alkuluvun ainoan tekijän erittäin nopeasti.

Joten ei tarvita muuta kuin koettaa onnistuuko jako ja jos se ei onnistu nopeasti niin kyseessä ei ole alkuluku ja voidaan siirtyä tutkimaan seuraavaa lukua. Tosin muutamien muidenkin lukujen tekijät selviävät nopeasti joten kyllä aina joutuu laskemaan myös tekijöiden lukumäärän.

Huonossa läppärissäni tuo tekijöihin jakava factor-ohjelma osaa melkein aina muodostaa alkuluvun ainoan tekijän 0.2 sekunnissa mikäli luku on alle 64 numeroinen ja kolmessa sekunnissa mikäli se on alle 192 numeroinen - ja kunnon koneessa toiminta on kymmeniäkertoja  nopeampaa. 
 
Hakuskripti on sama riippumatta siitä  kuinka suurista luvuista etsitään, mutta muutamia muutoksia sen parametreihin kannattaa tehdä:
timeout:it ovat: .2, .7 ja 3 ja etsintäalueet 5000, 50000 tai 500000 kun etsittävä alkuluku on alle 64 numeroa, 65-128 numeroa ja 129-193 numeroa. Skripti toiminee suuremmillakin numeromäärillä mutta se on toistaiseksi kokeilematta sillä laskenta-aika kasvaa suunnattomasti - esimerkiksi 173 numeroa kestää huonolla koneella päivän. Muuten myös koneen nopeus kannattaa ottaa huomioon: koneen parantuessa timeout pienenee ja hakualue kasvaa.
 
Skripti ja sen esimerkki_kutsu:
Koodia: [Valitse]
function alkuluvun_haku () { echo "Etsin alkulukuja luvun:$1  perästä."; echo -e "\n\nlöytösekunti        alkuluku" > /tmp/delme; pienet_alkuluvut=$( seq 2 200 | factor | awk {' if ($3 =="") {print $1} '} | sed 's/://'); alkuaika=$(awk 'BEGIN {printf "%s\n", systime()}'); time for n in $( seq $1 $(bc<<<$1+5000 | tr -d '\\\n')); do for apu in ${pienet_alkuluvut[*]}; do (( $(bc<<<$n%$apu)==0 )) && n=0 && break ; done; (( $n )) && echo -ne '\rTutkittavana: '$n && timeout .2  factor $n | awk -v alkuaika=$alkuaika {' if ($3 =="") {printf "\r%s\n", systime()-asta luvuitalkuaika"                  "$1}'} | sed 's/://' >> /tmp/delme; done ; cat /tmp/delme | column -t ;}

read -e -p "editoipa lukua josta etsintä aloitetaan: " -i 1234567890123456789012345678901234567890123456789012345678901234567891234567811 luku; alkuluvun_haku $luku


Skriptin toimiessa näytölle kirjoitetaan kokoajan uusia rivejä.  Noilla annetuilla arvoilla toiminta kestää noin 3 minuuttia jonka jälkeen näytölle kirjoitetaan loppuyhteenveto:

löytösekunti  alkuluku
28            1234567890123456789012345678901234567890123456789012345678901234567891234568623
105           1234567890123456789012345678901234567890123456789012345678901234567891234570811
168           1234567890123456789012345678901234567890123456789012345678901234567891234572647


- skriptiä on helppo kokeilla: senkun leikkaat-liimaat skriptin koko koodin yhdelläkertaa päätteeseesi.
ja painat return. Mitään muuta ei saa tehdä paitsi editoida kun pyydetään.

- niitä suuria alkulukuja ei tässä näytetä sillä ne eivät mahdu mihinkään kovinkaan siististi.

- alkulukujen testaamiseksi on verkossa serveri-ohjelma nimeltään: https://www.dcode.fr/primality-test. Sillä kuluu samankokoisen alkuluvun testaamiseen sekunteja - mistäs minä tiedän vaikka se kuluttaisi tuon ajan odotus-loopissa jotta serveriä ei käytettäisi alkulukujen etsintään mutta silti se osoittaa että homma on vaikea isoillekin koneille ja siihen tarkoitetuille ohjelmille.

- ja nyt näppini syyhyävät päästä kunnon koneen näppäimille sillä nopeus vähentää suoritusaikaa tunneista minuutteihin. Saavutukset alkavat olla ikivanhalta leikkikalulta kunnioitettavia?
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: nm - 14.05.23 - klo:19.26
Huonossa läppärissäni tuo tekijöihin jakava factor-ohjelma osaa melkein aina muodostaa alkuluvun ainoan tekijän 0.2 sekunnissa mikäli luku on alle 64 numeroinen ja kolmessa sekunnissa mikäli se on alle 192 numeroinen - ja kunnon koneessa toiminta on kymmeniäkertoja  nopeampaa. 
 
Hakuskripti on sama riippumatta siitä  kuinka suurista luvuista etsitään, mutta muutamia muutoksia sen parametreihin kannattaa tehdä:
timeout:it ovat: .2, .7 ja 3 ja etsintäalueet 5000, 50000 tai 500000 kun etsittävä alkuluku on alle 64 numeroa, 65-128 numeroa ja 129-193 numeroa. Skripti toiminee suuremmillakin numeromäärillä mutta se on toistaiseksi kokeilematta sillä laskenta-aika kasvaa suunnattomasti - esimerkiksi 173 numeroa kestää huonolla koneella päivän.

Tällaiseen alkulukujen etsintään kannattaa käyttää ensin probabilistista algoritmia, kuten Miller-Rabinia (https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test), ja sitten varmentaa löydetyt alkuluvut fastECPP:llä, jonka pätevä toteutus on saatavilla täältä: https://www.multiprecision.org/cm/home.html

173 numeroa on näille menetelmille pienen pieni alkupala. Esimerkiksi GMP:n mpz_nextprime() (https://gmplib.org/manual/Number-Theoretic-Functions) löytää ehdokkaan 2 millisekunnissa:
Koodia: [Valitse]
./nextprime 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
Lainaus
12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890409

CM:n fastECPP vahvistaa alkuluvun 200 millisekunnissa:

Koodia: [Valitse]
time ecpp -n 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890409
Lainaus
real    0m0.209s
user    0m0.166s
sys     0m0.028s

nextprimen C-koodi:

Koodia: [Valitse]
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <gmp.h>

int main (int argc, char* argv[])
{
  double elapsed;
  struct timespec start, finish;
  mpz_t n;
  clock_gettime(CLOCK_MONOTONIC, &start);
  if (argc < 2) {
    printf("usage: nextprime <start_integer>\n");
    return 0;
  }
  mpz_init_set_str(n, argv[1], 10);
  mpz_nextprime(n, n);
  clock_gettime(CLOCK_MONOTONIC, &finish);
  elapsed = finish.tv_sec - start.tv_sec;
  elapsed += (double) (finish.tv_nsec - start.tv_nsec) * 0.000000001;
  gmp_printf("%f %Zd\n", elapsed, n);
  mpz_clear(n);
  return 0;
}

Kääntö:
Koodia: [Valitse]
gcc -Wall -O3 -march=native nextprime.c -lgmp -o nextprime
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 14.05.23 - klo:22.27
Epäilemättä - ja on kiva että kirjoitat mikä muiden taso on sillä se kiinostaa mutta en kerkiä tietoa etsimään.

ja pitää täysin paikkansa että koska BASH:ia ei edes päivitetä kuin kerran vuodessa niin siitä on tullut surkea rääpäle jos se on koskaan varsinaisesti ohjelmointiin sopinutkaan. Ja minä nousen barrikaadeille surkeiden rääpäleiden puolesta - varsinkin kun niitä haukutaan väärin perustein.

Useimmat esittämäni skriptit ovat pelkälle BASH:ille täysin mahdottomia - siis ilman apua bc:ltä, awk:ilta tai joltain muulta. Ja kun kyseessä on jo alkuunsa mahdottomuus niin kuka voi sanoa mihin homma päätyy? Eikö se ole jo aikahyvä barrikaadi? 

Esimerkiksi juuri nyt olen kirjoittamassa logaritmien laskentaa uusiksi. Nopeus nousi yli kymmenkertaiseksi - ja sitäpaitsi se on nimeltäänkin raflaava: logaritmit binäärijärjestelmässä. Vielä uusi kymmenkertaistuminen ja bc alkaa olla vaikeuksissa. Yksinkertaisissa laskuisa se onkin 4 kertaa hitaampi.

***

Logaritmien laskeminen binäärijärjestelmässä:
- tämä skripti ei laske bc:n avulla - siis bc:n voi vaikka pyyhkiä koodista pois skriptin antaman tuloksen muuttumatta.

Logaritmin laskeminen nopeutui 17:sta millisekuntiin. Vaikka se on hidas, on sikäli mielekäs että se näyttää desimaalilaskujen toimivan ja opettaa kuinka logaritmit lasketaan - onhan BASH opetuskieli - kyllä kaikkiin muihinkin laskuihin löytyy montakin erilaista ratkaisumenetelmää - monimutkaisen näköisiä sillä ne ovat matalan-tason koodia mutta itseasiassa ne ovat helppoja. Juuri tämä onkin syy tämän skriptin kehittämiseen sillä se tosiaan osoittaa että BASH:illa on hampaat: miksi virtuoosit uskottelevat että BASH on hampaaton?

Teen tämmöisiä kummajais-skriptejä osoittaakseeni että kyllä BASH desimaalit hallitsee ja on siinäkin suhteessa ihan kelpo kieli prototyyppien tekemistä varten - tämä skripti kertoo lisäksi kuinka logaritmit lasketaan sillä logaritmeilla on kyllä hieman käyttöä edelleenkin. Skripti on kylläkin hidas mutta ei kai kukaan 17:sta millisekunnista hermostu?

Se että logaritmit lasketaan vain luvuista lukualueella 1.xxx....99.xxx... johtuu siitä tosiasiasta että logaritmit määritellään teoriassa aina tieteellisestä esitysmuodosta. Siis luvut saatetaan tälle lukualueelle muuttamalla eksponenttia siten että eksponentti on aina parillinen. Eksponentti ei osallistuu laskuun van ainoastaan määrää logaritmin ensimmäisen numeron. Tuolla perusalueellahan exponentti on nolla joten ensmmäiseen numeroon ei lisätä mitään. Tämä oikealle alueelle saattaminen on päässälasku.

Tämä logaritmien laskemiseen soveltuva skripti ei onnistu ilman nopeaa desimaalilaskentaa.
 
- jos haluat kokeilla toimiiko skripti niin leikkaa-liimaa skriptin jokainen rivi päätteeseen yhdellä kerralla - sillä toimivat skriptit niinkin.

Koodia: [Valitse]
function 2to10 () { # desimaalinen binääriluku (esim. 1.00101) muutetaan kymmenjärjestelmään:
taulu[1]=1;apu=1;for n in {2..60}; do apu=$((2*$apu)); taulu[$n]=$apu; done
apu=${1//./}; apu2=${#apu}
pilkunpaikka=$2
luku=0; for n in $(seq 0 $apu2); do (( ${apu:$n:1} )) && luku=$(($luku+1000000000000000000/${taulu[$n+1]} )); done                                                     
echo ${luku:0:$pilkunpaikka}.${luku:$pilkunpaikka} ;}               

function getlog ()  { # logaritmi määritellään binäärijärjestelmään
kokonaisosa=${1%%.*}; (($kokonaisosa>10)) && pilkunpaikka=1 || pilkunpaikka=0; (($kokonaisosa>100)) && pilkunpaikka=2
[[ ${apu//[^.]/} ]] && vanhalogaritmoitava=$1 || vanhalogaritmoitava=$1".0"
logaritmoitavankokonaisosa=${vanhalogaritmoitava%%.*}
logaritmi=$((${#logaritmoitavankokonaisosa}-1))
until (( ${#logaritmi}>=60 )); do
  apu=${vanhalogaritmoitava//./}; apu=${apu:0:1}.${apu:1}
  luku1=${apu:0:18}
  luku2=${apu:0:18}
  desimaaliosa1=${luku1##*.}
  desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
  luku1=000000000000000000${luku1//./}
  luku2=000000000000000000${luku2//./}
  a=${luku1: -18:9}; b=${luku1: -9}
  c=${luku2: -18:9}; d=${luku2: -9}
  luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
  luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
  luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
  luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
  luku1=${luku1: -36}
  luku2=${luku2: -36}
  luku3=${luku3: -36}
  luku4=${luku4: -36}
  luku11=${luku1:0:18}
  luku12=${luku1:18}
  luku21=${luku2:0:18}
  luku22=${luku2:18}
  luku31=${luku3:0:18}
  luku32=${luku3:18}
  luku41=${luku4:0:18}
  luku42=${luku4:18}
  summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42))
  summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus>=19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
  summa1=000000000000000000$summa1; summa1=${summa1: -18}
  summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto))
  apu=$summa2$summa1; (( $desimaaleja )) &&  uusilogaritmoitava=$((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || {  uusilogaritmoitava=$(( 10#$summa2 ))$summa1 ;}
  uudenlogaritmoitavankokonaisosa=${uusilogaritmoitava%%.*}
  logaritmi=$logaritmi$((${#uudenlogaritmoitavankokonaisosa}-1))
  vanhalogaritmoitava=$uusilogaritmoitava
done # tämä päättää se ryhmän joka alkaa käskystä: until
2to10 $logaritmi $pilkunpaikka ;}

logaritmoitava=17.3517; echo -e "\n\nLasketaan logaritmi luvusta: $logaritmoitava\nseuraavalla rivillä varmasti oikea tulos bc:stä vertailua varten ja sitäseuraavalla rivillä tämän skriptin tulos: ";  bc -l<<<"scale=18; l($logaritmoitava)/l(10)";  alkuhetki=$(date +%s.%N); getlog $logaritmoitava; loppuhetki=$(date +%s.%N);echo "kulunut aika mikrosekuntia: "$((${loppuhetki//./}/1000-${alkuhetki//./}/1000))


--- skriptin tuloste on seuraavanlainen:

Lasketaan logaritmi luvusta: 17.3517
seuraavalla rivillä varmasti oikea tulos bc:stä vertailua varten ja sitäseuraavalla rivillä tämän skriptin tulos:
1.239342030392094294
1.239342030392094280
kulunut aika mikrosekuntia: 16866

- tämän skriptin toiminta-aikaa ei voi mitata time-käskyllä sillä sen ja bc:n suhde on kummallinen. Vaan suoritusaika täytyy määritellä sillä tavalla kun se on tässä tehty jotta bc:n poistaminen ei vaikuttaisi ajoitustuloksiin. Sillä tottakai on syytä varmistaa ettei bc vaikuta tämän skriptin toimintaan mitenkään poistamalla lause: bc -l<<<"scale=18; l($logaritmoitava)/l(10)".

- tosin ajoitustavan suurempi tarkkuus on mukavaa sekin - tarkemmat ajat ovat muuten täyttä asiaa ja ihan oikeat vaikka ne heiluvatkin rajusti - nimittäin heiluminen johtuu siitä että Linux suo BASH:lle toiminta-aikaa sillointällöin, vain satunnaisesti ja silloinkin  pienissä palasissa kuin muilta töiltään kerkiää ja hommat tosiaan kestävät mitä milloinkin - tämän heilumisen estämiseksi reaaliaika-käyttöjärjestelmät kehitettiinkin sillä ongelma koskee useimpia ohjelmointi ympäristöjä.

***

Seuraava on liian omituinen käytettäväksi mutta on hyvä tietää että tällainenkin toimintatapa on mahdollinen:

Funktion nimeksi voidaan antaa melkein mitähyvänsä, vaikka merkki: +
- tosin ä:tä ja ö:tä on syytä välttää. Siten voidaan määrätä:

function + () { echo $(($1+$2)) ;}
- matemaattinen merkki + ei ole funktio joten kyseesä ei ole rekursio (eli funktio ei kutsu itseään)

kun tämänjälkeen annetaan käsky: + 1 2
niin se tulostaa 3


mutta vaikka annettaisiin käsky:
funktion + () { echo painu suolle, tänään ei lasketa mitään. ;}
niin echo $((1+2)) tulostaa 3 ihan niinkuin ennenkin ja vain tuo: + 1 2 tulostaisi:  painu suolle, tänään ei lasketa mitään.

---

Samoin voidaan määritellä: function !  () { echo $(($(seq -s* $1))) ;};
mutta käsky : ! 6 ei semmoisenaan viittaa tuohon kertoman määrittelyyn vaan event-määrittelyyn ja siten: ! 20 ei toimi vaan täytyy käskeä: \! 20
- \! 21 menee jo yli lukualueesta.

- kun lopettaa sen pääte-istunon jossa funktio on määritelty unohdetaan sen määrittely. Avattaessa uusi pääte se vanha määrittely palaa voimaan.
 


Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 16.05.23 - klo:20.42
***

Nämä skriptini ovat yritystä löytää BASHin perusta - skriptit ovat mielettömiä mutta kukaan ei voi tietää millaisiin tuloksiin ne johtavat. Ja muuten: kaikki nopeat käskyt briljanteraa tekstinkäsittelyssä vaikka on niistä apua laskennassakin.

BASH:issa on kammottava määrä käskyjä joista ei koskaan puhuta. Ne ovat nopeita ja tekevät pieniä ihmeitä. Esimerkiksi käskyt seuraavan skriptin tulostukseen: poistetaaan etunollat kokonaisosasta, takanollat desimaaliosasta ja desmaalipiste silloin kun se jää viimeiseksi:
Koodia: [Valitse]
apu2=${apu2##+(0)}; [[ ${apu2//[^.]/} ]] && apu2=${apu2%%+(0)}; echo ${apu2%.}
Toiminto on salaman-nopea ja se on ollut olemassa aina mutta jokainen joka noita toimintoja nykyisin tarvitsee tekee varmasti toimintoa varten pitkän ja hitaan skriptin - ja vain koska 'advance guidet' pitävät huolta siitä että noiden kaltaiset käskyt pysyvät unholassa. Ei niistä tosin paljoa BASH-raamatuissakaan puhuta. Mutta netti ei koskaan unohda ja sinne kerkisi kauan sitten tulla monia kirjoituksia niistä - tosin noiden käskyjen nopeudesta ei ole hyötyä jos niiden ohella käyttää uusi käskyjä. Eikä missään ei ole kattavaa yhteenvetoa.

Tehdessäni skiptiajuria kohtasin toisenlaisia ihmeellisiä käskyjä sllä se on pääasiassa tekstikäsittelyä - taas aivan toisenlainen maailma. Montakohan maailmaa BASHissa on?

***

Koska nyt  haukun virtuooseja pystyyn niin halusin varmistaa että haukun toimivien asioiden perusteella - tälläkinkertaa on kyse desimaalimatematiikasta - mutta tätä ennenhän on ollut kaikenlaista muuta - paljon merkityksellisempääkin. Jokatapauksessa tein taas yhden mielettömän skriptin jossa on runsaasti moninumeroista laskentaa - mutta toisenlaista laskentaa kuin aikaisemmissa skripteissä joten uudetkin laskut tulee testattua. BASH itse laskee muuten montakertaa nopeammin kuin matematiikka-ohjelmat joten niitä ei laskennassa käytetä - varsinaisessa matematiikassa BASH jää kyllä paljon huonommaksi ja sitähän tässä koetetaan nopeuttaa - toistaiseksi huonosti menestyen. Mutta kyse ei tälläkertaa ole varsinaisesti matematiikasta vaan siitä että soiminto ylipäätään onnistuu jollaintavalla - se että se toimii näinkin hyvin on kylläkin mukavaa.

Skripti muuntaa digitaaliluvun desimaaliosan kymmenjärjestelmään. Esimerkiksi luvun .11100000001000000010000000010000000010000000100000001001 muuntaminen kymmenjärjestelmään:
skripti:                          0.875490192331227615183197488403320317865025843484497
netin muunnosmoottori: 0.87549019233122761519

- onko kyseessä pyöristysvirhe vai laskeeko muunnosmoottori liian vähillä numeroilla? Sillä 'tarkkuuskato' näissä laskuissa on niin suuri että saa pelätä ettei tuo skriptinkään numeromäärä vielä riitä vaan kaikki nuo ylimääräiset numerot ovat jo potaskaa - ne ovat tuossa vain siksi että näkisitte paljonko niitä on.
Koodia: [Valitse]

function summaa72 () { # viimeisin versio
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia ja haetaanko vertailutulos bc:stä. Vaihtoehdot:tulosta=echo ja tulosta=: . Vaatii siis yksinkertaisen koodimuutoksen.
$tulosta
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
desi1=${luku1##*.}
desi2=${luku2##*.}
koko1=${luku1%%.*}
koko2=${luku2%%.*}

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

summattava1='0000000000000000000000000000000000000000000000000000000000000000000000000000'$koko1$desi1; summattava1=${summattava1: -72}; $tulosta $summattava1
summattava2='0000000000000000000000000000000000000000000000000000000000000000000000000000'$koko2$desi2; summattava2=${summattava2: -72}; $tulosta $summattava2 ; $tulosta

luku11=${summattava1:0:18}; luku12=${summattava1:18:18}; luku13=${summattava1:36:18}; luku14=${summattava1:54:18}; $tulosta $luku1{1..4}"  "
luku21=${summattava2:0:18}; luku22=${summattava2:18:18}; luku23=${summattava2:36:18}; luku24=${summattava2:54:18}; $tulosta $luku2{1..4}"  "; $tulosta

apu14=$((10#$luku14+10#$luku24)) ;          [[ ${#apu14} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto1; apu14=${apu14:1:18} ;} || ylivuoto=0
apu13=$((10#$luku13+10#$luku23+$ylivuoto)); [[ ${#apu13} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto2; apu13=${apu13:1:18} ;} || ylivuoto=0
apu12=$((10#$luku12+10#$luku22+$ylivuoto)); [[ ${#apu12} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto3; apu12=${apu12:1:18} ;} || ylivuoto=0
apu11=$((10#$luku11+10#$luku21+ylivuoto))
apu=$apu11$apu12$apu13$apu14

$tulosta "ylemmällä rivillä varmasti oikea tulos bc:stä ja toisella rivillä mitä tämä skripti antaa:"
$tulosta $(bc<<<"$1+$2")
# poistetaaan etunollat kokonaisosasta, takanollat desimaaliosasta ja desmaalipiste jos se jää viimeiseksi
apu2=${apu:0: -$desipituus}.${apu: -$desipituus}; apu2=${apu2##+(0)}; [[ ${apu2//[^.]/} ]] && apu2=${apu2%%+(0)}; echo ${apu2%.} ;}

function jaa () { (( ! $# )) && echo funktion ajokäsky on muotoa: jaa 1 2 . Siitä pitää tulla: .5000000000000000000000000000 && sleep 2 && return
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##*.}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

#desimaaliosat yhtäpitkiksi:
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
$tulosta $kokonaisosa1$desimaaliosa1
$tulosta $kokonaisosa2$desimaaliosa2

#kokonaisosat yhtäpitkiksi:
(( ${#kokonaisosa2} >= ${#kokonaisosa1} )) &&
{ apu="00000000000000000000"$kokonaisosa1; kokonaisosa1=${apu: -${#kokonaisosa2}} ;} || { apu="00000000000000000000"$kokonaisosa2; kokonaisosa2=${apu: -${#kokonaisosa1}} ;}
luku1=$kokonaisosa1$desimaaliosa1;luku1=$((10#$luku1))
luku2=$kokonaisosa2$desimaaliosa2;luku2=$((10#$luku2))
$tulosta jaettava:$luku1"   "jakaja:$luku2

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

# tähänasti on selvitetty desimaalipisteen paikkaa. Nyt aletaan laskea numeroita.
unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
luku1=${luku1##+(0)}; [[ ${luku1//[^.]/} ]] && luku1=${luku1%%+(0)} # poistetaan etu-ja takanollat sillä tästälähtien ne vain haittaavat.!!!
luku2=${luku2##+(0)}; [[ ${luku2//[^.]/} ]] && luku2=${luku2%%+(0)} 
q=$(($luku1%$luku2));((q>1)) && q=$((10**$((${#q}-2)) )) || q=1 # siis q on jakojäännös esitettynä siten että ensimmäinen numero on 1 ja lopuista numeroista tulee nollia - viimeinen nolla poistettuna
luku1=$luku1'0000000000000000000'; luku1=${luku1:0:18} ;$tulosta " "$luku1 #"  "${#apu2}"  "$apu

for n in {1..6}; do # muodostetaan tulos-palasia 
apu=$(($luku1/$luku2)); tulos[$n]=${apu}; (( $(($luku1%$luku2)) > $((10*$q )) )) && apu=$apu'0' # on joskus tarpeen ja joskus liikaa
apu2=$(($luku1%$luku2)); luku1=$apu2'0000000000000000000'; luku1=${luku1:0:18} ; $tulosta " "$luku1"  "${#apu2}"  p"$apu
done

vanhatmerkit=1
for n in {1..6}; do # kootaan tulosta matriisin palasista
uudetmerkit=${#tulos[$n]}; [[ $uudetmerkit -lt $vanhatmerkit ]] && tulos[$n]=$nolliatulosteessa${tulos[$n]} ; vanhatmerkit=$uudetmerkit
tulos=$tulos${tulos[$n]}
done
 
#[[ ${tulos%%+(0)} -eq 1 ]] && nolliatulosteessa=${nolliatulosteessa:1} # purkkaviritys?
[[ $nolliatulosteessa ]] && echo .$nolliatulosteessa${tulos:0} || echo ${tulos:0:$kokonaisiatulosteessa}.${tulos:$kokonaisiatulosteessa} ;}

function muunna () { local apu7; apu7=$1;luku=0; for n in $(seq $((${#apu7}+1))); do  (( ${apu7:$n-1:1} )) && luku=$(summaa72 $luku ${taulu[$n+1]});done ; echo $luku ;} 

unset taulu;taulu[1]=1;apu=1;for n in {2..57}; do apu=$((2*$apu)); apu8=$(jaa 1 $apu); taulu[$n]=${apu8%%+(0)};  echo $n"  "${taulu[$n]}; done;   # taulukon muodostaminen

muunna 111 # esimerkkikutsu
 

***

Tässä ei varsinaisesti ole kyse desimaalimatematiikasta vaan kaikenmaailman guidejen saattamisesta naurunalaiseksi - ne apinoivat kaikki samoja rajoittuneita alkeita.. Kyllä ne silti on syytä oppia mutta niissä ei tosiaankaan ole mitään edistynyttä - ja valitettavasti tilanne on tällähetkellä se ettei mitään parempaakan ole. BASH raamatutkin ovat kovin yksipuolisia ja niistä on vaikea oppia mitään. BASH-maailma on ainakin kymmeniä kertoja suurempi kuin mitä ne antavat uskoa.

Ja desimaalimatematiikka ei ainoastaan toimi vaan on lisäksi salaman-nopeaa - alle 1ms.

Voitte olla varmoja että heitä kirpaisee jos tämä joskus heidä kurkustaan alas tungetaan - sillä homma on karmea: tappoivat oman lapsensa. Sillä tuo nopeutus ei koske yksinomaan  matematiikkaa vaan myös tekstinkäsittelystä tulee samoilla käskyillä erittäin nopeaa.

Ja vaikka ilmanmuuta koettavat vaieta desimaalilaskennan kuoliaaksi niin minkäs sille voi että tämä toimii. Syy skriptin esittämiseen onkin se ettei tällaisia voi puhua esittämättä kuinka homma hoidetaan.

Aikoinaan  BASH oli parhaimmillaan koeteltaessa toimiiko joku teoria käytännössä - syy BASH-skriptin tekoon oli selvittää kannattaako tehdä kunnollinen ohjelma jollain kunnon kielellä - nimittäin BASH:illa saa nopeasti tehtyä välttävästi toimivan skriptin. Mutta siinä hommassa säällisen nopeat peruslaskutoimitukset desimaali-luvuilekin ovat melkein välttämättömiä. Desimaalilaskuihin käytettiinkin matematiikkaohjelmaa, esimerkiksi bc:tä sillä luultiin ettei BASH desimaalilaskuihin kykene. Mutta kyllä kykenee ja pienehköissä peruslaskutoimituksissa BASH on jopa paljon nopeampi.

Desimaalilaskut suoritetiin silloin aikoinaan kokonaisluvuilla isoissakin koneissa - joten kyllä senaikaiset virtuoosit tiesivät että myös BASH kykenee niihin. Mutta he välttelivät puuttumasta koko asiaan sillä se on sellainen salahautojen viidakko että paraskin virtuoosi haksahtaisi joskus ja sitä he eivät kestä - joutua naurettavuuden kaakinpuuhun. Niinpä he eivät puhuneet koko asiasta - mikä antaa sellaisen mielikuvan etteivät desimaalilaskut BASH:issa edes onnistu. Ja oli heillä laho selityskin: BASH:in matematiikkamoottori ei osaa desimaalilaskuja eikä BASH:in muuttujien tyyppimäärityksissäkään desimaaliluvuista mitään puhuta. Mutta tuo tyyppimääritys on tahallinen väärinymmärrys sillä tekstijonojahan ne desimaaliluvut vain ovat.
 
Mutta siis niitä salahautoja on: jakolaskua vaivaa esimerkiksi 'katoavien nollien syndrooma'. Ja kun nolla katoaa on senjälkeinen tulos pelkkää potaskaa. Tämän korjaaminen on henkimaailmasta: jakolasku ei esitä etunollia -> jos jakolaskun tulos on 010 (esimerkiksi 100/10) niin näytetään vain 10. Mutta vaikka tähän löytyikin korjaus niin jakolaskua täytyy vielä kauan testailla ja kenties krjailla.

Mutta kyse ei ole mitättömän piennistä jakolaskuista vaan esimerkiksi: jaa .123456 1233457890123.23  josta tulos on: .00000000000010008935123651930204696 - joka on kyllä ihan oikein - ei niitä kadonneita nollia silloin löydä kun etsii niitä.

- itse skripti vie aikaa alle millisekunnin. bc tässä sitä aikaa vie laskiessaaan varmasti oikeaa arvoa johon verrata tämän skriptin tulosta. Kommentoi bc pois niin näet.

Koodia: [Valitse]
function jaa () { (( ! $# )) && echo funktion ajokäsky on muotoa: jaa 1 2 . Siitä pitää tulla: .5000000000000000000000000000 && sleep 2 && return
 
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."

desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

#desimaaliosat yhtäpitkiksi:
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo $kokonaisosa1$desimaaliosa1
#echo $kokonaisosa2$desimaaliosa2

#kokonaisosat yhtäpitkiksi:
(( ${#kokonaisosa2} >= ${#kokonaisosa1} )) &&
{ apu="00000000000000000000"$kokonaisosa1; kokonaisosa1=${apu: -${#kokonaisosa2}} ;} || { apu="00000000000000000000"$kokonaisosa2; kokonaisosa2=${apu: -${#kokonaisosa1}} ;}
luku1=$kokonaisosa1$desimaaliosa1;luku1=$((10#$luku1))
luku2=$kokonaisosa2$desimaaliosa2;luku2=$((10#$luku2))
echo jaettava:$luku1"   "jakaja:$luku2

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

echo nolliatulosteessa:$nolliatulosteessa"   "kokonaisiatulosteessa:$kokonaisiatulosteessa   

# tähänasti on selvitetty desimaalipisteen paikkaa. Nyt aletaan laskea numeroita.
unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
luku1=${luku1##+(0)}; luku1=${luku1%%+(0)} # poistetaan etu-ja takanollat sillä tästälähtien ne vain haittaavat.
luku2=${luku2##+(0)}; luku2=${luku2%%+(0)} 
# seraava oli: q=$(($luku1%$luku2));q=$((10**$((${#q}-1)))) # siis q on jakojäännös esitettynä siten että ensimmäinen numero on 1 ja lopuista numeroista tulee nollia - viimeinen nolla poistettuna
q=$(($luku1%$luku2));((q>1)) && q=$((10**$((${#q}-2)) )) || q=1 # siis q on jakojäännös esitettynä siten että ensimmäinen numero on 1 ja lopuista numeroista tulee nollia - viimeinen nolla poistettuna
luku1=$luku1'0000000000000000000'; luku1=${luku1:0:18} ;echo " "$luku1 #"  "${#apu2}"  "$apu
for n in {1..6}; do # muodostetaan tulos-palasia 9 merkkiä kerrallaan
apu=$(($luku1/$luku2)); (( $(($luku1%$luku2)) < $q )) && apu=$apu'0'; tulos[$n]=${apu}  # yksi keino palauttaa niitä kadonneita nollia;  (( $(($luku1%$luku2)) > $((10*$q )) )) && apu=$apu'0' on joskus tarpeen ja joskus liikaa
apu2=$(($luku1%$luku2)); luku1=$apu2'0000000000000000000'; luku1=${luku1:0:18} ;echo " "$luku1"  "${#apu2}"  "$apu
done

vanhatmerkit=1
for n in {1..6}; do # kootaan tulosta matriisin palasista
uudetmerkit=${#tulos[$n]}; [[ $uudetmerkit -lt $vanhatmerkit ]] && tulos[$n]='0'${tulos[$n]} ; vanhatmerkit=$uudetmerkit
tulos=$tulos${tulos[$n]}
done
 
[[ ${tulos%%+(0)} -eq 1 ]] && nolliatulosteessa=${nolliatulosteessa:1} # purkkaviritys?
echo "oikea tulos 54 desimaalilla esitetynä on päällä ja alla tulos tästä skriptistä"
bc<<<"scale=${#tulos}; $1/$2" | tr -d '\\\n'; echo ' tämä rivi on bc:stä'
[[ $nolliatulosteessa ]] && echo .$nolliatulosteessa${tulos:0} || echo ${tulos:0:$kokonaisiatulosteessa}.${tulos:$kokonaisiatulosteessa} ;}

# Seuraavia on kokeiltu:
         
time jaa 1233457890123.23 .123456
time jaa .123456 1233457890123.23
time jaa 1 2
time jaa 2 1
time jaa 1 3
time jaa 1 1000
time jaa 1 1001
***
 Nämä skriptini ovat satoja kertoja nopeampia kuin skriptit samoissa laskuisssa olisivat uusilla käskyillä olleet jos olisi tajuttu kuinka ne tehdään - mutta silti skriptieni nopeudessa ei vielä ole kehumista. Mutta kukaan ei voi sanoa mihin nämä päätyvät - jos tämä tahti jatkuu niin kaikki toiset skriptikielet saavat turpiinsa jo vuoden kuluttua. Eivätkä skriptini matematiikkaa tee vaan pääasiassa tekstinkäsittelyä.

Koska BASH:illa ei ole kirjastoja niin näitä nopeita skriptejä valmistuu hyvin hitaasti. Sillä kirjastot ovat täysin välttämättömät kaikille kielille sillä niissä on ylensä paljon toimintoja joista jotkut ovat todella pitkiä ja monimutkaisia - ja kirjastojen oikeellinen toiminta on jo testattu joten ei sitä skriptaajan täydy enää paljoakaan testata. Ja kun skriptin teko kestää hetken niin sen testaaminen kestää silti iäisyyksiä.

Tässä on kyseessä liukuvan pilkun desimaalilukujen kertolasku jonka tuloksessa saaa olla korkeintaan 18 numeroa - ja skriptin toteutus on aikatavalla toisenlainen kuin aikaisemmissa skripteissä - ja jonka pahin puute on sen vähäinen numeromäärä - eikä se edes ole juurikaan nopeampi kuin matematiikka-ohjelmat. Mutta siihen saisi paljon lisää nopeutta esikääntämisellä jota aikoinaan harrastettiinkin - matematiikka-ohjelmiin ei saa lisänopeutta.

Jokatapauksessa skriptistä saa käsitystä siitä kuinka sriptit pitäisi koota sillä virtuoosit eivät sitä kerro - ja eivät halua toistenkaan kertovan? - mikä on todella omituista sillä aikoinaan BASH:ia käytettiin suunnattoman paljon ja sillä oli monia todella kuuluisia ja päteviä virtuooseja. Täytynee olla niin että sana: 'kokonaislukulaskenta' saa kaikki lopettamaan ajattettelemisen.

Tottakai skripteissäni on reikiä ja ne tarvitsevat korjauksia - mutta sen käsityksen ne varmistavat että virtuoosit jostain syystä estävät BASH:ia kehittymästä.
Koodia: [Valitse]
function kerro9 () { # syötettävissä saa olla korkeintaan 9 merkitsevää desimaalia jolloin tuloksessa voi olla 18 merkitsevää desimaalia.
luku1=${1//./,}
luku2=${2//./,}
luku1=$(printf "%.19e" $luku1)
luku2=$(printf "%.19e" $luku2)
exp1=${luku1##*e}; exp1=${exp1//+0/+}; exp1=${exp1//-0/-}
exp2=${luku2##*e}; exp2=${exp2//+0/+}; exp2=${exp2//-0/-}
exp3=$(($exp1+$exp2+1))

luku1=${luku1%%e*}; luku1=${luku1%%+(0)}
luku2=${luku2%%e*}; luku2=${luku2%%+(0)}

[[ ${luku1//[^-]/} ]] && { merkki1=-1; luku1=${luku1:1} ;} || merkki1=1
[[ ${luku2//[^-]/} ]] && { merkki2=-1; luku2=${luku2:1} ;} || merkki2=1 

kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}
apu=$(($kokonaisosa1*$kokonaisosa2))
kokonaisia=${#apu}

tulos=$((${luku1//,/}*${luku2//,/})) # siis lasketaan mitkä tuloksen merkittävät numerot osvat ja esitetään ne sitten erikseen.
bc<<<"scale=60; $1*$2" # bc tarjoaa vain varmasti oikean tuloksen johon tämän skriptin tulosta voi verrata - laskuihin bc ei osallistu. Voit vaikka pyyhkiä tämän rivin pois.
[[ $exp3 -gt -1 ]] && { [[ $(($merkki1*$merkki2)) -eq -1 ]] && echo -n '-'; echo ${tulos:0:$exp3}.${tulos:$exp3} ;} ||  { [[ $(($merkki1*$merkki2)) -eq -1 ]] && echo -n '-'; echo .$(printf '0%.s' $(seq ${exp3:1}))${tulos%%+(0)} ;} ;}


# esimerkkikutsuja
kerro9 1234567891 1234567892
kerro9 123456789012345678 2.2
kerro9 123.4567891 1234567.89
kerro9 1234567891 1234567892E-18                                # kerrottavien esitysmuoto saa olla tieteellinenkin (bc ei selviä tästä)
kerro9 .000000000000123456789 -.00000000123456789 # desimaaliosissa saa olla etunollia kuinkamonta vaan sillä ne eivät ole merkitseviä numeroita.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: Jere Sumell - 18.06.23 - klo:22.18
Tässä tänä iltana parisen tuntia lueskelin Bash-scriptaukseen liittyen tutoriaalia Ryan's Tutorial -lähteestä, ja sitten mitä katsoin tuon read -komennon manuaali-ohjeistusta, niin heräsi kysymys, mikä tuo read -komennon palautusarvo on, jos nyt esimerkiksi jotain tekstitiedostosta luetaan sitä dataa, jos se nyt on esimerkiksi ihan standardi 128 -merkin ASCII-merkistö.

Mitä tuo palautusarvo on positiivinen, mutta sitten mitä useimmissa ohjelmointikielissä, yleensä sitten -1, tai negatiivinen palautusarvo on siinä kohtaa, kun tiedoston loppu, tai syötteen loppu saavutetaan, EOF. read -komennossahan -1 palautuksena heitetään virhetilanteessa.

Mitä jonkun verran katsoin tuosta syventäen sitä, mitä alkoi kiinnostamaan, niin ASCII-tiedostossa ei ole sitä EOF-riviä sitten lopussa, niin onko read -komennolla mitään arvoa, mitä se palauttaa tiedoston lopun saavuttamisen jälkeen?

Vai onko se käytännössä kertokaa te, jotka olette ehkä jo ikänne ohjelmoineet ihan käytännön työkokemusta ehkä on, tai muutkin niin jos syötevirroilla ohjaa tekstitiedoston bash-scriptille, missä sitten read komennolla luetaan muuttuja, joka käydään do-while -toistorakenteessa läpi se läpikäytävä tiedosto?

Onhan se sama asia, että tiedosto tulee läpi käytyä rivi riviltä, tai merkki merkiltä, siinä on ohjelmoimista muutamia merkkijonoa välilyönnillä erotettuna ehkä jopa enemmän, mitä sitten jos tuolla read-komennolla olisi jokin EOF-palautusarvo.

Itsekin melkein päädyin, että ei siellä melkein ole mitään, mitä olisi tullut jo tossa vastaan, mitä katselin oikeilla hakusanoilla. Kai siihenkin sitten pätevät syyt on. Se voi se tausta olla ja siinä, mitä BASH on kehitettynä 1989, niin siihen maailman aikaan varmaan valtaosin tekstitiedostot oli 128 merkin ASCII -merkistöä ja se nyt selvisi, että siinähän ei tosiaan ole sitä. Tämä nyt oma arvioni, jos joku tuntee tätä skriptauskielen historiaa ja taustoja enemmän, niin voi valaista minua.

Pistän tähän nyt, jos joku etsii hyviä tutoriaaleja, mistä itselleni oli hyötyä, vaikka nyt en nollasta ihan tätä lähtenyt liikenteeseen, mutta en ole bash-scriptejä enimmäkseen linux-koneellani ohjelmoinut.

https://ryanstutorials.net/bash-scripting-tutorial/ (https://ryanstutorials.net/bash-scripting-tutorial/)

Tuo nyt oli selkeä ja hyvin kirjoitettu paketti. Lisäksi alkuperäinen lähde, jossa kuulin ensimmäistä kertaa tuon viittauksen joskus 2000-luvun vaihteessa Musiikmitelevisiossa esitetyssä "Pimp My Ride" -formaatissa "Pimp My Bash Script" ja viittaus tuohon auto-ohjelman juontajaan siinä, että hän ei ole sanonut niin, mutta tutoriaalin laatijan mukaan olisi voinut hyvinkin olla quoten takana.

Mitä lopulta sitten tuli 4 hakutulosta tuosta, katsoin mikä tuon alkuperä on, niin tuo on ainoa lähde, tai alkuperäinen missä tuo noin on esitetty.

Kiinnostus sitten heräsi muutoin miehen tarjontaan, ihan positiivisella tavalla kaikin puolin mielekkään oloinen tutoriaaleihin keskittyvä selkeä sivusto. Pidin kovastikin.

Sitten hyvä lunttilappu (cheatsheet) seinään teipattavaksi
https://devhints.io/bash (https://devhints.io/bash)

Päällisin puolin nuo lunttilappu-tiivistelmät aiheesta kuin aiheesta on siinä määrin käteviä myös ohjelmointikielien osalta, että siinä kohtaa kun ei ole sillä tavalla selkärangasta tule aivan kaikki, niin nopeita ja käteviä tarkastaa nopeasti asia, mikä on hukassa.
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: petteriIII - 03.07.23 - klo:10.41
BASH:in palauttaminen järkevään kuntoon täytynee alkaa inhottavimmasta päästä: BASH:in matematiikasta. Tai eihän BASH:illa matematiikkaa oikeastaan ole vaan ainoastaan kyky kokonaisluku laskentaan - kyky jota ei koskaan käytetä sillä kyky kyetä käsittelemään myös desimaalilukuja on lähes ehdoton. Desimaalilukujen käsittelemiseksi täytyy siis aluksi tehdä funktiot. Selvitetään ensin laskennan merkintätapaa:

Kaikissa kielissä matemaattiset toiminnot suoritetaan samallatavoin, mutta laskujen suorittamisen merkintätavat vaihtelevat paljon - ja kunkin merkintätavan hyvät ja huonot puolet paljastuvat vasta kun niitä käyttää paljon - mutta useimmat eivät koskaan edes kokeile muita kuin normaalia merkintätapaa.  Mutta muutama laskennan merkintätapa: 

                       Normaali merkintätapa: a=1; b=2; c=a+b
Käänteinen puolalainen merkintätapa: a=1; b=2; c=ab+ 
                        BASH:in merkintätapa: a=1; b=2; c=$(($a+$b)) . Tietokoneohjelmassa merkintätavan monimutkaisuus menettää merkityksensä - vaikka eihän se sentään merkityksetöntä ole.
               
- merkki + kutsuu niissä kaikissa funktiota joka selvittää kuinka homma hoidetaan.
- muissa kielissä laskun jäsenet voivat olla desimaalilukujakin - mutta ei BASH:issa. Joten kun muissa kielissä on desimaalien käsittelyyn jo tehtynä funktioita - käytät niitä vaikka et edes tietäisi että sellaisia tarvitaan - funktiot ovat nykyään usein matematiikkaprosessorien langoituksessa - mutta BASH:iin nuo funktiot täytyy tehdä itse. Ja käytettässsä funktiota laskuihin muuttuu toiminnon merkintätapakin: a=1.1; b=2.2; c=$( ynnää $a $b ) .
- mutta ei sen funktion nimeltä: + hommaa pysty kukaan toinen tekemään joten funktiota: + täytyy kutsua funktiossa: ynnää.

***             
             
Desimaali-laskun suorittamiseksi täytyy ensin laskea missä desimaalipiste tulee tuloksessa olemaan. Tämänjälkeen poistetaan luvuista desimaalipisteet ja suoritetaan laskutoimitus saaduilla kokonaisluvuilla. Sitten vain kirjoitetaan tulos sijoittaen desimaalipiste lasketulle paikalle. Tietokoneiden alkuaikoina oli vain  kokonaislukulaskimia ja haluttessa saada desimaalinen tulos toimittiin kuten edellä kuvattiin.

Kyllä virtuoosit siis ilmanmuuta tämän tiesivät 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 desimaalilaskujakin oli estettävä kehittymästä. Helppo nakki - tehtiin uusi käskykanta joka oli muissa tehtävissä parempi kuin entinen mutta desimaalilaskuissa se oli kammottavan hidas. Vanhoilla käskyillä desimaalilaskenta onkin tuhansia? kertoja nopeampaa 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 nopeuseroa käskyillä on käsittämättömän paljon sillä 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 lopu sillä kirjastoahan ei ole. Eikä yksi tyyppi niitä voi tehdä - se olisi ihan liian hidasta ja yksipuolista. Mutta kannattanee yrittää.

***

Akutilanne: haluat laskea: yhteen kaksi korkeintaan 72 numeroista positiivista lukua(merkin huomiooon ottaminen monimutkaistaisi esimerkin vaikeaksi käsittää), desimaalipiste saa kummassakin sijaita missävaan. Toiminta on silloin:
     
       Otetaan esimerkki joka kirjoittaan ( siis desimaalipisteet samalle kohtaa alekkain ja desimaaliosissa pienemmän numerot täytetään nollilla:
       12345678901234567890.2000000000000000000000000000
                        +73.4567890123456789012345678901
     
Sitten vaan laskemaan - desimaalipisteen paikka pistetään muistiin esimerkiksi sen perusteella kummassa on kokonaisoasassa enemmän numeroita - desimaalipiste poistetaan ja jaetaan loppu18 merkin palasiin lopusta alkaen ja lasketaan palaset yhteen alkaen vähiten merkityksellisestä päästä ja mikäli jonkun palasen summaan tuleekin 19 numeroa niin se viimeinen numero lisätään seuraavaan palaseen poistaen se alkuperäisestä palasesta. Sitten vain laitetaan palaset peräkkäin ja kirjoittetaan oikealla hetkellä desimaalipistekin.
Koodia: [Valitse]

function summaa72 () {
tulosta=: # yhdessä paikassa päätetään tulostetaanko välituloksia ja vertailutulos bc:stä. Vaihtoehdot:tulosta=echo ja tulosta=:
$tulosta

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

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

summattava1='0000000000000000000000000000000000000000000000000000000000000000000000000000'$koko1$desi1; summattava1=${summattava1: -72}; $tulosta $summattava1
summattava2='0000000000000000000000000000000000000000000000000000000000000000000000000000'$koko2$desi2; summattava2=${summattava2: -72}; $tulosta $summattava2 ; $tulosta

luku11=${summattava1:0:18}; luku12=${summattava1:18:18}; luku13=${summattava1:36:18}; luku14=${summattava1:54:18}; $tulosta $luku1{1..4}"  "
luku21=${summattava2:0:18}; luku22=${summattava2:18:18}; luku23=${summattava2:36:18}; luku24=${summattava2:54:18}; $tulosta $luku2{1..4}"  "; $tulosta

apu14=$((10#$luku14+10#$luku24)) ;          [[ ${#apu14} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto1; apu14=${apu14:1:18} ;} || ylivuoto=0
apu13=$((10#$luku13+10#$luku23+$ylivuoto)); [[ ${#apu13} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto2; apu13=${apu13:1:18} ;} || ylivuoto=0
apu12=$((10#$luku12+10#$luku22+$ylivuoto)); [[ ${#apu12} -gt 18 ]] && { ylivuoto=1; $tulosta ylivuoto3; apu12=${apu12:1:18} ;} || ylivuoto=0
apu11=$((10#$luku11+10#$luku21+ylivuoto))
apu=$apu11$apu12$apu13$apu14

$tulosta "ylemmällä rivillä varmasti oikea tulos bc:stä ja toisella rivillä mitä tämä skripti antaa:"
bc<<<"$1+$2" | tr -d ' \\\n'; echo
# poistetaaan etunollat kokonaisosasta, takanollat desimaaliosasta ja desmaalipistekin jos se jää viimeiseksi
apu2=${apu:0: -$desipituus}.${apu: -$desipituus}; apu2=${apu2##+(0)}; [[ ${apu2//[^.]/} ]] && apu2=${apu2%%+(0)}; echo ${apu2%.} ;}
   
time summaa72 55555555555555555555555555555.55555555555555555555555555555555555555555 6666666666666666666666666666666.6666666666666666666666666666666666666666

- ei tämä kyllä vielä ihan moitteeton ole mutta kelpaaa jo käyttöönkin.
- funktion nopeus on 1ms jos poistaa rivin: $tulosta $(bc<<<"$1+$2") - sillä tuota riviä tarvitaan vain antamaan varmasi oikea tulos vertaamista varten sillä onhan bc todettu varmasti oikeelliseksi ja se on varmasti myös tarpeeksi tarkka. Mutta toiminnataan tämä funktio on paljon nopeampi kuin bc.

***

Olisihan näihin BASH:in matemattisiin ongelmiin ihan yleisestikin käytettyjä menetelmiä niinkuin bc ja AWK. Mutta ihan mielenkiinnosta koetan mihin BASH itse pystyy - ja sitäpaitsi pienissä perus-laskutoimituksissa BASH on jo paras. Ja mikäli BASH:in funktioista tehtäisiin kirjastofunktiot niin ne olisivat myös yksinkertaisimmat

BASH:in kehittäjät olivat aikoinaan toteuttaneet toimivat menetelmät moitteettomiin peruslaskuihin ja virtuoosit ovat suunnanneet nuo menetelmät lattian alle. Nimittäin kaikki haluavat päästä BASH:ista eroon sillä BASH laajeni aikoinaan niinpaljon etteivät edes virtuoosit sitä enää täysin hallitse - ei semmoista voi korjata vaan ainoastaan pyrkiä siitä eroon. Ja mikäli osoitettaisiin että BASH hallitsee myös desimaalit olisi siitä paljon hankalampaa päästä eroon.
- siis tosiaan kymmenistätuhansista asioista on tehty paksuja raamattuja täynnä käskyjä ja menetelmien selityksiä. Kenelläkään ei ole pienintäkään mahdollisuutta edes lukea kaikkia niitä - ja nykyäänhän niistä on moni historian hämärissä.

Alkuunsa kielen matemaattisia kykyjä hiotaan suorittamalla "näyttäviä laskuja". Muut kielet harrastivat näitä "näyttäviä laskuja" kolmekymmentävuotta sitten - sillä ne on pakko läpikäydä joskus. Vaikka BASH on kyllä taitanut ne jo silloin kauan sitten niin kukaan ei ole esittänyt toimivaa koodia - joten niitä täytyy esittää nyt:

Tarvitsin taulukkoa jonka ensimmäinen jäsen on .5 ja seuraavat jäsenet aina puolta pienempiä kuin edellinen. Toteutus BASH:illa onnistui hyvin - lukujen tarkkuus oli 26 numeroa ja siihen tarvittiin 86 kahdella jakamistaja. Koko 86:n laskun sarja kesti noin 10 ms - BASH:ilta se on nopeaa vaikka muilla kielillä se onkin hidasta. Skripti on näennäisesti iso, mutta muissa kielissä vastaavat ohjelmat ovat paljon isompia, mutta niissä toiminta tapahtuu kirjastoissa, siis katseelta piilossa. Skriptistä tuli tällainen:
Koodia: [Valitse]

time { number=.1; for n in {1..86}; do apu=${number%%[1-9]*}; apu2=${number:${#apu}}'00000000000000000';apu2=${apu2:0:18}; number=$(($apu2/2))'00000000000000'; (( ${apu2:0:1} == 1 )) && number=0$number; number=${number%%+(0)}; number=$apu$number; echo $n; bc<<<"scale=26;1/(2^$n)" | tr -d '\\\n'; echo; echo .${number:2:26}; echo; done ;}
- bc tarjoaa taas vain vertailuarvon. Mikäli nopeutta haluaa niin se on poistettava samoin kun turhat echot
- tämä on kokoajan melkoisessa kehitysvaiheessa
Otsikko: Vs: Ohjeita shell-skriptaukseen (bash)
Kirjoitti: kamara - 03.07.23 - klo:12.37
Desimaalilaskut suoritetiin silloin aikoinaan kokonaisluvuilla isoissakin koneissa - joten kyllä senaikaiset virtuoosit tiesivät että myös BASH kykenee niihin. Mutta he välttelivät puuttumasta koko asiaan sillä se on sellainen salahautojen viidakko että paraskin virtuoosi haksahtaisi joskus ja sitä he eivät kestä - joutua naurettavuuden kaakinpuuhun. Niinpä he eivät puhuneet koko asiasta - mikä antaa sellaisen mielikuvan etteivät desimaalilaskut BASH:issa edes onnistu. Ja oli heillä laho selityskin: BASH:in matematiikkamoottori ei osaa desimaalilaskuja eikä BASH:in muuttujien tyyppimäärityksissäkään desimaaliluvuista mitään puhuta. Mutta tuo tyyppimääritys on tahallinen väärinymmärrys sillä tekstijonojahan ne desimaaliluvut vain ovat.
 
Toimivat ne desimaalilaskut BASH:issa ihan hyvin vaikkakin merkintätapa on epäsovinnainen - ja yleensä niitä käyttämällä saa aikaan nopeamman skriptin kuin jos turvatuisi matematiikka ohjelmiin. Mutta siis niitä salahautoja on: jakolaskua vaivaa esimerkiksi 'katoavien nollien syndrooma'. Ja kun nolla katoaa on senjälkeinen tulos pelkkää potaskaa.

Kaikki, mikä ei ole mahdotonta, ei ole järkevää. Itse teen bashilla vain pieniä scripteja, koska protoilussa se on tehokas kieli, mutta se  ei ole nopein (kuin koodauksessa) eikä selväkielisin.

Bash:n matematiikasta, silmukoista, ehto-lauseista saan vähintäänkin näppylöitä, ja jopa merkkijonojen käsittelyäkään en sillä mielellään tee.
Se, missä bash on mielestäni hyvä:

Näistä huolimatta joudun usein googlaamaan merkkijonojen, silmukoiden ja ehto-lauseiden  käsittelyä varten, koska niissä bash ei loista.
(Jos meni liian OffTopic:n puolelle, tämän voineen siirtää testauspuolelle.)