Erinevus lehekülje "Bash" redaktsioonide vahel

Allikas: Kuutõrvaja
(Sisestus märgini)
(If-elif-else valikulause)
 
(ei näidata 2 kasutaja 84 vahepealset redaktsiooni)
9. rida: 9. rida:
 
Kooriku skript kirjutatakse tekstiredaktoris, kusjuures failil peab olema lugemis- ja käivitamisõigus.
 
Kooriku skript kirjutatakse tekstiredaktoris, kusjuures failil peab olema lugemis- ja käivitamisõigus.
  
Näiteks toome skpriti, mis kirjutab ekraanile "Näe, poolkuu".
+
Näiteks toome skripti, mis kirjutab ekraanile "Näe, poolkuu".
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  echo "Näe, poolkuu"
 
  echo "Näe, poolkuu"
 
+
</source>
 
Selgituseks märgime, et
 
Selgituseks märgime, et
  
19. rida: 20. rida:
 
* echo on sisekäsk, mis väljastab ekraanile oma argumendi
 
* echo on sisekäsk, mis väljastab ekraanile oma argumendi
  
===Skript käivitakse käsurealt===
+
===Skript käivitatakse käsurealt===
  
 
Esmalt tuleb skript teha käivitatavaks öeldes
 
Esmalt tuleb skript teha käivitatavaks öeldes
34. rida: 35. rida:
  
 
===Muutuja ja väärtuse omistamine===
 
===Muutuja ja väärtuse omistamine===
 
  
 
Muutuja nimi peab algama tähe või alakriipsuga, kusjuures võib sisaldada neid ja ka numbreid. Muutuja tüüpi pole tarvis deklareerida.
 
Muutuja nimi peab algama tähe või alakriipsuga, kusjuures võib sisaldada neid ja ka numbreid. Muutuja tüüpi pole tarvis deklareerida.
40. rida: 40. rida:
 
Muutujale väärtuse omistamisel märgitakse teda vaid nimega (a_1), kuid talle viidates peab alustama muutuja nime dollar ($a_1). Näites omistatakse võrdusmärgi abil muutujale väärtus ja kasutatakse seda
 
Muutujale väärtuse omistamisel märgitakse teda vaid nimega (a_1), kuid talle viidates peab alustama muutuja nime dollar ($a_1). Näites omistatakse võrdusmärgi abil muutujale väärtus ja kasutatakse seda
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a_1=5
 
  a_1=5
47. rida: 48. rida:
 
  echo $b_1
 
  echo $b_1
 
  echo $c_1
 
  echo $c_1
 +
</source>
  
 
Tühikute olemasolu ja puudumine on oluline.
 
Tühikute olemasolu ja puudumine on oluline.
52. rida: 54. rida:
 
Kui muutujale väärtust omistades on vaja vältida metasümbolite erilist käsitlust, siis tuleb avaldis kirjutada ülakomade vahele, näiteks
 
Kui muutujale väärtust omistades on vaja vältida metasümbolite erilist käsitlust, siis tuleb avaldis kirjutada ülakomade vahele, näiteks
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  echo 'Ei asenda keskkonnamuutujat $OSTYPE tema väärtusega'
 
  echo 'Ei asenda keskkonnamuutujat $OSTYPE tema väärtusega'
 +
</source>
  
 
Kui on tarvis, et keskkonnamuutujad asenduksid oma väärtustega ning toimuks muutuja väärtustamine, siis kasutatakse jutumärke, näiteks
 
Kui on tarvis, et keskkonnamuutujad asenduksid oma väärtustega ning toimuks muutuja väärtustamine, siis kasutatakse jutumärke, näiteks
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  echo "Keskkonnamuutujate asemele tekivad nende väärtused: $OSTYPE"
 
  echo "Keskkonnamuutujate asemele tekivad nende väärtused: $OSTYPE"
62. rida: 67. rida:
 
  echo "Käsud väärtustatakse: `date`"
 
  echo "Käsud väärtustatakse: `date`"
 
  echo "Aritmeetiline väärtustamine: 11 + 11 = $((11+11))"
 
  echo "Aritmeetiline väärtustamine: 11 + 11 = $((11+11))"
 +
</source>
 +
 +
NB skriptimiskeeltes, jäetakse 'märkide' vahel olevad muutujad muutmata
 +
 +
Kui muutuja ja tema ümbruse vaheline piir ei ole nö ilmne, siis sobib muutuja esitada kujul ${muutujanimi}, nt
 +
 +
#/bin/bash
 +
arv=2
 +
echo "teine koht, ehk ${arv}nd place"
  
 
===Aritmeetilised tehted===
 
===Aritmeetilised tehted===
67. rida: 81. rida:
 
Esitame näited aritmeetiliste tehete kohta
 
Esitame näited aritmeetiliste tehete kohta
  
    * "+, -" - liitmine ja lahutamine
+
* "+, -" - liitmine ja lahutamine
    * "*, /, %" - korrutamine, jagamine ja jääk
+
* "*, /, %" - korrutamine, jagamine ja jääk
    * "<<, >>" - bitinihe  
+
* "<<, >>" - bitinihe  
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=255
 
  a=255
82. rida: 97. rida:
 
  echo "$a % $d = $a_jaak_d"
 
  echo "$a % $d = $a_jaak_d"
 
  echo "$a >> $b = $a_bitinihe_paremale_b"
 
  echo "$a >> $b = $a_bitinihe_paremale_b"
 +
</source>
  
 
===Tingimused ja valikulause===
 
===Tingimused ja valikulause===
89. rida: 105. rida:
 
Arvulisi suurusi võrreldakse järgmiste loogiliste operaatoritega
 
Arvulisi suurusi võrreldakse järgmiste loogiliste operaatoritega
  
    * -eq - võrdne (ingl. k. equal)
+
* -eq - võrdne (ingl. k. equal)
    * -ne - mittevõrdne (ingl. k. not equal)
+
* -ne - mittevõrdne (ingl. k. not equal)
    * -lt - väiksem (ingl. k. less than)
+
* -lt - väiksem (ingl. k. less than)
    * -le - väiksemvõrdne (ingl. k. less equal)
+
* -le - väiksemvõrdne (ingl. k. less equal)
    * -gt - suurem (ingl. k. greater than)
+
* -gt - suurem (ingl. k. greater than)
    * -ge - suuremvõrdne (ingl. k. greater equal)  
+
* -ge - suuremvõrdne (ingl. k. greater equal)  
  
 
Näiteks tingimus
 
Näiteks tingimus
  
  [ 5 < 6 ]
+
  [ 5 -lt 6 ]
  
 
on tõene kuna "viis on väiksem kuuest".
 
on tõene kuna "viis on väiksem kuuest".
  
 
===If-elif-else valikulause===
 
===If-elif-else valikulause===
 +
 +
Esimene skript on üsna lihtne. See kontrollib mis väärtus on muutujal $roll ja muudab vastavalt sellele $roll_id muutuja väärtust
 +
 +
<source lang=bash>
 +
if [ "\"$roll\"" == "kasutaja" ]; then
 +
  roll_id=1;
 +
elif [ "\"$roll\"" == "poweruser" ]; then
 +
  roll_id=2;
 +
elif [ "\"$roll\"" == "adminn" ]; then
 +
  roll_id=3;
 +
fi
 +
</source>
  
 
Skript genereerib juhusliku arvu vahemikus 0 kuni 32 767 (2^15) ja teatab, milline see on
 
Skript genereerib juhusliku arvu vahemikus 0 kuni 32 767 (2^15) ja teatab, milline see on
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=$RANDOM
 
  a=$RANDOM
115. rida: 144. rida:
 
       echo "Juhuslik arv on 20 000 <= $a < 32767"
 
       echo "Juhuslik arv on 20 000 <= $a < 32767"
 
  fi
 
  fi
 +
</source>
  
 
Näites esitatakse tingimused, kusjuures
 
Näites esitatakse tingimused, kusjuures
  
    * -a (AND) nõuab mõlema tingimuse samaaegset täidetust
+
* -a (AND) nõuab mõlema tingimuse samaaegset täidetust
    * -o (OR) väljendab, et täidetud võib olla üks või teine või mõlemad.
+
* -o (OR) väljendab, et täidetud võib olla üks või teine või mõlemad.
    * ! (NOT) alustades tingimust hüüumärgiga märgitakse eitust  
+
* ! (NOT) alustades tingimust hüüumärgiga märgitakse eitust  
  
 
Näiteks tingimus
 
Näiteks tingimus
  
  [ ! 5 < 6 ]
+
  [ ! 5 -lt 6 ]
  
 
on väär kuna "viis on väiksem kuuest", aga kasutatakse loogilist eitust (!).
 
on väär kuna "viis on väiksem kuuest", aga kasutatakse loogilist eitust (!).
130. rida: 160. rida:
 
Kantsulgude asemel võib tingimuse esitamisel kasutada suvalist programmi. Sel juhul tõlgendatakse programmi lõppkoodi väärtust tõeväärtusena, so 0 on tõene ja muu väär. Näites saadab õppejõud igale praktikumist puudujale kirja, milles ta vabandab, et viib õppetööd läbi ilma tegelase osavõtuta
 
Kantsulgude asemel võib tingimuse esitamisel kasutada suvalist programmi. Sel juhul tõlgendatakse programmi lõppkoodi väärtust tõeväärtusena, so 0 on tõene ja muu väär. Näites saadab õppejõud igale praktikumist puudujale kirja, milles ta vabandab, et viib õppetööd läbi ilma tegelase osavõtuta
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  for i in priit miima peeter mart liibu
 
  for i in priit miima peeter mart liibu
138. rida: 169. rida:
 
  fi
 
  fi
 
  done
 
  done
 +
</source>
  
 
Nagu ikka, peab rida jätkav kaigas olema viimane sümbol real.
 
Nagu ikka, peab rida jätkav kaigas olema viimane sümbol real.
143. rida: 175. rida:
 
Tihti kasutatakse tingimuste esitamisel spetsiaalset sisekäsku test. Esitame näite selle käsu kasutamisest
 
Tihti kasutatakse tingimuste esitamisel spetsiaalset sisekäsku test. Esitame näite selle käsu kasutamisest
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=$RANDOM
 
  a=$RANDOM
  if test $a -lt 10000; then
+
if test $a -lt 10000; then
 
       echo "Juhuslik arv $a < 10 000"
 
       echo "Juhuslik arv $a < 10 000"
 
  elif test $a -ge 10000 -a $a -lt 20000; then
 
  elif test $a -ge 10000 -a $a -lt 20000; then
152. rida: 185. rida:
 
       echo "Juhuslik arv on 20 000 <= $a < 32767"
 
       echo "Juhuslik arv on 20 000 <= $a < 32767"
 
  fi
 
  fi
 +
</source>
  
 
If-else tingimust saab esitada ka && (AND) ja || (OR) abil
 
If-else tingimust saab esitada ka && (AND) ja || (OR) abil
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=$RANDOM
 
  a=$RANDOM
 
  (test $a -lt 15000 && echo "$a < 15 000" ) || echo "$a >= 15 000"  
 
  (test $a -lt 15000 && echo "$a < 15 000" ) || echo "$a >= 15 000"  
+
</source>
 +
 
 
  && -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli null (tõene).
 
  && -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli null (tõene).
 
  || -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli üks (väär).
 
  || -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli üks (väär).
164. rida: 200. rida:
 
Loogiliste tehetega mängimiseks on otstarbekas kasutada spetsiaalseid programme true ja false, mis ei tee muud, kui tagastavad vastava lõppkoodi
 
Loogiliste tehetega mängimiseks on otstarbekas kasutada spetsiaalseid programme true ja false, mis ei tee muud, kui tagastavad vastava lõppkoodi
  
 +
<source lang=bash>
 
  bash~$ (true && echo "õige" ) || echo "väär"
 
  bash~$ (true && echo "õige" ) || echo "väär"
 
  õige
 
  õige
 
  bash~$ (false && echo "õige" ) || echo "väär"
 
  bash~$ (false && echo "õige" ) || echo "väär"
 
  väär
 
  väär
 +
</source>
  
 
Tavaline on skriptides kontrollida mingite muutujate seadistust, näiteks kontrollime kas ssl kataloog on seadistatud
 
Tavaline on skriptides kontrollida mingite muutujate seadistust, näiteks kontrollime kas ssl kataloog on seadistatud
  
 +
<source lang=bash>
 
  if [ "$MOD_SSL_DIR" = "" ]
 
  if [ "$MOD_SSL_DIR" = "" ]
 
  then                       
 
  then                       
176. rida: 215. rida:
 
         exit 1                     
 
         exit 1                     
 
  fi  
 
  fi  
 +
</source>
  
 
Leksikograafilisi tingimusi esitatakse järgmiste loogiliste operaatorite abil
 
Leksikograafilisi tingimusi esitatakse järgmiste loogiliste operaatorite abil
  
    * = võrdne
+
* = võrdne
    * != mittevõrdne  
+
* != mittevõrdne  
 +
 
 +
Lihtne stringide võrdlus oleks selline
 +
 
 +
<source lang=bash>
 +
  if [ "$p_real" = "$p_try" ]; then
 +
    echo "Parool klappis!"
 +
  else
 +
    echo "Parool ei sobi"
 +
  fi
 +
</source>
  
 
Esitame näite, kus skript ootab vastust küsimusele, "Kes on Kuutõrvaja üks peategelaskujusid?"
 
Esitame näite, kus skript ootab vastust küsimusele, "Kes on Kuutõrvaja üks peategelaskujusid?"
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  echo "Kes on Kuutõrvaja üks peategelaskujusid?\n"
 
  echo "Kes on Kuutõrvaja üks peategelaskujusid?\n"
 
  read vastus
 
  read vastus
  if test $vastus = "Priit"; then  
+
  if [ $vastus = "Priit" ]; then  
 
   echo "Õige"
 
   echo "Õige"
 
  else
 
  else
 
     echo "Vale"
 
     echo "Vale"
 
  fi
 
  fi
 +
</source>
  
 
Rida 'read vastus' ootab klaviatuurilt sisestust ning omistab selle muutuja $vastus väärtuseks.
 
Rida 'read vastus' ootab klaviatuurilt sisestust ning omistab selle muutuja $vastus väärtuseks.
199. rida: 251. rida:
 
Case valik võimaldab valida mitme tegevuse vahel. Näide teatab, kas sisestatud sõna algustäht oli vahemikust A-K, L-Z või midagi muud
 
Case valik võimaldab valida mitme tegevuse vahel. Näide teatab, kas sisestatud sõna algustäht oli vahemikust A-K, L-Z või midagi muud
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  echo "Sisestage klaviatuurilt sõna:"
 
  echo "Sisestage klaviatuurilt sõna:"
207. rida: 260. rida:
 
               *) echo "Midagi muud";;   
 
               *) echo "Midagi muud";;   
 
  esac  
 
  esac  
 +
</source>
  
 
Valikut määrav regulaaravaldise stiilis avaldis [a-k]*|[A-K]* tähendab, et klapivad stringid, mis algavad väikese vahemikku a kuni k jääva tähega või samasse vahemikku jääva suure tähega.
 
Valikut määrav regulaaravaldise stiilis avaldis [a-k]*|[A-K]* tähendab, et klapivad stringid, mis algavad väikese vahemikku a kuni k jääva tähega või samasse vahemikku jääva suure tähega.
 +
 +
Case abil on hea kirjutada näiteks linux süsteemides stardiskripte
 +
 +
<source lang=bash>
 +
#!/bin/bash
 +
case $1 in
 +
    start)  /bin/su <username> /bin/startup.sh ;;
 +
    stop)    /bin/su <username> /bin/shutdown.sh ;;
 +
    restart)
 +
            /bin/su <username> /bin/shutdown.sh
 +
            /bin/su <username> /bin/startup.sh
 +
            ;;
 +
esac
 +
exit 0
 +
</source>
  
 
===Select valikulause===
 
===Select valikulause===
214. rida: 283. rida:
 
See on väga sarnane Case valikule olles interaktiivsem
 
See on väga sarnane Case valikule olles interaktiivsem
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  select i in "Tartu" "Valga" "Paide" "Ahja" "Ots"
 
  select i in "Tartu" "Valga" "Paide" "Ahja" "Ots"
226. rida: 296. rida:
 
   esac
 
   esac
 
  done
 
  done
 +
</source>
  
 
===Korduslaused===
 
===Korduslaused===
235. rida: 306. rida:
 
For korduses seatakse tavaliselt otseselt paika korduste arv. Toome näite
 
For korduses seatakse tavaliselt otseselt paika korduste arv. Toome näite
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a="Tartu Elva Puhja Polva"
 
  a="Tartu Elva Puhja Polva"
241. rida: 313. rida:
 
  echo $i
 
  echo $i
 
  done
 
  done
 +
</source>
  
 
Iga järgmise kordusega omandab muutuja $i uue väärtuse nimekirjast, milleks on muutuja $a tühikuga eraldatud alamstringid. Muutujale $a võib väärtust omistada ka kasutades käsu väärtustamist, näiteks jätame muutuja $a enda kasutamata
 
Iga järgmise kordusega omandab muutuja $i uue väärtuse nimekirjast, milleks on muutuja $a tühikuga eraldatud alamstringid. Muutujale $a võib väärtust omistada ka kasutades käsu väärtustamist, näiteks jätame muutuja $a enda kasutamata
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  for i in `ls r*`
 
  for i in `ls r*`
249. rida: 323. rida:
 
  echo $i
 
  echo $i
 
  done   
 
  done   
 +
</source>
  
 
Näites kuvatakse kõik r tähega algavad failinimed.
 
Näites kuvatakse kõik r tähega algavad failinimed.
 +
 +
Näide: Lisame kõigile failidele uue eesliidese
 +
 +
Failid: aaa.ee asd.ee uuu.aa
 +
 +
$ for i in *; do mv "$i" "ehee-`basename $i`"; done
 +
 +
Tulemus: ehee-aaa.ee ehee-asd.ee ehee-uuu.aa
  
 
====While korduslause====
 
====While korduslause====
256. rida: 339. rida:
 
While kordust korratakse kuni tingimus on tõene. Näites sooritatakse kordus kuni muutuja $a on väiksem kümnest
 
While kordust korratakse kuni tingimus on tõene. Näites sooritatakse kordus kuni muutuja $a on väiksem kümnest
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=0
 
  a=0
263. rida: 347. rida:
 
  a=$(($a+1))
 
  a=$(($a+1))
 
  done
 
  done
 +
</source>
 +
 +
Või siis saab seda kasutada failist kõikide ridade välja võtmiseks ning töötlemiseks
 +
 +
<source lang=bash>
 +
while read inputline
 +
do
 +
echo $inputline
 +
done < /tmp/viiruslogi.txt
 +
 +
exit 0
 +
</source>
 +
 +
Kolmas näide kuidas while abil lugeda kokku passwd faili ridade arv.
 +
 +
<source lang=bash>
 +
counter=0
 +
while read; do ((counter++)); done </etc/passwd
 +
echo "Lines: $counter"
 +
</source>
 +
 +
Muide kõike seda võib teha ka ühe real, nt loeme nimetused.txt failist failide nimed ja tekitame need kettale
 +
 +
# cat nimetused.txt | while read a; do touch $a & done
  
 
====Until korduslause====
 
====Until korduslause====
268. rida: 376. rida:
 
Kordust sooritatakse kuni tingimus saab tõeseks.
 
Kordust sooritatakse kuni tingimus saab tõeseks.
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a=20
 
  a=20
275. rida: 384. rida:
 
  a=$(($a-1))
 
  a=$(($a-1))
 
  done
 
  done
 +
</source>
  
 
====Kordusest väljumine====
 
====Kordusest väljumine====
280. rida: 390. rida:
 
Kasutades korduse sees lisatingimust, on võimalik kordusest väljuda käsuga exit. Skripti täitmine jätkub kordusele järgnevast käsust. Näites sooritatakse kordus kuni muutuja $i saab võrdseks stringiga "teie"
 
Kasutades korduse sees lisatingimust, on võimalik kordusest väljuda käsuga exit. Skripti täitmine jätkub kordusele järgnevast käsust. Näites sooritatakse kordus kuni muutuja $i saab võrdseks stringiga "teie"
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  a="mina sina tema meie teie nemad"
 
  a="mina sina tema meie teie nemad"
291. rida: 402. rida:
 
  echo "Skripti lõpp"
 
  echo "Skripti lõpp"
 
  done
 
  done
 +
</source>
  
 
===Sisend, väljund ja käsurea argumendid===
 
===Sisend, väljund ja käsurea argumendid===
296. rida: 408. rida:
 
Skripti sisendi vastuvõtmist korraldab käsk read. Loeme näites sisendit kuni andmete lõppemiseni ning trükime välja vastavad suured tähed.
 
Skripti sisendi vastuvõtmist korraldab käsk read. Loeme näites sisendit kuni andmete lõppemiseni ning trükime välja vastavad suured tähed.
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  while read a
 
  while read a
301. rida: 414. rida:
 
  echo $a | tr "a-z" "A-Z"
 
  echo $a | tr "a-z" "A-Z"
 
  done
 
  done
 +
</source>
  
 
Suuname programmi df väljundi skripti sisendisse
 
Suuname programmi df väljundi skripti sisendisse
315. rida: 429. rida:
 
Lisaks on olulised sellised muutujad
 
Lisaks on olulised sellised muutujad
  
    * * - "$*" väärtustub stringiks, kus on kõik argumendid tühikutega eraldatud
+
* * - "$*" väärtustub stringiks, kus on kõik argumendid tühikutega eraldatud
    * @ - "$@" väärtustub mitmeks stringiks, mis on vastavalt käsurea argumendid
+
* @ - "$@" väärtustub mitmeks stringiks, mis on vastavalt käsurea argumendid
    * # - "$#" käsurea argumentide arv
+
* # - "$#" käsurea argumentide arv
    * ? - "$?" eelmise esiplaanil käivitatud programmi väljundkood
+
* ? - "$?" eelmise esiplaanil käivitatud programmi väljundkood
    * $ - "$$" skripti enda PID
+
* $ - "$$" skripti enda PID
    * ! - "!$" viimati tagaplaanil käivitatud programmi nimi
+
* ! - "!$" viimati tagaplaanil käivitatud programmi nimi
    * 0 - "$0" skripti enda nimi
+
* 0 - "$0" skripti enda nimi
 +
 
 +
"$@" on kõige õigem ja puhtam meetod argumentide edasikandmiseks, säilitatakse kõik tühikud jm metamärgid. Ja tegu ei ole mitte bashismi, vaid klassikalise Bourne shelli omadusega.
 +
 
 +
Alternatiivid -- $@, $*, "$*" -- on eri kõrvalmõjudega. Mdx mõnel juhul on abiks ka kontruktsioon "saba $@ sarved"  nSeejuures $@ ei käitu nagu tavalised muutujad, kui topeltjutumärkide vahele satuvad, vaid seal laiendatakse seda justkui: "$1" "$2" - ehk siis kui iga argument oleks eraldi topeltjutumärkide vahel antud.
 +
 
 +
Natuke mõeldes leiab sellisele käitumisele ka kasutusjuhu - kui ühe argumendi sees on tühikud:
 +
 
 +
<source lang=bash>
 +
#!/bin/bash
 +
 +
f1(){
 +
  for arg in "$@"; do echo "arg: $arg";done
 +
}
 +
 +
f2(){
 +
  args=$@
 +
  for arg in $args; do echo "arg: $arg"; done
 +
}
 +
 
 +
echo "F1:"
 +
f1 a b c "d ja e"
 +
echo "F2:"
 +
f2 a b c "d ja e"
 +
</source>
 +
 
 +
Tulemus:
 +
% bash test.sh
 +
F1:
 +
arg: a
 +
arg: b
 +
arg: c
 +
arg: d ja e
 +
F2:
 +
arg: a
 +
arg: b
 +
arg: c
 +
arg: d
 +
arg: ja
 +
arg: e
 +
 
 +
Veel üks praktiline näide, järgnev skript käivitab kõik argumendid ükshaaval ettemääratud nodes
 +
 
 +
<source lang=bash>
 +
  #!/bin/bash
 +
    for osd in "$@"; do
 +
    ssh -q $node argument=$argument bash <<EOF
 +
    $osd
 +
  EOF
 +
  done
 +
</source>
 +
 
 +
Tasub tähele panna ssh käsus $node argument=$argument konstruktsiooni, mis annab muutuja edasi kaugel olevale serverile
  
 
===Failitestid===
 
===Failitestid===
327. rida: 493. rida:
 
Sisekäsk test võimaldab kontrollida faili tüüpi. Näiteks eristame kataloogis olevad lingid, tavalised faili ja kataloogid
 
Sisekäsk test võimaldab kontrollida faili tüüpi. Näiteks eristame kataloogis olevad lingid, tavalised faili ja kataloogid
  
  #!/bin/sh
+
Näiteks
 +
 
 +
<source lang=bash>
 +
if [ -r test.jpg ]
 +
then
 +
  echo "Fail on loetav"
 +
fi
 +
</source>
 +
 
 +
Keerukam näide:
 +
 
 +
<source lang=bash>
 +
  #!/bin/bash
 
  for i in `ls -a`
 
  for i in `ls -a`
 
  do
 
  do
338. rida: 516. rida:
 
  fi
 
  fi
 
  done
 
  done
 +
</source>
 +
 +
Kasutatavad võtmed on järgnevad:
 +
 +
*-d Kas tegu kataloogiga
 +
*-f Kas tegu failiga
 +
*-r Kas fail loetav
 +
*-s Kas fail nullist erineva suurusega
 +
*-w Kas fail kirjutatav
 +
*-x Kas fail käivitatav
 +
*-L Kas tegu lingiga
 +
 +
===Käskude käivitamine skripti sees===
 +
 +
Aegajalt tuleb ette, et on vaja käivitada mingi käsk ning selle tulemit
 +
skripti sees töödelda. Oletame, et soovime saada skriptis käsu: uname -a väjundi
 +
 +
Selleks olemas kolm võimalust, esiteks kasutades eraldajaid
 +
 +
echo `uname -a`
 +
 +
Või
 +
 +
echo $(uname -a)
 +
 +
Käsu tulemi saab kergelt edastada muutujale:
 +
 +
OS=$(uname -a)
 +
echo $OS
  
 
===Sisestus märgini===
 
===Sisestus märgini===
  
Märgini sisestamine võimaldab vältida mitmete järjestikuste käskude echo kasutamist. Toome näite CGI valdkonnast
+
Märgini sisestamine võimaldab vältida mitmete järjestikuste käskude echo kasutamist.  
 +
 
 +
cat >> /home/kasutaja/fail <<EOT
 +
esimene rida
 +
teine rida
 +
kolmas rida
 +
EOT
 +
 
 +
NB! EOT või EOF peab olema rea alguses, kui on taandega, pannakse kuni faili lõpuni kõik stringi sisse.
 +
 
 +
Toome näite CGI valdkonnast
  
  #!/bin/sh
+
<source lang=bash>
 +
  #!/bin/bash
 
  echo -e "Content-type: text/html\n\n"
 
  echo -e "Content-type: text/html\n\n"
 
  a=`df`
 
  a=`df`
 
  cat <<mark
 
  cat <<mark
  < HTML >< BODY >
+
  <HTML><BODY>
  < p >Tere, masina failisüsteemiga on sellised lood:< p >
+
  <p>Tere, masina failisüsteemiga on sellised lood:<p>
  < pre >
+
  <pre>
 
  $a
 
  $a
  < /pre >
+
  </pre>
  < pre >
+
  <pre>
 
  $(hostname -f)
 
  $(hostname -f)
 
  $(date)
 
  $(date)
  < /pre >
+
  </pre>
  < /BODY >< /HTML >
+
  </BODY></HTML>
 
  mark
 
  mark
 +
</source>
  
 
Kusjuures, kui 'cat <<mark' rida asendada 'cat <<\mark', siis muutujaid ei asendata ega väärtustata.
 
Kusjuures, kui 'cat <<mark' rida asendada 'cat <<\mark', siis muutujaid ei asendata ega väärtustata.
363. rida: 582. rida:
 
===Funktsioonid===
 
===Funktsioonid===
  
Tihti esinevaid järgnevusi on mõistlik kirjeldada kord funktsioonina ning hiljem kasutada nende poole pöördumiseks seda funktsiooni. Näideks loome funktsiooni, mis paneb teksti < pre > ja < /pre > märkide vahele.
+
Tihti esinevaid järgnevusi on mõistlik kirjeldada kord funktsioonina ning hiljem kasutada nende poole pöördumiseks seda funktsiooni. Näiteks loome funktsiooni, mis paneb teksti < pre > ja < /pre > märkide vahele.
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
  function pre () {
 
  function pre () {
  echo -e "< pre >$1< /pre >"
+
  echo -e "<pre>$1</pre>"
 
  }
 
  }
 
   
 
   
  echo -e "Content-type: text/html\n\n< HTML >< BODY >"
+
  echo -e "Content-type: text/html\n\n<HTML ><BODY>"
 
  pre "`df`"
 
  pre "`df`"
 
  pre "`uptime`"
 
  pre "`uptime`"
  echo "< /BODY >< /HTML >"
+
  echo "</BODY></HTML>"
 +
</source>
  
 
Funktsioonis kirjeldatu käivitamiseks ei tekitata uut koorikut. Argumente saab kasutada sarnaselt skriptile endale.
 
Funktsioonis kirjeldatu käivitamiseks ei tekitata uut koorikut. Argumente saab kasutada sarnaselt skriptile endale.
  
Näiteks küsime kinnitust kasutajalt mingi tegevuse toestamise kohta
+
Teise näitena küsime kinnitust kasutajalt mingi tegevuse toestamise kohta
  
 +
<source lang=bash>
 
  kinnitus(){                                                 
 
  kinnitus(){                                                 
 
         echo                                                 
 
         echo                                                 
390. rida: 612. rida:
 
         fi                     
 
         fi                     
 
  }  
 
  }  
 +
</source>
  
Kasutamieks siis
+
Kasutamine järgnev
  
 
  kinnitus "installime vajalikud komponendid ?"
 
  kinnitus "installime vajalikud komponendid ?"
 +
 +
Üks shelliskriptis suisa häda vajalik funktsioon oleks kontrollida kas eelmine käsk on väljastanud mingi veateate
 +
ehk siis lõpetanud enda tegevuse ebaviisakalt
 +
 +
<source lang=bash>
 +
kas_korras(){                                   
 +
  RET=$?                                       
 +
  if [ "$RET" != '0' ]                         
 +
  then                                         
 +
    echo                                       
 +
    echo "eelmine käsk väljastas: $RET"       
 +
    exit $RET                                   
 +
  fi                                           
 +
}
 +
</source>
 +
 +
Ning seejärel võib lisada näiteks make käsu järele kontrollfunktskiooni
 +
 +
kas_korras
 +
 +
Võimalus on ka genereerida funktsiooniga paroole:
 +
 +
genpasswd() {
 +
        local l=$1
 +
        [ "$l" == "" ] && l=16
 +
        tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${l} | xargs
 +
}
 +
 +
Ja kasutamiseksd
 +
 +
genpasswd 8
 +
 +
===Käsurea parameetrid===
 +
 +
Skripti järgi antud parameetrid omistatakse muutujatele $1 $2 $3 jne
 +
 +
Näiteks soovime anda sedasi parameetreid:
 +
 +
$ ./skript eesnimi perenimi vanus
 +
 +
Skript mis need väljastab võiks olla selline
 +
 +
#!/bin/bash
 +
    echo $1
 +
    echo $2
 +
    echo $3
  
 
===Lock faili kasutamine skriptis===
 
===Lock faili kasutamine skriptis===
403. rida: 672. rida:
 
skripti idee lihtsalt tuhat sekundit passida
 
skripti idee lihtsalt tuhat sekundit passida
  
 +
<source lang=bash>
 
  #!/bin/sh
 
  #!/bin/sh
 
   if [ -f /var/run/skript.lock ]; then
 
   if [ -f /var/run/skript.lock ]; then
408. rida: 678. rida:
 
     return
 
     return
 
   else
 
   else
     lockf -t 0 /var/run/skript.lock '''sleep 1000'''
+
     lockf -t 0 /var/run/skript.lock sleep 1000
 
   fi
 
   fi
 +
</source>
  
 
Paneme skripti toimima taustal
 
Paneme skripti toimima taustal
421. rida: 692. rida:
 
  eelmine programm ikka veel toimib.
 
  eelmine programm ikka veel toimib.
  
 +
Lockfaili võib muidugi käsu lockf puudumisel tekitada ka lihtsalt mkdir-touch käsuga ja hiljem eemaldada, näiteks
 +
 +
  if [ -f /var/run/$1.lock ]; then
 +
    echo "$1 eelmine mirror ikka veel töötab."
 +
    return
 +
  else
 +
if mkdir /run/$1.lock; then
 +
  rsync  ....
 +
  rmdir /run/$1.lock
 +
fi
 +
 +
===Bash ja internet===
 +
 +
Bash võimaldab tegeleda ka võrgu socketiga. Viimaste kaudu on võimalik avada võrguühendus
 +
mõne pordiga ja vahetada sellega infot.
 +
 +
Socketi asukohaks Linuxis '''/dev/tcp/SERVER/PORT'''
 +
 +
Näiteks saadame emaili serveri zoo.tartu.ee 25 pordile HELO käsu
 +
 +
<source lang=bash>
 +
#avame socketi
 +
exec 3<>/dev/tcp/zoo.edu.ee/25
 +
# saadame HELO käsu
 +
echo -e "HELO mail.edu.ee" >&3
 +
# loeme tulemust
 +
while read line <&3
 +
  do
 +
  echo -n $line >&1
 +
  done
 +
# saadame QUIT käsu
 +
echo -e "QUIT" >&3
 +
# sulgeme sisendi
 +
exec 3<&-
 +
# sulgeme väljundi
 +
exec 3>&-
 +
</source>
 +
 +
===Subshelli kasutamine===
 +
 +
Sulgude vahel esitatu töötab omaette keskkonnas, mille kohta öeldakse, et ta moodustab subshelli. Nt sobib subshelli abil suunata mitme käsu väljundit koos edasi
 +
 +
<source lang=bash>
 +
$ (echo tere1; echo tere2) | tr a-z A-Z
 +
TERE1
 +
TERE2
 +
</source>
 +
 +
===Skriptide hingeelust===
 +
 +
Shelli skriptidele saadetakse SIGHUP (võibolla veel midagi), kui ühendus katkeb ja selle peale lõpetab skript tavaliselt töö (ja üritab oma lapsed kah ära tappa).
 +
 +
Testimisel piisas, kui SIGHUP kinni püüda ja ignoreerida. Lõpus skript, mis seda demob.
 +
 +
<source lang=bash>
 +
#!/usr/local/bin/bash$
 +
$
 +
trap trhup HUP$
 +
trhup(){$
 +
  debug "SIGHUP detected.. interesting"$
 +
}$
 +
$
 +
debug(){$
 +
  echo "`date` $1" >> tulemus.txt$
 +
}$
 +
$
 +
debug "ALGUS"$
 +
echo -n "Katsetuse testimine. Toksi teksti: "$
 +
read -t 10 VASTUS$
 +
$
 +
debug "VASTUS: $VASTUS"$
 +
</source>
 +
 +
===Sõnastiku kasutamine===
 +
 +
Bash v. 4 võimaldab kasutada sõnastikku (ingl. k. associative array). Nt olgu failisüsteemis sellised failid
 +
 +
$ pwd
 +
/home/priit/skriptid
 +
$ find . -type f
 +
./sql/konvert_ee/konvert_20120515.sql
 +
./sql/konvert_ee/konvert_20120515a.sql
 +
./sql/konvert_ee/konvert_20120515b.sql
 +
./sql/konvert_20120520a.sql
 +
./sql/konvert_20120505.sql
 +
./sql/konvert_20120520b.sql
 +
./sql/konvert_20120520.sql
 +
 +
ning eesmärk käivitada, sõltumata sellest, millises kataloogis üks või teine skript asub, nad kronoloogilises järjekorras (kasutades seejuures faili nimes esinevat YYYYMMDD osa).
 +
 +
$ cat jarjesta-ja-kaivita.sh
 +
#!/bin/bash
 +
declare -A sql_kataloog
 +
 +
for i in `cd /home/priit/skriptid; find sql -type f | xargs`; do
 +
  j=`echo $i | sed 's/sql\/konvert_ee\///' | sed 's/sql\/konvert_lv\///' | sed 's/sql\///'`
 +
  sql_kataloog[$j]=$i
 +
done
 +
 +
for key in `export LANG=C; echo ${!sql_kataloog[@]} | tr " " "\n" | sort | tr "\n" " "` ; do
 +
  echo psql ${sql_kataloog[$key]}
 +
done
 +
 +
kus
 +
 +
* declare -A sql_kataloog - kirjeldab sõnastik tüüpi muutuja
 +
* find ja xargs abil moodustatakse kataloogiteedega failinimede nimekiri
 +
* sql_kataloog[$j]=$i - omistatakse sõnastiku võtmele (nt konvert_20120515b.sql) vastav väärtus (nt sql/konvert_ee/konvert_20120515b.sql)
 +
* export LANG=C abil saab mõjutada järjestust
 +
* echo ... tr ... sort ... tr abil järjestatakse sõnastik võtmete järjekorras
 +
 +
Kävitamisel vajastatakse
 +
 +
$ ./jarjesta-ja-kaivita.sh
 +
psql sql/konvert_20120505.sql
 +
psql sql/konvert_ee/konvert_20120515a.sql
 +
psql sql/konvert_ee/konvert_20120515b.sql
 +
psql sql/konvert_ee/konvert_20120515.sql
 +
psql sql/konvert_20120520a.sql
 +
psql sql/konvert_20120520b.sql
 +
psql sql/konvert_20120520.sql
 +
 +
===Tingimuse kasutamine programmi väljundkoodiga===
 +
 +
If tingimusega saab kasutada lisaks test vms konstruktsioonidele ka programmide väljundkoode (ingl. k. exit code, exit status) arvestades asjaolu, et reeglina on väljundkood programmi töö ilma veata lõppemisel 0 ning igal muul juhul > 0. Nt sobiks sellise skripti abil kustutada mingi rakenduse töö käigus tekkivaid zip faile /tmp kataloogist
 +
 +
for i in $(find /tmp -maxdepth 1 -mtime +2 -type f | xargs); do
 +
  if $(unzip -qt $i 1>/dev/null 2>&1) ; then
 +
    rm $i
 +
  fi
 +
done
 +
 +
===Sõnedega töötamine===
 +
 +
Sõnest (ingl. k. string) saab eralda osa selliselt, viienda positsiooni järelt lõpuni
 +
 +
$ sone="tuulevaikus"
 +
$ echo ${sone:5}
 +
vaikus
 +
 +
Või expr abil
 +
 +
$ sone="tuulevaikus"
 +
$ echo `expr substr $sone 6 7`
 +
vaikus
 +
 +
Kasulikud lisamaterjalid
 +
 +
* http://tldp.org/LDP/abs/html/string-manipulation.html
 +
 +
===Tühikuid sisaldavate failinimedega töötamine===
 +
 +
Kui faili või katalooginimedes esinevad tühikud, siis selline konstruktsioon ei tööta
 +
 +
$ for i in *; do du -sk $i; done
 +
 +
aga töötab selline find + while kasutamine
 +
 +
$ find . -type d ! -name . | while read nimi; do du -sk "$nimi"; done
 +
8      ./Teater Vanemuine
 +
8      ./Kala maja
 +
8      ./Supi linn
 +
 +
===Skriptisiseste teadete ja veateadetega tegelemine===
 +
 +
Bash shelli stdout ja stderr
 +
 +
0 Stdin - kasutatakse info programmi sisestamiseks
 +
1 stdout - kasutatakse info kirjutamiseks ekraanile
 +
2 stderr - kasutatakse error teadete väljutamiseks
 +
 +
Näiteks veateadete faili logimine toimib nii
 +
 +
$ program-name 2> error.log
 +
 +
Nii veatedete kui niisama programmi sisteste teadete faili suunamiseks
 +
 +
$ command-name &>file
 +
 +
Selleks, et suunatada stderr ümber stdouti
 +
 +
$ command-name 2>&1
 +
 +
Programmi väljundi saab saata /dev/nulli lihtsa käsuga > /dev/null kahjuks ei tööta
 +
see programmidega, mis väljutavad ohtralt standard error 2 teateid. Selleks tuleb kasutada järgnevat süntaksit
 +
 +
$ command > /dev/null 2>&1
 +
 +
===Suvaliste numbrite 0-10 genereerimine===
 +
 +
Mitte seotud küll bashi sisemise struktuuri vaid hulga unixi/linuxi käskude koos kasutamisega aga tulem
 +
kulub sageli bashis ära.
  
===Kasulikud lisamaterjalid===
+
grep -m1 -ao '[0-9]' /dev/urandom | sed s/0/10/ | head -n1
  
http://www.tinker.ncsu.edu/LEGO/shell_help.html
+
===Mõned kasulikud skriptide näited===
  
Bashi koorik
+
[[Mirrordamise skript]] Sobilik näiteks avaliku FTP serveri loomiseks.
  
© EENet 2000
+
[[Skript posti quota kontrollimiseks mboxidel]]

Viimane redaktsioon: 14. mai 2019, kell 16:17

Bash kooriku skript

Kooriku skript on tekstifail, millesse on kirjutatud üksteise järele programmi nimed või kooriku sisekäsud. Lisaks saab kasutada programmeerimisele iseloomulikke konstruktsioone, nagu tingimus, kordus- ja valiklause jne. Koorik on interpreteeritav keel.

Paljud süsteemi utiliidid on kooriku skriptid.

Skripti kirjutamine ja käivitamine

Kooriku skript kirjutatakse tekstiredaktoris, kusjuures failil peab olema lugemis- ja käivitamisõigus.

Näiteks toome skripti, mis kirjutab ekraanile "Näe, poolkuu".

 #!/bin/sh
 echo "Näe, poolkuu"

Selgituseks märgime, et

  • #!/bin/sh näitab interpretaatori nime
  • echo on sisekäsk, mis väljastab ekraanile oma argumendi

Skript käivitatakse käsurealt

Esmalt tuleb skript teha käivitatavaks öeldes

 $ chmod 0755 skript.sh

ja käivitada

 $ ./skript.sh

Heaks kombeks on skripti nimi lõpetada mõnetähelise kombinatsiooniga, mis viitab interpretaatorile - '.sh'.

Skripti käivitamisel käivitatakse uus koorik, milles skripti käsud täidetakse.

Muutuja ja väärtuse omistamine

Muutuja nimi peab algama tähe või alakriipsuga, kusjuures võib sisaldada neid ja ka numbreid. Muutuja tüüpi pole tarvis deklareerida.

Muutujale väärtuse omistamisel märgitakse teda vaid nimega (a_1), kuid talle viidates peab alustama muutuja nime dollar ($a_1). Näites omistatakse võrdusmärgi abil muutujale väärtus ja kasutatakse seda

 #!/bin/sh
 a_1=5
 b_1=$a_1
 c_1=Tere
 echo $a_1 
 echo $b_1
 echo $c_1

Tühikute olemasolu ja puudumine on oluline.

Kui muutujale väärtust omistades on vaja vältida metasümbolite erilist käsitlust, siis tuleb avaldis kirjutada ülakomade vahele, näiteks

 #!/bin/sh
 echo 'Ei asenda keskkonnamuutujat $OSTYPE tema väärtusega'

Kui on tarvis, et keskkonnamuutujad asenduksid oma väärtustega ning toimuks muutuja väärtustamine, siis kasutatakse jutumärke, näiteks

 #!/bin/sh
 echo "Keskkonnamuutujate asemele tekivad nende väärtused: $OSTYPE"
 echo "Muutujad väärtustatakse: $(date)"
 echo "Käsud väärtustatakse: `date`"
 echo "Aritmeetiline väärtustamine: 11 + 11 = $((11+11))"

NB skriptimiskeeltes, jäetakse 'märkide' vahel olevad muutujad muutmata

Kui muutuja ja tema ümbruse vaheline piir ei ole nö ilmne, siis sobib muutuja esitada kujul ${muutujanimi}, nt

#/bin/bash
arv=2
echo "teine koht, ehk ${arv}nd place"

Aritmeetilised tehted

Esitame näited aritmeetiliste tehete kohta

  • "+, -" - liitmine ja lahutamine
  • "*, /, %" - korrutamine, jagamine ja jääk
  • "<<, >>" - bitinihe
 #!/bin/sh
 a=255
 b=1
 c=5
 d=50
 a_miinus_c=$(($a - $b))
 a_jaak_d=$(($a % $d))
 a_bitinihe_paremale_b=$(($a >> $b))
 echo "$a - $b = $a_miinus_c"
 echo "$a % $d = $a_jaak_d"
 echo "$a >> $b = $a_bitinihe_paremale_b"

Tingimused ja valikulause

Valikulause võimaldab vastavalt seatud tingimustele programmi edasist käiku suunata.

Arvulisi suurusi võrreldakse järgmiste loogiliste operaatoritega

  • -eq - võrdne (ingl. k. equal)
  • -ne - mittevõrdne (ingl. k. not equal)
  • -lt - väiksem (ingl. k. less than)
  • -le - väiksemvõrdne (ingl. k. less equal)
  • -gt - suurem (ingl. k. greater than)
  • -ge - suuremvõrdne (ingl. k. greater equal)

Näiteks tingimus

[ 5 -lt 6 ]

on tõene kuna "viis on väiksem kuuest".

If-elif-else valikulause

Esimene skript on üsna lihtne. See kontrollib mis väärtus on muutujal $roll ja muudab vastavalt sellele $roll_id muutuja väärtust

if [ "\"$roll\"" == "kasutaja" ]; then
   roll_id=1;
elif [ "\"$roll\"" == "poweruser" ]; then
   roll_id=2;
elif [ "\"$roll\"" == "adminn" ]; then
   roll_id=3;
fi

Skript genereerib juhusliku arvu vahemikus 0 kuni 32 767 (2^15) ja teatab, milline see on

 #!/bin/sh
 a=$RANDOM
   if [ $a -lt 10000 ]; then
      echo "Juhuslik arv $a < 10 000"
 elif [ $a -ge 10000 -a $a -lt 20000 ]; then
      echo "Juhuslik arv on 10 000 <= $a < 20 000"
 else [ $a -ge 20000 -a $a -lt 32767 ]
      echo "Juhuslik arv on 20 000 <= $a < 32767"
 fi

Näites esitatakse tingimused, kusjuures

  • -a (AND) nõuab mõlema tingimuse samaaegset täidetust
  • -o (OR) väljendab, et täidetud võib olla üks või teine või mõlemad.
  •  ! (NOT) alustades tingimust hüüumärgiga märgitakse eitust

Näiteks tingimus

[ ! 5 -lt 6 ]

on väär kuna "viis on väiksem kuuest", aga kasutatakse loogilist eitust (!).

Kantsulgude asemel võib tingimuse esitamisel kasutada suvalist programmi. Sel juhul tõlgendatakse programmi lõppkoodi väärtust tõeväärtusena, so 0 on tõene ja muu väär. Näites saadab õppejõud igale praktikumist puudujale kirja, milles ta vabandab, et viib õppetööd läbi ilma tegelase osavõtuta

 #!/bin/sh
 for i in priit miima peeter mart liibu
 do
 if ! who | grep $i ; then
   echo -e "Subject: Ma vabandan, et Teid polnud kohal\n\n." | \
 /usr/sbin/sendmail $i
 fi
 done

Nagu ikka, peab rida jätkav kaigas olema viimane sümbol real.

Tihti kasutatakse tingimuste esitamisel spetsiaalset sisekäsku test. Esitame näite selle käsu kasutamisest

 #!/bin/sh
 a=$RANDOM
 if test $a -lt 10000; then
      echo "Juhuslik arv $a < 10 000"
 elif test $a -ge 10000 -a $a -lt 20000; then
      echo "Juhuslik arv on 10 000 <= $a < 20 000"
 else test $a -ge 20000 -a $a -lt 32767
      echo "Juhuslik arv on 20 000 <= $a < 32767"
 fi

If-else tingimust saab esitada ka && (AND) ja || (OR) abil

 #!/bin/sh
 a=$RANDOM
 (test $a -lt 15000 && echo "$a < 15 000" ) || echo "$a >= 15 000"
&& -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli null (tõene).
|| -le järgnev käsk täidetakse, kui eelnenud käsu lõppkood oli üks (väär).

Loogiliste tehetega mängimiseks on otstarbekas kasutada spetsiaalseid programme true ja false, mis ei tee muud, kui tagastavad vastava lõppkoodi

 bash~$ (true && echo "õige" ) || echo "väär"
 õige
 bash~$ (false && echo "õige" ) || echo "väär"
 väär

Tavaline on skriptides kontrollida mingite muutujate seadistust, näiteks kontrollime kas ssl kataloog on seadistatud

 if [ "$MOD_SSL_DIR" = "" ]
 then                      
         echo "MOD_SSL_DIR seadmata"
         exit 1                     
 fi

Leksikograafilisi tingimusi esitatakse järgmiste loogiliste operaatorite abil

  • = võrdne
  •  != mittevõrdne

Lihtne stringide võrdlus oleks selline

  if [ "$p_real" = "$p_try" ]; then
    echo "Parool klappis!"
  else
    echo "Parool ei sobi"
  fi

Esitame näite, kus skript ootab vastust küsimusele, "Kes on Kuutõrvaja üks peategelaskujusid?"

 #!/bin/sh
 echo "Kes on Kuutõrvaja üks peategelaskujusid?\n"
 read vastus
 if [ $vastus = "Priit" ]; then 
   echo "Õige"
 else
    echo "Vale"
 fi

Rida 'read vastus' ootab klaviatuurilt sisestust ning omistab selle muutuja $vastus väärtuseks.

Case valikulause

Case valik võimaldab valida mitme tegevuse vahel. Näide teatab, kas sisestatud sõna algustäht oli vahemikust A-K, L-Z või midagi muud

 #!/bin/sh
 echo "Sisestage klaviatuurilt sõna:"
 read a
 case $a in
   [a-k]*|[A-K]*) echo "Sisestatud sõna algab tähega vahemikust A kuni K";;
   [l-z]*|[L-Z]*) echo "Sisestatud sõna algab tähega vahemikust L kuni Z";;
               *) echo "Midagi muud";;  
 esac

Valikut määrav regulaaravaldise stiilis avaldis [a-k]*|[A-K]* tähendab, et klapivad stringid, mis algavad väikese vahemikku a kuni k jääva tähega või samasse vahemikku jääva suure tähega.

Case abil on hea kirjutada näiteks linux süsteemides stardiskripte

#!/bin/bash
case $1 in
    start)   /bin/su <username> /bin/startup.sh ;;
    stop)    /bin/su <username> /bin/shutdown.sh ;;
    restart)
             /bin/su <username> /bin/shutdown.sh
             /bin/su <username> /bin/startup.sh
             ;;
esac
exit 0

Select valikulause

See on väga sarnane Case valikule olles interaktiivsem

 #!/bin/sh
 select i in "Tartu" "Valga" "Paide" "Ahja" "Ots"
 do
 case $i in
 Tartu) echo "Tartu" ;;
 Valga) echo "Valga" ;;
 Paide) echo "Paide" ;;
  Ahja) echo "Ahja" ;;
  Ots) exit ;;  
     *) echo "midagi muud" ;;
  esac
 done

Korduslaused

Kordus võimaldab teatud arv kordi korrata tegevust.

For korduslause

For korduses seatakse tavaliselt otseselt paika korduste arv. Toome näite

 #!/bin/sh
 a="Tartu Elva Puhja Polva"
 for i in $a
 do
 echo $i
 done

Iga järgmise kordusega omandab muutuja $i uue väärtuse nimekirjast, milleks on muutuja $a tühikuga eraldatud alamstringid. Muutujale $a võib väärtust omistada ka kasutades käsu väärtustamist, näiteks jätame muutuja $a enda kasutamata

 #!/bin/sh
 for i in `ls r*`
 do
 echo $i
 done

Näites kuvatakse kõik r tähega algavad failinimed.

Näide: Lisame kõigile failidele uue eesliidese

Failid: aaa.ee asd.ee uuu.aa

$ for i in *; do mv "$i" "ehee-`basename $i`"; done

Tulemus: ehee-aaa.ee ehee-asd.ee ehee-uuu.aa

While korduslause

While kordust korratakse kuni tingimus on tõene. Näites sooritatakse kordus kuni muutuja $a on väiksem kümnest

 #!/bin/sh
 a=0
 while [ $a -lt 10 ]
 do
 echo $a
 a=$(($a+1))
 done

Või siis saab seda kasutada failist kõikide ridade välja võtmiseks ning töötlemiseks

while read inputline
do
 echo $inputline
done < /tmp/viiruslogi.txt

exit 0

Kolmas näide kuidas while abil lugeda kokku passwd faili ridade arv.

counter=0
while read; do ((counter++)); done </etc/passwd
echo "Lines: $counter"

Muide kõike seda võib teha ka ühe real, nt loeme nimetused.txt failist failide nimed ja tekitame need kettale

# cat nimetused.txt | while read a; do touch $a & done

Until korduslause

Kordust sooritatakse kuni tingimus saab tõeseks.

 #!/bin/sh
 a=20
 while [ $a -lt 10 ]
 do
 echo $a
 a=$(($a-1))
 done

Kordusest väljumine

Kasutades korduse sees lisatingimust, on võimalik kordusest väljuda käsuga exit. Skripti täitmine jätkub kordusele järgnevast käsust. Näites sooritatakse kordus kuni muutuja $i saab võrdseks stringiga "teie"

 #!/bin/sh
 a="mina sina tema meie teie nemad"
 for i in $a
 do
 if test $i = "teie"; then
    exit
 else
    echo $i
 fi
 echo "Skripti lõpp"
 done

Sisend, väljund ja käsurea argumendid

Skripti sisendi vastuvõtmist korraldab käsk read. Loeme näites sisendit kuni andmete lõppemiseni ning trükime välja vastavad suured tähed.

 #!/bin/sh
 while read a
 do
 echo $a | tr "a-z" "A-Z"
 done

Suuname programmi df väljundi skripti sisendisse

bash~$ df | skript.sh

Skripti väljund tekitatakse tavaliselt käsuga echo.

Skripti käivitamisel saab anda ka argumente, kusjuures nende väärtused on skriptis muutujates $1, $2 jne.

#!/bin/sh
echo $1 $2 $3

Lisaks on olulised sellised muutujad

  • * - "$*" väärtustub stringiks, kus on kõik argumendid tühikutega eraldatud
  • @ - "$@" väärtustub mitmeks stringiks, mis on vastavalt käsurea argumendid
  • # - "$#" käsurea argumentide arv
  •  ? - "$?" eelmise esiplaanil käivitatud programmi väljundkood
  • $ - "$$" skripti enda PID
  •  ! - "!$" viimati tagaplaanil käivitatud programmi nimi
  • 0 - "$0" skripti enda nimi

"$@" on kõige õigem ja puhtam meetod argumentide edasikandmiseks, säilitatakse kõik tühikud jm metamärgid. Ja tegu ei ole mitte bashismi, vaid klassikalise Bourne shelli omadusega.

Alternatiivid -- $@, $*, "$*" -- on eri kõrvalmõjudega. Mdx mõnel juhul on abiks ka kontruktsioon "saba $@ sarved" nSeejuures $@ ei käitu nagu tavalised muutujad, kui topeltjutumärkide vahele satuvad, vaid seal laiendatakse seda justkui: "$1" "$2" - ehk siis kui iga argument oleks eraldi topeltjutumärkide vahel antud.

Natuke mõeldes leiab sellisele käitumisele ka kasutusjuhu - kui ühe argumendi sees on tühikud:

 #!/bin/bash
 
 f1(){
   for arg in "$@"; do echo "arg: $arg";done
 }
 
 f2(){
  args=$@
  for arg in $args; do echo "arg: $arg"; done
 }

 echo "F1:"
 f1 a b c "d ja e"
 echo "F2:"
 f2 a b c "d ja e"

Tulemus:

% bash test.sh
F1:
arg: a
arg: b
arg: c
arg: d ja e
F2:
arg: a
arg: b
arg: c
arg: d
arg: ja
arg: e

Veel üks praktiline näide, järgnev skript käivitab kõik argumendid ükshaaval ettemääratud nodes

  #!/bin/bash
    for osd in "$@"; do
     ssh -q $node argument=$argument bash <<EOF
     $osd
  EOF
  done

Tasub tähele panna ssh käsus $node argument=$argument konstruktsiooni, mis annab muutuja edasi kaugel olevale serverile

Failitestid

Sisekäsk test võimaldab kontrollida faili tüüpi. Näiteks eristame kataloogis olevad lingid, tavalised faili ja kataloogid

Näiteks

if [ -r test.jpg ]
then
   echo "Fail on loetav"
fi

Keerukam näide:

 #!/bin/bash
 for i in `ls -a`
 do
   if test -f $i; then
     echo $i: fail
 elif test -L $i; then
     echo $i: link
 elif test -d $i; then
     echo $i: kataloog
 fi
 done

Kasutatavad võtmed on järgnevad:

  • -d Kas tegu kataloogiga
  • -f Kas tegu failiga
  • -r Kas fail loetav
  • -s Kas fail nullist erineva suurusega
  • -w Kas fail kirjutatav
  • -x Kas fail käivitatav
  • -L Kas tegu lingiga

Käskude käivitamine skripti sees

Aegajalt tuleb ette, et on vaja käivitada mingi käsk ning selle tulemit skripti sees töödelda. Oletame, et soovime saada skriptis käsu: uname -a väjundi

Selleks olemas kolm võimalust, esiteks kasutades eraldajaid

echo `uname -a`

Või

echo $(uname -a)

Käsu tulemi saab kergelt edastada muutujale:

OS=$(uname -a)
echo $OS

Sisestus märgini

Märgini sisestamine võimaldab vältida mitmete järjestikuste käskude echo kasutamist.

cat >> /home/kasutaja/fail <<EOT
esimene rida
teine rida
kolmas rida
EOT

NB! EOT või EOF peab olema rea alguses, kui on taandega, pannakse kuni faili lõpuni kõik stringi sisse.

Toome näite CGI valdkonnast

 #!/bin/bash
 echo -e "Content-type: text/html\n\n"
 a=`df`
 cat <<mark
 <HTML><BODY>
 <p>Tere, masina failisüsteemiga on sellised lood:<p>
 <pre>
 $a
 </pre>
 <pre>
 $(hostname -f)
 $(date)
 </pre>
 </BODY></HTML>
 mark

Kusjuures, kui 'cat <<mark' rida asendada 'cat <<\mark', siis muutujaid ei asendata ega väärtustata.

Funktsioonid

Tihti esinevaid järgnevusi on mõistlik kirjeldada kord funktsioonina ning hiljem kasutada nende poole pöördumiseks seda funktsiooni. Näiteks loome funktsiooni, mis paneb teksti < pre > ja < /pre > märkide vahele.

 #!/bin/sh
 function pre () {
 echo -e "<pre>$1</pre>"
 }
 
 echo -e "Content-type: text/html\n\n<HTML ><BODY>"
 pre "`df`"
 pre "`uptime`"
 echo "</BODY></HTML>"

Funktsioonis kirjeldatu käivitamiseks ei tekitata uut koorikut. Argumente saab kasutada sarnaselt skriptile endale.

Teise näitena küsime kinnitust kasutajalt mingi tegevuse toestamise kohta

 kinnitus(){                                                 
         echo                                                
         echo                                                
         echo -n "$1 (y/n) [n] "                             
         read VASTUS                                         
 
         if [ "$VASTUS" != "y" ]
         then                   
                 exit           
         fi                     
 }

Kasutamine järgnev

kinnitus "installime vajalikud komponendid ?"

Üks shelliskriptis suisa häda vajalik funktsioon oleks kontrollida kas eelmine käsk on väljastanud mingi veateate ehk siis lõpetanud enda tegevuse ebaviisakalt

 kas_korras(){                                    
   RET=$?                                         
   if [ "$RET" != '0' ]                           
   then                                           
     echo                                         
     echo "eelmine käsk väljastas: $RET"        
     exit $RET                                    
   fi                                             
 }

Ning seejärel võib lisada näiteks make käsu järele kontrollfunktskiooni

kas_korras

Võimalus on ka genereerida funktsiooniga paroole:

genpasswd() {
        local l=$1
        [ "$l" == "" ] && l=16
        tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${l} | xargs
}

Ja kasutamiseksd

genpasswd 8

Käsurea parameetrid

Skripti järgi antud parameetrid omistatakse muutujatele $1 $2 $3 jne

Näiteks soovime anda sedasi parameetreid:

$ ./skript eesnimi perenimi vanus

Skript mis need väljastab võiks olla selline

#!/bin/bash
    echo $1
    echo $2
    echo $3

Lock faili kasutamine skriptis

Vahel on vajalik saavutada olukord kus skripti selle töötamise ajal uuesti enam käivitada ei saa, siin tuleb appi lockf abil tekitatav /var/run/skript.lock nimeline fail

skripti idee lihtsalt tuhat sekundit passida

 #!/bin/sh
   if [ -f /var/run/skript.lock ]; then
     echo "eelmine programm ikka veel toimib."
     return
   else
     lockf -t 0 /var/run/skript.lock sleep 1000
   fi

Paneme skripti toimima taustal

# ./skript.sh &
[1] 80562

proovime veelkord startida

# ./skript.sh
eelmine programm ikka veel toimib.

Lockfaili võib muidugi käsu lockf puudumisel tekitada ka lihtsalt mkdir-touch käsuga ja hiljem eemaldada, näiteks

 if [ -f /var/run/$1.lock ]; then
   echo "$1 eelmine mirror ikka veel töötab."
   return
 else
if mkdir /run/$1.lock; then
  rsync  ....
  rmdir /run/$1.lock
fi

Bash ja internet

Bash võimaldab tegeleda ka võrgu socketiga. Viimaste kaudu on võimalik avada võrguühendus mõne pordiga ja vahetada sellega infot.

Socketi asukohaks Linuxis /dev/tcp/SERVER/PORT

Näiteks saadame emaili serveri zoo.tartu.ee 25 pordile HELO käsu

 #avame socketi
 exec 3<>/dev/tcp/zoo.edu.ee/25 
 # saadame HELO käsu
 echo -e "HELO mail.edu.ee" >&3
 # loeme tulemust
 while read line <&3
  do
   echo -n $line >&1
  done
 # saadame QUIT käsu 
 echo -e "QUIT" >&3
 # sulgeme sisendi
 exec 3<&-
 # sulgeme väljundi
 exec 3>&-

Subshelli kasutamine

Sulgude vahel esitatu töötab omaette keskkonnas, mille kohta öeldakse, et ta moodustab subshelli. Nt sobib subshelli abil suunata mitme käsu väljundit koos edasi

 $ (echo tere1; echo tere2) | tr a-z A-Z
 TERE1
 TERE2

Skriptide hingeelust

Shelli skriptidele saadetakse SIGHUP (võibolla veel midagi), kui ühendus katkeb ja selle peale lõpetab skript tavaliselt töö (ja üritab oma lapsed kah ära tappa).

Testimisel piisas, kui SIGHUP kinni püüda ja ignoreerida. Lõpus skript, mis seda demob.

 #!/usr/local/bin/bash$
 $
 trap trhup HUP$
 trhup(){$
   debug "SIGHUP detected.. interesting"$
 }$
 $
 debug(){$
   echo "`date` $1" >> tulemus.txt$
 }$
 $
 debug "ALGUS"$
 echo -n "Katsetuse testimine. Toksi teksti: "$
 read -t 10 VASTUS$
 $
 debug "VASTUS: $VASTUS"$

Sõnastiku kasutamine

Bash v. 4 võimaldab kasutada sõnastikku (ingl. k. associative array). Nt olgu failisüsteemis sellised failid

$ pwd
/home/priit/skriptid
$ find . -type f
./sql/konvert_ee/konvert_20120515.sql
./sql/konvert_ee/konvert_20120515a.sql
./sql/konvert_ee/konvert_20120515b.sql
./sql/konvert_20120520a.sql
./sql/konvert_20120505.sql
./sql/konvert_20120520b.sql
./sql/konvert_20120520.sql

ning eesmärk käivitada, sõltumata sellest, millises kataloogis üks või teine skript asub, nad kronoloogilises järjekorras (kasutades seejuures faili nimes esinevat YYYYMMDD osa).

$ cat jarjesta-ja-kaivita.sh
#!/bin/bash
declare -A sql_kataloog

for i in `cd /home/priit/skriptid; find sql -type f | xargs`; do
  j=`echo $i | sed 's/sql\/konvert_ee\///' | sed 's/sql\/konvert_lv\///' | sed 's/sql\///'`
  sql_kataloog[$j]=$i
done

for key in `export LANG=C; echo ${!sql_kataloog[@]} | tr " " "\n" | sort | tr "\n" " "` ; do
  echo psql ${sql_kataloog[$key]}
done

kus

  • declare -A sql_kataloog - kirjeldab sõnastik tüüpi muutuja
  • find ja xargs abil moodustatakse kataloogiteedega failinimede nimekiri
  • sql_kataloog[$j]=$i - omistatakse sõnastiku võtmele (nt konvert_20120515b.sql) vastav väärtus (nt sql/konvert_ee/konvert_20120515b.sql)
  • export LANG=C abil saab mõjutada järjestust
  • echo ... tr ... sort ... tr abil järjestatakse sõnastik võtmete järjekorras

Kävitamisel vajastatakse

$ ./jarjesta-ja-kaivita.sh 
psql sql/konvert_20120505.sql
psql sql/konvert_ee/konvert_20120515a.sql
psql sql/konvert_ee/konvert_20120515b.sql
psql sql/konvert_ee/konvert_20120515.sql
psql sql/konvert_20120520a.sql
psql sql/konvert_20120520b.sql
psql sql/konvert_20120520.sql

Tingimuse kasutamine programmi väljundkoodiga

If tingimusega saab kasutada lisaks test vms konstruktsioonidele ka programmide väljundkoode (ingl. k. exit code, exit status) arvestades asjaolu, et reeglina on väljundkood programmi töö ilma veata lõppemisel 0 ning igal muul juhul > 0. Nt sobiks sellise skripti abil kustutada mingi rakenduse töö käigus tekkivaid zip faile /tmp kataloogist

for i in $(find /tmp -maxdepth 1 -mtime +2 -type f | xargs); do
  if $(unzip -qt $i 1>/dev/null 2>&1) ; then
    rm $i
  fi
done

Sõnedega töötamine

Sõnest (ingl. k. string) saab eralda osa selliselt, viienda positsiooni järelt lõpuni

$ sone="tuulevaikus"
$ echo ${sone:5}
vaikus

Või expr abil

$ sone="tuulevaikus"
$ echo `expr substr $sone 6 7`
vaikus

Kasulikud lisamaterjalid

Tühikuid sisaldavate failinimedega töötamine

Kui faili või katalooginimedes esinevad tühikud, siis selline konstruktsioon ei tööta

$ for i in *; do du -sk $i; done

aga töötab selline find + while kasutamine

$ find . -type d ! -name . | while read nimi; do du -sk "$nimi"; done
8       ./Teater Vanemuine
8       ./Kala maja
8       ./Supi linn

Skriptisiseste teadete ja veateadetega tegelemine

Bash shelli stdout ja stderr

0 Stdin - kasutatakse info programmi sisestamiseks
1 stdout - kasutatakse info kirjutamiseks ekraanile
2 stderr - kasutatakse error teadete väljutamiseks

Näiteks veateadete faili logimine toimib nii

$ program-name 2> error.log

Nii veatedete kui niisama programmi sisteste teadete faili suunamiseks

$ command-name &>file

Selleks, et suunatada stderr ümber stdouti

$ command-name 2>&1

Programmi väljundi saab saata /dev/nulli lihtsa käsuga > /dev/null kahjuks ei tööta see programmidega, mis väljutavad ohtralt standard error 2 teateid. Selleks tuleb kasutada järgnevat süntaksit

$ command > /dev/null 2>&1

Suvaliste numbrite 0-10 genereerimine

Mitte seotud küll bashi sisemise struktuuri vaid hulga unixi/linuxi käskude koos kasutamisega aga tulem kulub sageli bashis ära.

grep -m1 -ao '[0-9]' /dev/urandom | sed s/0/10/ | head -n1

Mõned kasulikud skriptide näited

Mirrordamise skript Sobilik näiteks avaliku FTP serveri loomiseks.

Skript posti quota kontrollimiseks mboxidel