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

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #120 : 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ä.
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #121 : 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.

 
« Viimeksi muokattu: 28.09.16 - klo:12.58 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #122 : 13.03.12 - klo:10.27 »
Lainaus
Bless vaikuttaa hyvältä hexeditorilta.

...paitsi että ei suostu itselläni tallentamaan muutoksia.
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #123 : 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.


ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #124 : 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ää.
« Viimeksi muokattu: 13.03.12 - klo:11.52 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #125 : 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
« Viimeksi muokattu: 01.03.15 - klo:20.55 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #126 : 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.
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #127 : 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.
« Viimeksi muokattu: 28.03.12 - klo:06.18 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #128 : 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

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".

 
  
« Viimeksi muokattu: 27.03.12 - klo:09.59 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #129 : 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
« Viimeksi muokattu: 07.04.12 - klo:13.28 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #130 : 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.
 
« Viimeksi muokattu: 02.04.12 - klo:13.59 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #131 : 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ä ?

Asmo Koskinen

  • Käyttäjä
  • Viestejä: 4443
    • Profiili

nm

  • Käyttäjä
  • Viestejä: 16425
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #133 : 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. "

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #134 : 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
  •  Merkkijonomuuttujat ovat aina myös taulukoita joilla on indeksi.
  •  Indeksi alkaa aina numerosta 0, ensimmäisen "laatikon" numero on nolla !
  •  Muuttuja on oletuksena "yksipaikkainen" eli taulukossa on vain yksi elementti.
  •  Kun käsket niin bash tekee kyllä monielementtisen muuttujan, "monta laatikkoa".


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ä...
« Viimeksi muokattu: 06.04.12 - klo:13.12 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #135 : 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
  •  Muuttujan rakenne voidaan määrätä sijoituslauseella monielementtiseksi taulukoksi.
  •  "kissa koira hevonen"   =>   yksi elementtinen taulukko   "=yksi laatikko".
  •  (kissa koira hevonen)    =>   kolme elementtinen taulukko  "=kolme laatikkoa".
  •  Sulkusijoituslauseke säilyttää taulukon rakenteen.
  •  Rakenteen muuttamista yksielementtiseksi voit myös tarvita !


Jatkuu osassa 3...
« Viimeksi muokattu: 06.04.12 - klo:13.34 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #136 : 19.04.12 - klo:20.43 »
Lainaus
...ajetaankin skripti tulkin niin että se tarkistaa skriptin koodin
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #137 : 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.

 

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #138 : 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. 
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #139 : 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ö
« Viimeksi muokattu: 03.06.12 - klo:21.07 kirjoittanut petteriIII »