Expect

From EIK wiki

Sissejuhatus

Expect on Don Libes'i poolt kirjutatud Tcl (Tool Command Language) laiendus, mida kasutatakse erinevate käsurea tegevuste automatiseerimiseks. Põhiliselt kasutatakse expecti käsurealt kasutatavate interaktiivsete rakenduste (ssh, telnet, ftp, sudo jne) automatiseerimiseks. Expect on asendamatu abimees neile, kes tahavad automatiseerida näiteks paroolide muutmist, võrguseadmete haldamist vms tegevust, mis nõuab kasutaja-terminali vahelist suhtlust. Piltlikult võib öelda, et kasutades etteantud skripti expect "räägib" teiste interaktiivsete programmidaga. Järgides skripti expect teab mida programmilt oodata ja mida programmile erinevates olukordades "öelda".

Üldine

Expecti põhiliseks alustalaks on kolm käsku: expect, send ja spawn. Samuti on tihti suureks abiks käsk interact. Expect ootab teatud andmeid protsessilt. Send käsk saadab andmed protsessile. Spawn alustab protsessi. Interact annab juhtimise üle kasutajale.

Järgneva artikli paremaks mõistmiseks peaks olema eelnevalt tuttav Linuxi käsureaga.
Artikli koostamisel on kasutatud expecti versiooniga 5.44.1.15.

Expect interpretaatori käivitamiseks sisesta käsureale:

 expect 

tulemus:

expect1.1>

Send

Send käsk loeb etteantud stringi kui argumenti ja saadab selle protsessile või standard väljundisse.
Näiteks:

send "tere maailm"

Eelneva puhul saadetakse string tere maailm. Kui expect juba suhtleb mõne programmiga siis saadetakse string sellele programmile. Hetkel saadetakse see aga standard väljundisse. Kasutades expect interpretaatorit, näeks tulemus välja selline:

tere maailmexpect1.2>

Send käsk ei muuda string formaati ja seega peale selle esitamist liidetakse otsa expecti prompt. Selleks, et prompt prinditaks uuele reale on vaja lisada stringi lõppu reavahetuse sümbol \n.

send "tere maailm\n"

tere maailm
expect1.2>

Expecti kasutamiseks on otstarbekas kasutada skriptimist, mis on ühtlasi ka levinuim viis expecti rakendamisel.
Selleks loome skriptifaili (nt. nimega test), ja lisame sinna eelmainitud send käsu. Tulemuseks saab skripti käivitada UNIXi käsurealt.

expect test

Mõningase faili muutmisega saab aga skripti käivitada ka ilma expecti lisamata. Selleks lisa faili algusesse expecti asukoht:

#!/usr/bin/expect

ja muuda fail käivitatavaks:

chmod +x test

Nüüd saad skripti käivitada ka käsuga:

./test

Edaspidiste näidete puhul tulebki kasutada expecti käivitamist failist, mitte expect interpretaatorit.

Expect

Expect on vastupidine send'le. Expect ootab vastust programmilt või kasutajalt. Expect võib oodata kindlat stringi või ka osa teadaolevast stringist. Näiteks loome faili "vestlus" alloleva sisuga ja anname talle käivitamise õigused.

#!/usr/bin/expect
expect "tere\n"
send "tere ise ka!\n"

Käivitme skripti

./vestlus

Juhtub see, et programm ootama stringi tere ja reavahetust (\n). Sisestades käsureale tere väljastatakse vastuseks tere ise ka!.

expect_out(0,string) ja expect_out(buffer)

Expect jääb oodatavat stringi ootama seniks kuni see sisetatakse. Kui kasutada eelnevat skripti ja sisestada tere asemel näiteks hello siis expect ei väljasta midagi ja jääb endiselt ootama tere sisestamist. Kui sisestada lõpuks ka oodatav string (tere) siis expect vastab. Enne vastamist aga salvestab expect sobinud märgid muutujasse expect_out(0,string). Sobinud märgid ning lisaks kõik sellele eelnevalt sisestatud märgid salvestatakse muutujasse expect_out(buffer). Eelnevat tehakse iga kord kui expect tuvastab sobinud märgid.

Muudame eelneva skripti vestlus sisu järgnevaks:
#!/usr/bin/expect
expect "tere"
send "sina sisestasid <$expect_out(buffer)>"
send "oodatud string oli <$expect_out(0,string)>"


Nurksulud ei ole iseenesest vajalikud aga teevad väljundi lugemise lihtsamaks - parem on aru saada kus algavad ja lõpevad muutujad. Kui skript käivitada ja sisestada näiteks:

hei, tere

väljastatakse tulemuseks:


sina sisestasid <hei, tere>
oodatud string oli <tere>

Ankrud

Nagu enne mainitud ei ole mittesobituva info leidmine sisendis expecti jaoks probleem. Sisendi jälgmist jätkatakse kuni sobitumiseni. Vahel on vaja aga piirata mittevajalike sümbolite ilmumist sisendisse.

^ kasutatakse juhul kui on vaja sobitumist sisendi alguses. Näiteks ^hei sobitub kui kirjutada sisendisse hein aga ei sobitu sisendiga juhhei

$ kasutatakse juhul kui on vaja sobitumist sisendi lõpus. Näiteks hei$ sobitub sisendiga juhhei kuid mitte hein. Järelikult ^hei$ ei sobitu kummagagi, vaid ainult hei'ga.

Timeout

Vaikimisi ootab expect sobituvat sisendit 10 sekundit. Kui selle aja jooksul sobitumist ei toimu siis expect lõpetab. Loomilikult saab timeouti ka muuta. Selleks on käsk:

set timeout 60

Eelnev määrab timeoudiks 60 sekundit.

Üldiselt peab olema timeout positiivne arv. On aga paar erandit.

0 määrab, et expect ei peaks ootama üldse.

-1 määrab, et excpect peaks ootama igavesti.

Expect-Send paarid

Expect-Send paaride korral kirjeldatakse Expect, mida oodatakse ja selle järele kohe Send mis peaks sobitumisel väljastatama.

Näide 1:

expect "tere" {send "$expect_out(buffer)"}

Näide 1 puhul jääb expect ootama tere sisestamist ja kui see on sisestatud siis väljastab kogu sisestatud teksti mis sellele eelnes ja tere.

Näide 2:

expect "tere" { send "tere ise ka!\n" }\
"hei" { send "hei sullegi!\n" }


Näide 2 puhul oodatakse üheaegselt nii tere kui ka hei sobitumist. Täide viiakse see send, mille ees olev expect esimesena sobitub. Kui ükski expect ei sobitu enne timeout aega siis expect lõpetab selle lause ning jätkab skripti järgmise käsu täitmisega.
Expect koodis ei ole vahet kuidas expect ja send visuaalselt paigutatud on. Põhimõtteliselt võib koodi esitada ka ühe pika reana kuigi eraldi ridadel on expect-send paare parem lugeda. Silmas tuleb pidada vaid seda, et send käsud peavad olema loogeliste sulgude sees. Eelnevas näites kasutatud \ rea lõpus tähendab seda, et käsk jätkub, st. et kui expecte oleks selles näites rohkem olnud siis peab kõigi ridade, peale viimase, lõpus olema \.

Sama näidet saab kujutada ka kujul:

expect {
"tere" { send "tere ise ka!\n" }
"hei" { send "hei sullegi!\n" }
}

Spawn

Spawn käsku kasutatakse protsesside välja kutsumiseks. Spawni esimene argument on protess, mida välja kutsutakse ja järnevad argumendid edastatakse väljakutsutavale protsessile.

spawn telnet 192.168.1.254

Eelnev spawn käsk kutsub välja protsessi telnet. 192.168.1.254 on telneti protsessi argument. Kokkuvõttes tehakse telnet ip-le 192.168.1.254, mis võrdub samaga kui anda terminali aknasse käsk

telnet 192.168.1.254

Kui spawn on protsessi välja kutsunud saab protsessiga manipuleerimiseks anda juhtimise üle expectile.

Näide 1:

Lihtne ruuteri rebootimise skript, mis kutsub välja telnet protsessi ip-le 192.168.1.254. Seejärel logib sisse ja käsurea ilumisel sisestab käsu reload ning kinnituse.

#!/usr/bin/expect
spawn telnet 192.168.1.254
expect "Username*"
send "MinuKasutajanimi\r"
expect "Password*"
send "MinuParool\r"
expect "*#"
send "reload\n"
expect "Are you sure?"
send "yes\r"

Interact

Eelnenud rebootimise näide oli täisautomaatne, st expect tegi kogu töö ilma, et kasutaja oleks saanud sekkuda. Vahel on aga vajalik, et expect annaks juhtimise üle kasutajale. Selleks kasutatakse käsku interact.

Näide 1:
#!/usr/bin/expect
spawn telnet 192.168.1.254
expect "Username*"
send "MinuKasutajanimi\r"
expect "Password*"
send "MinuParool\r"
interact

Selle näite puhul tehakse telnet, logitakse sisse ja seejärel antakse juhtimine üle kasutajale. Kasutaja on nüüd sisseloginud ja võib teha edasised toimingud.

Muutujad

Muutujaid defineeritakse järgmiselt:

set <muutuja> <väärtus>

Näide 1:
Eelneva sisselogimise skripti saab ümber teha kasutades muutujaid järgmiselt:

#!/usr/bin/expect
set kasutajanimi Minukasutajanimi
set parool Minuparool
spawn telnet 192.168.1.254
expect "Username*"
send "$kasutajanimi\r"
expect "Password*"
send "$parool\r"

Samuti on võimalik anda muutujaid kaasa käsurealt. Sellisel juhul kasutatakse muutuja väärtust [lindex $argv X], kus x on argumendi number.

Näide 2:
Teeme sisselogimise näite ümber selliselt, et kasutajanimi ja parool tuleb kaasa anda skripti käivitamisel. Skript käivitatakse käsuga:

 ./skript minukasutajanimi minuparool 

#!/usr/bin/expect
set kasutajanimi [lindex $argv 0]
set parool [lindex $argv 1]
spawn telnet 192.168.1.254
expect "Username*"
send "$kasutajanimi\r"
expect "Password*"
send "$parool\r"

Kasutatud kirjandus

1. http://oreilly.com/catalog/expect/chapter/ch03.html
2. http://expect.sourceforge.net/
3. man expect

Autor

Meelis Kurnikov AK21

Viimati redigeeritud 26.12.2011