OsGate.org Logo

Expect - expect script tcl interattività

Script and coding Script and coding

Date 02.05.2010

Visits 2935

"Expect é un linguaggio di scripting che permette di risolvere i problemi di prompting ed interattività all'interno degli script."

Cenni ed interattività

Per chi non conoscesse questo linguaggio basato su tcl, si può partire dicendo che expect è la soluzione ai problemi di interattività all'interno degli script. Grazie ad esso infatti è possibile interagire con applicazioni che richiedono l'immissione di dati da parte dell'utente, come per esempio ssh o ftp.

Sviluppato da Don Libes, l'ultima versione, 5.44.1, risale al 2006. È rilasciato sotto licenza "Public domain".

Grazie ad expect  e possible di svolgere grandi operazioni di scripting in modo ottimale.

Come detto in precedenza expect garantisce l'interattività negli script, ma senza che noi dobbiamo interagire effettivamente. Questo vuol dire che quando un applicazione chiede direttamente all'utente l'immissione dei dati, per esempio username e password pensando a ssh, expect immetterà questi dati per noi evitandoci il prompt e facendo scorrere un eventuale script che funziona in background senza problemi.

Istruzioni di base

Spawn

Spawn è la prima istruzione che si trova solitamente all'interno di uno script expect, grazie ad essa è possibile creare un nuovo processo in base agli argomenti che gli vengono passati.

Se vogliamo che il nostro script expect interagisca con ftp o ssh dobbiamo prima di tutto fare partire il programma, ed è a questo che serve spawn.

spawn  ftp ftp.foo.bar

Così verra creato un nuovo processo ftp, che si connetterà al server ftp.foo.bar. Il comportamento del programma lanciato non subisce alcuna variazione se lo si lancia tramite spawn.

È possibile lanciare qualsiasi programma basta posporlo dopo l'istruzione spawn.

Expext / Send

Expect e send sono le istruzioni essenziali per iniziare a capire come funziona expect in quanto sono loro che processano ciò che gli viene inviato e ciò che devono restituire al programma. A tutti gli effetti sono questi i due comandi responsabili dell'interattività all'interno di uno script.

Expect

Expect può essere tradotto con "mi aspetto che" e ciò vuol dire che questa istruzione si aspetta in entrata ciò che l'eventuale programma invocato (ssh, ftp, ecc..) ci invia. Facciamo un esempio con ftp.

Al momento di connetterci ad un server ftp digitiamo:

# ftp ftp.foo.bar

Questo comando ci restituirà in seguito:

--Welcome to balbla -
#...
#..
#..
Name (ftp.foo.bar:pippo):

Tutto ciò che dobbiamo passare all'istruzione expect è ciò che si deve aspettare.

Vale a dire:

expect "Name (ftp.foo.bar:pippo):"

Avremmo anche potuto scrivere:

expect "Name" 

oppure:

expect "pippo"

Basta che la stringa che specifichiamo sia presente nell'output del programma.

Se la stringa che indichiamo non è presente nell'output lo script expect eseguirà l'azione successiva.

Send

Send tradotto letteralmente vuol dire "inviare", ed è questo quello che fa all'interno di uno script, invia i dati da noi scelti all'applicazione.

Send lavora a stretto contatto con "expect", questo perchè se quest'ultimo ha un riscontro nella stringa che noi indichiamo, ci permetterà di inviare con send ciò che desideriamo.

Riprendendo l'esempio dell'ftp:

# ftp ftp.foo.bar

--Welcome to balbla -
#...
#..
#..
Name (ftp.foo.bar:pippo):

Ora noi vogliamo inviare al prompt lo username "sysadmin". Considerando che prima di send è sempre meglio anteporre expect:

expect "Name (ftp.foo.bar:pippo):"
send "sysadmin "

Cosi abbiamo inviato il nome utente al prompt di ftp. Da notare l'uso di " " che simula la pressione di enter "Enter" sulla tastiera. Senza " " è come se scrivessimo lo username senza premere invio quindi lo script resterebbe in attesa.

Durante le operazioni di login nei nostri script è bene scrivere questo costrutto:

expect "username" { send "foo
"}
expect "password" { send "bar " }

L'istruzione "send" all'interno delle parentesi graffe viene eseguita solo e soltanto se "expect" ha avuto successo, quindi se non viene trovato "username" o "password" non verrà inviato ne "foo " ne "bar ".

Interact

Interact da il controllo del processo corrente all'utente, cosi da inviare la combinazione di tasti al processo e inoltre permette di far ritornare al processo il suo stderr e stdout.

Vediamo un esempio dello script seguente senza "interact":

#!/usr/bin/expect -f

spawn ssh 192.168.1.98

expect "foo@192.168.1.98's password:"
send "passsh "
expect "foo@bar:~$"
send "free "
expect "total used free shared buffers cached"
send "ls "
send "exit "

L'output di questo script è il seguente:

bash-4.1$ ./ssh.expect
spawn ssh 192.168.1.98
foo@192.168.1.98's password:
Last login: Sun May 1 12:39:01 2010 from 192.168.1.33
Linux 2.6.31.
foo@bar:~$ free
total used free shared buffers cached
Mem: 1552760 1450076 102684 0 72916 1046304
-/+ buffers/cache: 330856 1221904
Swap: 3020180 22628 2997552
foo@bar:~$ bash-4.1$

Il risultato non è conforme a quanto ci aspettavamo e possiamo notare che dei comandi "ls" ed "exit" non viene mostrato l'output.

Se inseriamo "interact" alla fine dell'ultima istruzione, quindi nel nostro caso dopo "send 'exit '" l'ouput è corretto:

bash-4.1$ ./ssh.expect
spawn ssh 192.168.1.98
foo@192.168.1.1's password:
Last login: Sun May 1 12:47:51 2010 from 192.168.1.33
Linux 2.6.31.
foo@bar:~$ free
total used free shared buffers cached
Mem: 1552760 1471536 81224 0 74612 1065428
-/+ buffers/cache: 331496 1221264
Swap: 3020180 22628 2997552
foo@bar:~$: ls
Delphi 2A.zip
Delphi 3A.zip
foo@bar:~$: exit
logout
Connection to 192.168.1.98 closed.
bash-4.1$

Interponiamo "interact" per esempio dopo che abbiamo effettuato il login:

#!/usr/bin/expect -f

spawn ssh 192.168.1.98

expect "foo@192.168.1.98's password:"
send "passsh "
expect "foo@bar:~$"
interact

TCL

Essendo basato su tcl, expect è in grado di interpretarne le istruzioni in maniera del tutto normale.

Possiamo quindi munire il nostro script di molteplici funzioni garantiteci da tcl, quindi cicli istruzioni condizionali come if e altri comandi tcl sono ammessi.

Vediamo alcune delle espressioni base:

set variable_name value

Setta la variabile variable_name con il valore value

puts stringa

Stampa stringa, con un carattere newline che viene aggiunto automaticamente alla fine della stringa.

if, elseif, else

if {$var == 1} {
puts "var 1 ok"
} elseif {$var == 2} {
puts "var 2 ok"
} else {
puts "var isn't ok"
}

for

for {set i 0} {$i < 5} {incr i} {
puts "i == $i"
}

apertura di un file

set handle [open file.txt]

salvataggio contenuto file in una variabile

set content [read $handle]

È consigliato uno sguardo al manuale di tcl => http://www.tcl.tk/man/tcl8.5/TclCmd/contents.htm

Esempi vari

SSH

#!/usr/bin/expect -f
set timeout 1
spawn ssh 192.168.1.10

expect -exact "foo@192.168.1.98's password:" {send "bar "}
expect "foo@bar:~$"

...Istruzioni a vostro piacimento...

send "exit "
interact

Da notare "set timeout 1", questa speciale variabile imposta il timeout in secondi delle azioni all'interno dello script, nel nostro caso lo script aspetterà 1 secondo prima di eseguirà la successiva azione.

"-exact" si aspetta che ciò che gli viene passato sia esattamente uguale a quello che viene specificato tra gli apici:

expect -exact "ciao"

Si aspetta esattamente la parola "ciao" e non "ci" o "cia".

FTP

#!/usr/bin/expect -f
set timeout 1
spawn ftp ftp.foo.bar

expect "Name (ftp.foo.bar:foo):" {send "admin "}
expect "Password:" {send "ftpuser "}
expect "ftp>"

...Istruzioni a vostro piacimento...

send "exit "
interact