TCL/Expect - taśma klejąca programisty!
2008-02-13, Środa 16:50:51 +0100, autor Karol „Zal” Zalewski, licencja LPRCTKC
TCL (Tool Command Language) to proceduralny język skryptowy. Specyficzny - należy dodać. Przeglądanie kodu programów napisanych w TCL rzadko kiedy jest przyjemne - widząc tak wiele nawiasów w kodzie można dostać oczopląsu. Richard Stallman ma jednoznaczne zdanie na temat tego języka. TCL, rok po ukazaniu się listu Stallmana, zyskał jednak mocnego sojusznika - obok Tk pojawił się Expect. Od tej pory w pełni zasługuje na miano "taśmy klejącej" świata programistycznego.
BASH sprawdza się w większości zadań, kiedy to wymagana jest automatyzacja często wykonywanych czynności. Jest tylko jedno "ale" - wystarczy, iż wywoływany program jest interaktywny, a cała sytuacja mocno się komplikuje. W tym to właśnie momencie z pomocą przychodzi nam para TCL/Expect. Idea Expecta jest prosta - program interaktywny zakłada, iż po drugiej stronie ekranu znajduje się człowiek, zatem wystarczy jedynie napisać program, który będzie niezbędnego nam człowieka udawać. Expect umożliwia wykonanie tej operacji w kilku liniach kodu.
#!/usr/bin/expect --
set IP "127.0.0.1"
set PORT 23
# Starting Telnet
spawn telnet $IP $PORT
set ID $spawn_id
Powyżej widzimy początek jednego z najprostszych skryptów wykorzystujących potęgę Expecta oraz program Telnet (występujący chyba we wszystkich przykładach Expecta). Z Expecta wykorzystaliśmy tylko jedną komendę - spawn - oraz zmienną przez Expect używaną - spawn_id. W ten oto sposób uruchomiliśmy program Telnet oraz zapisaliśmy sobie jego unikatowy numer. To ostatnie nie jest konieczne w przypadku pracy z jednym programem, ale w momencie, kiedy potrzebujemy korzystać z kilku programów interaktywnych jest czynnością niezbędną. Powracając do samej komendy spawn - od tego momentu jesteśmy w stanie komunikować się z uruchomionym przez nas programem tak, jakby nasz skrypt był człowiekiem. Służą do tego dwie proste komendy - expect oraz exp_send. Oto i one w akcji:
# Set timeout and human-like delay
set timeout 1
set send_slow {1 0.2}
set LOGIN "user"
# Waiting for login prompt
expect {
-i $ID
-re ".*login:" {
# Username sending
exp_sleep 1
exp_send -s -i $ID -- "$LOGIN\r"
} timeout {
puts "INFO: Can't find login prompt"
return 1
}
}
W powyższym kodzie sporo się dzieje - na samym początku ustawiamy czas oczekiwania na 1 sekundę (zmienna timeout - przyda się później) oraz sprawiamy, iż napisany przez nas program będzie wysyłał tekst do komputera z którym się połączył w sposób "ludzki" (z losowymi opóźnieniami itp.). Później pojawia się oczekiwana przez nas komenda expect (istnieje również jej odpowiednik działający w tle - expect_background). Jak sama nazwa wskazuje - umożliwia ona reakcję naszego skryptu na to, co jest wyświetlane przez wywołany przez komendę spawn program. W tym celu możemy skorzystać zarówno z wymyślonych przez nas ciągów znaków (np. "login", "password", "folder" itp.), jak i wyrażeń regularnych. Na powyższym przykładzie widać, iż autor skryptu przewidział dwa przypadki - reakcję na pojawienie się słowa "login" - bez względu na to, co było wcześniej - oraz na przypadek w którym to po jednej sekundzie oczekiwania (pamiętacie zmienną timeout) nie odnaleziono żadnego pasującego fragmentu tekstu. W pierwszym przypadku, po odczekaniu jednej sekundy (exp_sleep 1), przesyła odpowiedni tekst (w naszym przypadku login) do uruchomionego programu (ważne jest, aby uwzględniać również takie klawisze, jak np. enter - "\r"). W drugim - wyświetla informację na ekranie naszego lokalnego komputera i kończy pracę. Prawda, że proste? Jeżeli nie wiecie, co oznacza -i, -s oraz -re to śpieszę z wyjaśnieniem. Jest to instrukcja dla komend, aby kolejno - komunikowały się z programem wywołanym przez spawn o danym ID, wysyłały znaki w sposób podobny do ludzkiego oraz traktowały następujący po nich ciąg znaków, jako wyrażenie regularne.
Do podanego wyżej kodu należy dopisać jeszcze część odpowiedzialną za podawanie hasła i uzyskamy program do automatyzacji logowania poprzez Telnet. Proste, prawda? "Jakie płyną z tego korzyści?" - zapytacie. Połączenie TCL, Expect oraz Tk posiada szereg zalet - dzięki tej kombinacji jesteśmy w stanie tworzyć graficzny interfejs dla konsolowych programów bez ingerencji w ich kod źródłowy (zarówno pod Linuksem, jak i Windowsem), zautomatyzować czynności wymagające wcześniejszego udziału człowieka (działania opierające się o zastosowanie programów interaktywnych), czy też połączyć funkcjonalność kilku programów (interaktywnych i nieinteraktywnych) w jedną, spójną całość. Wspomniane narzędzia są również przydatne podczas automatyzacji testów oprogramowania/sprzętu, czego przykładem może być stworzony przeze mnie BIOSMan (framework do zdalnego i automatycznego testowania BIOSu komputerów typu SBC). Zastosowań jest wiele, a same narzędzia proste i intuicyjne. Czego chcieć więcej?
Wszystkim zainteresowanym polecam książkę napisaną przez twórcę Expecta Dona Libesa pt. "Exploring Expect", a wydaną przez wydawnictwo O'Reilly. Dokumentacja również okazuje się być niezwykle przydatną.
2008-02-13, Środa 19:02:31 +0100
Warto też pamiętać, że istnieją moduły w językach programowania (np. perlowe Expect.pm, Expect::Simple czy Net::SSH::Expect) działające na podobnej zasadzie (choć niekoniecznie do końca tak samo).
2010-05-22, Sobota 00:38:01 +0200
a czemu nie uzywac basha do tego po c sie meczyc z dziwnymi komendami
(sleep1 ; echo "username";sleep 1; echo "haslo";sleep1;echo "komenda";sleep1)|telnet adresip
2010-05-22, Sobota 11:12:01 +0200
@Anonim: Bo to jest przykład. Można napisać program, który będzie też reagował na zmiany w tym, co wypluwa Telnet. Służy do tego komenda "expect". To, co napisałeś, nie jest w stanie przeanalizować odpowiedzi. Komunikacja jest jednostronna.