Salve gente.
Settimana un po’ faticosa, quindi mi faccio vivo solo ora.
Il problema
Ma veniamo al sodo: una delle cose più noiose dell’amministrare svariate macchine è spostare file, smandrupparli, …
In particolare, in ambiente Unix (ma non solo…) la comodità e praticità di usare strumenti come OpenSSH (server e client) per accedere e gestire più nodi si scontra spesso con la noia di doversi loggare su tutte le macchine.
La soluzione “canonica”
Una soluzione è certamente rappresentata dal ricorso al programma Ssh-agent di OpenSSH, che permette di stabilire connessioni SSH senza dover digitare la password (opportunamente configurato, ci penserà lui al vostro posto).
Expect!
Un’altra alternativa, certamente grezza ma altrettanto efficace è usare expect, un eccellente programma che vi permette di rendere non-interattive (”batch“) delle sessioni altrimenti interattive.
Sostanzialmente vi permette di pianificare, sotto forma di script, una serie di “a-domanda-X-rispondi-Y-e-proseguiamo-con-la-prossima“.
Per capirci: quando effettuate una connessione SSH ad una macchina vi troverete la fatidica richiesta di inserimento password.
Voi potete fare in modo che expect attenda (da cui il nome) esattamente quella domanda e quando la riceve invii la password al posto vostro.
Un esempio concreto
Attenzione: quanto riportato di seguito ha solo una valore “didattico” e non mi assumo alcuna responsabilità dell’uso che ne farete. Per rendere le cose più semplici ho volutamente optato per tralasciare dettagli importanti “di sicurezza”. In particolare vedrete la password quando la digiterete (una e una sola volta) ed essa verrà comunicata in chiaro agli script. Non è una soluzione “sicura” ma lo scopo di questo post è solo quello di mostrarvi expect in azione.
Supponiamo di avere la necessità di trasferire un file (prova.txt) su diverse macchine, tutte con la stessa combinazione utente/password (come detto, solo per finalità didattiche) e di non voler digitare la password per ogni connessione/trasferimento via SSH.
In altre parole vogliamo usare expect per automatizzare i trasferimenti di file via SSH.
Iniziamo salvando gli IP, uno per riga, in un file (nel nostro caso lo chiameremo ip.txt).
Poi potremmo pensare di creare degli scrippettini come questi:
- uno script di shell “involucro”, chiamato ad esempio copia_multipla.sh, legge il file riga per riga (cioè un IP per volta) e richiama opportunamente i due seguenti scrippettini expect. La password di connessione SSH viene richiesta da questo script e passata ai successivi (non scrivetela mai dentro uno script!);
- il primo scrippettino expect, testa, tenta di collegarsi all’IP-riga e risponderà “yes” all’eventuale domanda posta da SSH quando ci si connette per la prima volta ad un IP/macchina. Lo script ha un timeout di pochi secondi visto che SSH pone la domanda solo la prima volta che ci si connette a quello specifico nodo (quindi risulta essere inutile se l’IP è già “conosciuto” da SSH);
- il secondo e ultimo scrippettino expect, testa, copierà il file su quel nodo, inserendo quando richiesto la password fornita da copia_multipla.sh.
Gli script
Il file copia_multipla.sh (script di shell Bash):
#! /bin/bash echo -n "Utente? " read USER stty -echo echo -n "Password? " read PASSWORD stty echo echo -n "File da copiare? " read FPATH echo -n "Destinazione? " read DPATH # legge il file con gli IP, una riga-IP alla volta... while read IP do echo "NODO: $IP"; ./testa $USER $PASSWORD $IP ./copia $USER $PASSWORD $IP $FPATH $DPATH echo " "; echo " "; done < ip.txt
Il file testa (script expect):
#!/usr/bin/expect -f set user [lrange $argv 0 0] set password [lrange $argv 1 1] set ipaddr [lrange $argv 2 2] # timeout di 5 secondi: se il nodo risponde # ma la sua richiesta non è quella di digitare "yes/no" # possiamo uscire perchè l'IP è già noto a SSH... set timeout 5 # connessione SSH all'host di destinazione... spawn ssh $user@$ipaddr ls # attendiamo la richiesta di primo accesso al nodo via SSH... expect "(yes/no)?*" # inviamo "yes"... send -- "yes\r" # mandiamo un altro ritorno a capo per essere sicuri # di aver concluso e chiudiamo la connessione send -- "\r" expect eof
Il file copia (script expect):
#!/usr/bin/expect -f set user [lrange $argv 0 0] set password [lrange $argv 1 1] set ipaddr [lrange $argv 2 2] set fpath [lrange $argv 3 3] set dpath [lrange $argv 4 4] # timeout infinito (-1): potete regolarvi sulla base # della dimensionedei file da trasferire... set timeout -1 # connessione SSH all'host di destinazione per trasferire il file... spawn scp $fpath $user@$ipaddr:$dpath # attendiamo la richiesta di password... expect "*?assword:*" # inviamo la password... send -- "$password\r" # mandiamo un altro ritorno a capo per essere sicuri # di aver concluso e chiudiamo la connessione send -- "\r
Eseguiamo!
Dopo aver impostato i permessi di esecuzione agli script (chmod +x copia_multipla.sh testa copia) vi basterà eseguire ./copia_multipla.sh.
Lo script vi chiederà username, password, file da trasferire e directory di destinazione dopodichè pensarà lui a copiare quel file sui nodi contenuti nel file ip.txt.
Conclusioni
Expect è davvero potente. In passato lo avevo usato persino per testare server di posta simulando sessioni SMTP via telnet (sì, expect è davvero sublime).
Spero che possa esservi d’aiuto!
Per maggiori info, vi consiglio di dare un’occhiata al sito, sezione apposita (Bash Shell Scripting Directory For Linux / UNIX) nonchè al post specifico da cui ho scopiazz… ehm “tratto ispirazione” inizialmente. ^^’
Ciau. ^^
Attenzione: se copiate il codice, controllate che gli apicetti/apostrofi (’) e le virgolette (”) siano “diritti” e non “obliqui”: non sono la stessa cosa e hanno significati diversi! (WordPress tende a cambiarli, presumo per ragioni di sicurezza)
Edit: quando viene richiesta la password, essa non appare. Tuttavia viene comunicata in chiaro agli script!

