Veikimo valdymas Unix skriptuose

Šis straipsnelis pratęsia puslapį Unix komandinės eilutė  
ir jo tęsinį Programavimas Unix apvalkalo aplinkoje  

Įvadiniame straipsnelyje pateikėme pagrindines žinias apie komandinės eilutės interpretatorius bei pačią komandinę eilutę. Tad, kilus neaiškumams (pvz., apie komandinės eilutės interpretatorius), pasitikslinkite tą puslapį. O čia toliau tęsime „pamokas“ apie programavimą Unix aplinkoje.

Visi čia pateikiami pavyzdžiai buvo patikrinti Aix 6.1.8.0 aplinkoje (tačiau, iš esmės, jie turėtų tikti ir Linux). Orientavomes, kad jie tiktų Bourne interpretatoriui (sh), - tačiau, jei pavyzdžiai arba aptariami klausimai skirti kitiems interpretatoriams (paprastai, tai bash), tai atskirai nurodoma.

Vykdymo eigos valdymas

Unix apvalkalai logines reikšmes traktuoja „atvirkščiai“ – jiems 0 – tiesa (true), o ne 0 – melas (false). Tai susiję su tuo, kad Unix komandos gražina 0, jei komanda buvo įvykdyta teisingai, ir klaidos kodą, jei buvo klaidų. Šis įvykdymo kodas gražinamas į apvalkalo aplinkos kintamąjį $?, nors jį naudoti ne visada prisireikia, nes dauguma eigos valdymo sakinių įvykdymo kodą apdoroja betarpiškai.

Operacijos pabičiui gali būt įvykdytos tik naudojant sintaksę su skliaustais - ((išraiška)) , pvz.,

echo $((1&0)) # - reikšmė – 0
((1&0))
echo $? # - pateikiama 1 (false)
echo $((13&7)) # - reikšmė – 5
((13&7))
echo $? # - pateikiama 0 (true)

&& ir || atveju antroji išraiška neskaičiuojama, jei pirmosios pakanka rezultatu nustatyti.

Pvz.,

((1 != 1)) || echo netiesa
((1 != 2)) && echo tiesa

Bus išvesta“

netiesa
tiesa
nes pirmosios išraiškos nepakako išraiškos teisingumui nustatyti ir buvo vykdyta antrosios dalys (su echo).

Sąlyginio vykdymo valdymas

If sakinio formatas:

if išraiška1
then 
  veiksmai1
elif išraiška2
  then
  veiksmai2
else
  veiksmai3
fi

elif ir else dalys nėra privalomos ir gali būti praleistos. elif dalių gali būti kelios. Pvz.

if ((1==1))
then 
   echo 1
elif ((1==2)) 
then 
   echo 2
elif ((3==3))
then
   echo 3
else 
   echo 4
fi

Galima naudoti patikros (()), [[]] ir [] konstruktus...

Operacijos skirstomos į tris grupes: Su teksto eilutėmis;
Su skaičiais;
Su failais:

Su eilutėmis yra šios operacijos:
-z – nulinio ilgio
-n – nenulinio ilgio
arg – nenulinio ilgio
==, !=, <, >

Su skaičiais:
-eq; -ne, -lt; -gt; -le; -ge

Pvz.,

typeset -i x=12
if [[ $x -eq 11 ]] 
  then 
     echo lygu
fi

Su failais:
-b – failas – blokais
-c – failas – simboliais
- d – katalogas
-e – egzistuoja
-f – egzistuoja ir yra įprastinis
-g – egzistuoja ir turi grupės nustatymą
- k – egzistuoja ir su “sticky” požymiu
-L – egzistuoja Ir yra simbolinė nuoroda
-r – egzistuoja ir leidžiama skaityti
-s – egzistuoja ir dydis > 0

Pvz.,

if [ -r data.txt ] && [ -s data.txt ]
 then
   echo "Failas data.txt egzistuoja, leistina jį skaityti ir jo dydis ne nulinis"
fi

if [ -e data.txt ] 
 then
   echo "Failas data.txt egzistuoja"
else
   touch data.txt # sukuriamas tuščias failas
fi

Ciklai

Kita svarbi galimybė yra ciklinės operacijos. Tam tikslui Unix apvalkalas numato for, while ir until konstrukcijas. Visose jose veiksmai apgaubiami do ir done raktiniais žodžiais, pvz.,

typeset -i x=0
while [$x -le 3]
do
   let x++
   echo $x
done

Rezultatas:

1
2
3
4

until ciklas beveik identiškas, tik jo užbaigimo sąlyga priešinga while – until ciklas baigiamas, kai sąlyga yra patenkinama. Pvz. (tam pačiam rezultatui):

typeset -i x=0
until [$x -gt 3]
do
    let x++
    echo $x
done

Tuo tarpu for ciklui reikalingas elementų sąrašas, pvz.,

for item in a b c
do
   echo $item
done

Rezultatas:

a
b
c
set – vienas du trys
for var
do
    echo $var
done

Rezultatas:

vienas
du
trys
suma=0
for item in 1 2 3 4
do
    ((suma += $item))
done
echo “Suma=$suma”

Rezultatas:
suma=10

Ciklo viduj galimi papildomi valdymo sakiniai:
break – nutraukia ciklo vykdymą;
continue – nevykdo likusių cikle veiksmų ir grįžta į ciklo pradžią.

suma=0
for item in 1 2 3 4
do
    ((suma += $item))
    if [ $suma -ge 4 ]; then break; fi
done
echo “Suma=$suma”

Atsakymas:
suma=6

for item in 1 2 3 4 5 6
do 
    if [ $item <4 ] || [ $item == 6 ];  then continue; fi
    echo $item
done

Rezultatas:

4
5

Keli išsišakojimai

case … esac sakinys suteikia galimybę patikrinti reikšmę su keliais šablonais.

Viduje nurodomi šablonai, besibaigiantys “)”, su kuriais tikrinama nurodyta reikšmė. Po šablonų eina sakiniai, vykdomi tuo atveju, jei tenkinamas šablonas. Veiksmų pabaiga žymi du kabliataškiai (;;). Reikšmė su šablonu tikrinama nuosekliai kol bus rastas ją tenkinantis šablonas, - įvykdžius veiksmus po to šablono, tikrinimas nutraukiamas

Pavyzdžiui:

c="bet kas"
case $c in 
tikra) echo "tikras tekstas";;
*) echo "kita: $c";;
esac

Bus patektas toks rezultatas (nes buvo tenkintas šablinas “*”:
kita: bet kas

Pastaba: bash interpretatoriuje šabloną galite pradėti atidarančiu skliaustu „(“, pvz.,

c="tikra"
case $c in 
(tikra) echo "tikras tekstas";;
(*) echo "kita: $c";;
esac

Šįkart atsakymas bus:
tikras tekstas

select sakinys sukuria ciklą panašų į while, kai vartotojui išvedami pasirinkimai. Jis gali nurodyti pasirinkimo numerį, - ir pasirinkimo tekstas priskiriamas nurodytam kintamajam. Ciklas su siūlymu įvesti pasirinkimą kartojamas tol, kol neįvykdoma komanda break.

Pvz.,

select var in Taip Ne Baigti
do
   echo $var
   case $var in 
     Taip) echo "bus kartojama";;
     Ne) echo "bus kartojama";;
     Baigti) echo "bus baigiama"; break;;
     *) echo "Nežinomas pasirinkimas";;
   esac
done

Čia pateikiamas vykdymo protokolas, kai buvo pasirinkti (įvesti) variantai 1, 4 ir 3.

1) Taip
2) Ne
3) Baigti
#? 1
Taip
bus kartojama
#? 4

Nežinomas pasirinkimas #? 3 Baigti bus baigiama

Taigi, pradžioje išvedamas pasirinkimų sąrašas, tada siūloma įvesti pasirinkimą (#?). Įvedus 1, išvedamas pasirinkimo tekstas (Taip) ir „bus kartojama“ (veiksmas prie „Taip“). Tada cikle vėl siūloma įvesti pasirinkimą (#?), o įvedus 4, pasirinkimo teksto nėra (nes nėra atitikmens pasirinkimuose), o išvedamas tekstas „Nežinomas pasirinkimas“ (pagal šablono „*” veiksmus). Vėl ciklas – ir įvedus 3 – pranešama „bus baigiama“ ir ciklas baigia darbą, nes įvykdoma komanda break (esanti veiksmuose prie „Ne“ šablono).

Vartotojo reikšmių įvedimui naudojama komanda read, kuri reikšmes priskiria nurodytiems kintamiesiems, pvz.,
read pavadinimas kiekis

Įvedame:
Stalas 5

Tada $pavadinimas įgauna reikšmę „Stalas“, o $kiekis – „5“.

read -a mas

Įvedame:
aaa bbb ccc

echo ${mas[1]}
pateiks
bbb

Pastaba: jei komandoje read nenurodomas joks kintamasis, reikšmė priskiriama kintamajam REPLY. Pvz.,
read

Įvedus:
aaa bbb ccc

echo $REPLY
pateiks
aaa bbb ccc

Leistini read parametrai:
-p nurodo „kvietimo“ tekstą;
-s nurodo, kad išjungiamas „aidas“ – renkamai „aklai“;
-t nurodo įvedimo laukimo laiką sekundėmis

Pvz.:
read -p Veskite slaptai: -s -t 5

Papildomos bash galimybės

bash interpretatorius suteikia daug papildomų galimybių...

Riestiniai skliaustai { ... } yra vienas „išplėtimų“, įtraukiančių visas kableliais atskirtas išvardintas simbolių eilutes. Sutikęs tokią konstrukciją interpretatorius suformuoja eilutę su visomis galimomis kombinacijomis riestinių skliaustų vietoje

Pvz.,
echo grafikas.{jpg,gif}

Rezultatas:
grafikas.jpg grafikas.gif

o
echo M{a,e}m{a,e}

duos
Mama Mame Mema Meme

${} sintaksė suteikia ir daugiau galimybių. Pvz., tokia forma leidžia atlikti pakeitimus tekste:
${kintamasis/šablonas/} – vienam atitikimui
${kintamasis//šablonas/} – visiems atitikimams

Jei kintamasis yra * arba @ - operacija (šalinimo ar ištraukimo) atliekama visiems poziciniams parametrams.

${kintamasis:pradžia} – eilutės galas pradedant nurodyta pozicija (skaičiuojant nuo 0);
${kintamasis:pradžia:ilgis} – eilutės nurodyto ilgio iškarpa pradedant nurodyta pozicija (skaičiuojant nuo 0);

Ilgis privalo būti teigiamas, tačiau pradžia gali būti ir neigiamas skaičius – tai rodo, kad poslinkis skaičiuojamas nuo eilutės pabaigos

Šis pavyzdys pateikia kintamųjų pavadinimus, tenkinančius nurodytą šabloną:

manoVardas="Vardenis Pavardenis";
manoAdresas="Gatvė Pagatvė 123";
manoUgis=180;
echo ${!mano*}

Rezultatas:
manoAdresas manoUgis manoVardas

Funkcijos

Galimybes labai išplečia funkcijos – pakartotinai panaudojamas kodas, gražinantis reikšmę, ir iškviečiamas iš kitų skriptų. Čia pateikiame vidurkio paskaičiavimo funkciją ir jos iškvietimo pavyzdį. Jame pailiustruojama, kad funkcijos irgi gali dirbti su poziciniais parametrais.

#!/bin/bash

vidurkis () {
viso=0
for var 
do
  ((viso += var))
done
return $((viso/$#))
} # end vidurkis

vidurkis 5 10 6
echo "Vidurkis (5 10 6): $?"

Rezultatas:
Vidurkis (5 10 6): 7

Antrame pavyzdyje pailiustruosime eilutės teksto pakeitimą mažosiomis raidėmis:

#!/bin/bash
LW () {
local ix=0
for v in "$@"
do
   rv[$ix]=$(echo $v | tr '[A-Z]' '[a-z]')
   ((ix += 1))
done

echo "${rv[@]}"
} # end LW

read -p "Šalis: " s
lsalis=$(LW $s)
echo $lsalis

Vykdymo pavyzdys:
Šalis: JAV
Jav

Funkcija gali gražinti tik skaitinę reikšmę. Tad panaudota konstrukcija $(cmd) sintaksė, leidžianti išvedimo srauto reikšmę priskirti kintamajam. O ${rv[@]} konstrukcija yra ekvivalentiška ${rv[*]}, tačiau geriau tvarkosi su tarpais (apie tai žr. >>>>>)

Pavyzdžiuose pateiktos procedūros yra bendrojo pobūdžio, tad jos gali būti surašytos į atskirą failą, o kituose skriptuose tuo failus prijungti source arba . komandomis, pvz.,
source procedures/myproc.bash

Kintamieji, aprašyti funkcijoje su typeset, declare ar local komandomis galioja tik toje procedūroje. Jei tokiu pat vardu yra už funkcijos ribų, jo reikšmės nebus paveiktos.

Naudingi pavyzdžiai

a) kaip patikrinti, ar katalogas yra tuščias?

[ "$(ls -A kat_vardas)" ] && echo "Nėra tuščias" || echo "Tuščias"

Pvz., jei katalogas nėra tuščias, pašalinti jame esančius failus:

work_DIR=darbinis
[ "$(ls -A $work_DIR)" ] && rm $work_DIR/*

b) Kaip suskaičiuoti failus veiksniame kataloge?

Tai galima atlikti komanda:
ls -1 | wc –l

Pirmosios (ls) komandos parametras yra vienetas (1) , reiškiantis, kad reikia failus vardinti po vieną eilutėje.
Antrosios komandos parametras -l (line) nurodo, kad reikia skaičiuoti eilučių pabaigas

c) Kaip paskaičiuoti skripto vykdymo laiką?

Žemiau pateikiamame pavyzdyje vykdymo trukmė paskaičiuojama milisekundėmis (tai nurodo „%3N” date komandoje). Norint didesnio tikslumo (pvz., nanosekundėmis), galima nurodyti „%6N“ (arba tiesiog „%N“).

begin=$(date +"%s%3N")
# [ reikalingi veiksmai ]
end=$(date +"%s%3N")
df=$(($end-$begin))
echo $df

d) JSON duomenų apdorojimas

Visuotinai išplitus JSON formatui, atsiranda poreikis apdoroti šio formato duomenis. Pvz., taip duomenis gražina daugelis servisų – ir reikia patikrinti jų atsakymą.

Pirmiausia, rekomenduoju įsidiegti patogų JSON parserį. Siūlau jq, kurį galima parsisiųsti iš čia. Tai tarsi sed (žr. apie jo naudojimą) analogas JSON duomenims.

Faktiškai, jq - tai filtras: programa paima įėjimo duomenis, juos patikrina su filtru ir išveda filtro atsijotus duomenis. Tad jos formatas:
jq filtras

Paprasčiausias jos panaudojimas (ir naudingas „gražesniam JSON struktūros išvedimui“) – ką gavau, tą gražinau:
jq '.'
pvz.,
cat failas.json | jq '.'

Dabar pereikime prie konkretesnio pavyzdžio. Tarkim kuriam kažkokį objektą ir tam skirtas servisas gražina jo ID, pvz., formatu {"id"="skaitinis_ID"}. Gali nutikti, kad serviso nepavyksta iškviesti, gaunamos kažkokios klaidos ir ID nėra gražinimas. Pateikiame pavyzdį, kuriame patikriname atsakymą ir ištraukiame ID reikšmę.

ats=`echo {"id"="567"}`
IDVal=`echo $ats | jq ".id" | tr -d '"'`
   if [[ $IDVal == +([0-9]) ]] ; then 
      objSukurtas=1; 
   else
      objSukurtas=0; IDVal=0;
   fi
echo $IDVal

Mūsų pavyzdžio rezultatas turi būti:
567

Komentarai pavyzdžiui:
1) tikrinamą reikšmę priskiriam kintamajam ats;
2) IDVal =`echo $ats | jq ".id" | tr -d '"'`
Ši konstrukcija yra skirta ištraukti ID reikšmei ir priskirti ją kintamajam Idval: jq ".id" bando gauti elemento id reikšmę. Jei pavyksta, gražina jo reikšmę, jei ne – gražina null;
tr -d '"'` - kadangi mūsų atveju reikšmė gali būti kabutėse ("567"), ši komanda pašalina gaubiančias kabutes;
3) if [[ $IDVal == +([0-9]) ]] ; then
Tai galutinis patikrinimas, ar reikšmė yra skaitinė (pvz., ne null).

Ankstesnės "Advanced HTML" skyrelio temos:
Tcl kalba
Unix komandinės eilutė
Ateities kalbos?
JavaScript pradmenys
CGI.pm biblioteka: sausainiai
AWK kalba - sena ir nuolat aktuali
MS SQL užklausų rezultatų puslapiavimas
CGI.pm biblioteka: sausainiai
Ką delne mums neša HTML 4.0?
Kaip valdyti piešinių pakrovimo tvarką
Java 8: Optional prieš null
Įlįskite į lankytojų kailį
Dygios JavaScript eilutės
Vaizdi rašysena - VB Script
ASP patarimų liūnas
Ruby on Rails

Sveikųjų skaičių žaidimai
Pelė uodega švystelėjo...
Anotacijos Java kalboje
Viešojo rakto kriptografija
Programavimo kalbų klegesys
Pirmoji programuotoja: Ada Lovelace
Kaip lankytoją nukreipti į kitą WWW puslapį
JavaScript atspindžiai
Kompiuterių ištakos
Vartiklis