wtorek, 14 grudnia 2010

Expect - automatyczne sekwencje

Expect

Ten artykuł należy dopracować zgodnie z zaleceniami edycyjnymi:
poprawić styl – powinien mieć encyklopedyczną formę, sformatować tekst (pomoc: podział na sekcje, tabele).
Po wyeliminowaniu niedoskonałości prosimy usunąć szablon {{Dopracować}} z kodu tego artykułu.
Expect jest to pakiet języka Tcl, który pozwala na interakcję z aplikacjami terminalowymi. Tworzy on własny terminal, który "udaje" użytkownika obsługującego program konsolowy. Choć teoretycznie jest możliwe sterowanie przez Expecta aplikacją w "curses", to najczęściej wykorzystuje się go do sterowania aplikacjami za pomocą prostego odczytywania i wypisywania ciągu znaków - jak np. aplikacje typu powłoka, telnet, czy FTP.
Spis treści [ukryj]
1 Funkcje pakietu
2 Podstawowe komendy
3 Praca z pakietem
4 Autor pakietu
Funkcje pakietu [edytuj]

Kwestia tego, czy Expect może być po prostu pakietem, zależy od tego, jak aktualnie zainstalowano i skonfigurowano Tcl-a na danej maszynie, dlatego też często istnieje on w najprostszej postaci - jako samodzielny interpreter Tcl-a razem z wbudowanym pakietem Expect, dostępny na Uniksie pod poleceniem "expect". Oczywiście istnieje również wersja Expect działająca pod kontrolą systemu Microsoft Windows (oferuje go - odpłatnie - ActiveState, ewentualnie można skorzystać z pakietu Cygwin), ale najpowszechniej Expect jest używany na systemach typu POSIX.
Podstawowe komendy [edytuj]

Podstawowymi komendami Expecta są spawn, send oraz expect. Żeby podać mały przykład, zaprezentuję w jaki sposób możemy zrobić skrypt, który sam zaloguje się na zdalny host (zdalny.host.pl) i odczyta zawartość katalogu domowego, po czym wypisze go na ekranie.
Praca z pakietem [edytuj]

Najpierw uruchamiamy podległą aplikację, czyli telnet:
spawn telnet zdalny.host.pl
Komenda ta zapisze w zmiennej spawn_id identyfikator utworzonego połączenia z podległą aplikacją; będą z niej potem korzystać komendy "send" i "expect". Teraz nasz skrypt musi zaczekać, aż zdalny host napisze "login:".

expect "login:"

Komenda ta powoduje cykliczne odczytywanie z kanału, do którego aplikacja usiłuje zapisywać, myśląc, że pisze na terminal. Komenda kończy się, gdy w odczytanym tekście znajdzie się szukany napis. No więc

send "sektor\r"

Tekst musi się kończyć znakiem \r. W przeciwnym razie mielibyśmy taki efekt, jakby ktoś wpisał nazwę użytkownika i zapomniał nacisnąć enter.
Teraz zaczekamy na zachętę do hasła. Ponieważ zdarza się, że owo "password" będzie się zaczynało wielką literą, a czasem małą, możemy ją pominąć i użyć po prostu:

expect "password:"

I oczywiście podamy mu hasło

send "wr353fg466s\r"

Powinniśmy zaczekać na prompt, ale odczytamy wszystko, co się da. Załóżmy też drobne opóźnienie, żeby nie odczytał za mało. Następnie spróbujemy odczytać sam prompt przez po prostu naciśnięcie enter:

sleep 2
expect *
send \r
expect ">"
Czas zatem na naszą akcję:
send "ls\r"
expect "ls"
expect ">" { set directory $expect_out(buffer) }

Po co to expect "ls"? Cóż, terminal sam wypisze komendę, którą wpisaliśmy (gdyby tego nie zrobił, byłby efekt podobny do prośby o hasło - wpisujemy a nic nie widać). Teraz trzeba jeszcze usunąć ostatnią linijkę, którą był prompt i wypisać to na ekranie:
set directory [join [lrange [split $directory \n] 1 end-1] \n]
puts $directory

Czemu od 1, nie od 0? Ponieważ było expect "ls", więc znak końca linii pozostał w buforze. Następna po tym komenda expect odczytała z $expect_out(buffer) wszystko co było w buforze, aż do ciągu, który podano w instrukcji expect.
Expect posiada jeszcze wiele ciekawych komend pozwalających na bardziej inteligentne interakcje, ale te pokazane tutaj są używane w przeważającej większości. Z expectem trzeba zawsze trochę poeksperymentować (niestety terminale potrafią sprawiać różne niespodzianki), w czym pomaga opcja -d w poleceniu expect - wtedy na standardowe wyjście diagnostyczne wypisywane są kolejne akcje podejmowane przez expecta.
Nie ma też uniwersalnych rad, jak pisać pod expectem - należy po prostu zaprogramować skrypt, żeby tak rozmawiał z aplikacją, jak robiłby to człowiek. Expect posiada nawet specjalne komendy nadające takie ustawienia, że komenda "send" wpisuje tekst z odpowiednimi nieregularnymi opóźnieniami, udając człowieka.

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

 #!/usr/bin/expect

#set timeout 1
set cmd {/ip firewall filter add action=accept src-address=10.0.0.1 chain=forward}


#Komentarz
spawn ssh admin@IP serwera
expect_after eof { exit 0 }


## interact with SSH
#expect "yes/no" { send "yes\r" }
expect "password:" { send "podaj haslo\r" }
sleep 2
expect "admin"
sleep 1
send "$cmd\r"
sleep 1
send "\x1d"
send "\r"