Controlando los Ecos

En el ejemplo anterior (sección 8.9.7) utilizamos el módulo Term::ReadKey para evitar el eco del password en pantalla. Es posible lograr el mismo efecto usando las funcionalidades proveídas por Expect. Sigue la porción de código que cambia:
lhp@nereida:~/Lperl/src/expect/tutorial$ cat -n etsii2
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use Expect;
 4
 5  # $Expect::Debug=2;
 6   $Expect::Exp_Internal=1;
 7
 8  my $RSH='/usr/bin/ssh';
 9  my $host_to_login_to='username@remote_machine';
10  my $machine='tomillo';
11
12  # Get the password.
13  print "Enter password: ";
14  my $stdin=Expect->exp_init(\*STDIN);
15  $stdin->exp_stty('-echo'); # Now turn off echoing
16  my ($match_num,$error,$match,$before,$after)=$stdin->expect(undef,"\n");
17  my $password = $before;
18  $stdin->exp_stty('echo'); # Turn echo back on
19  print "\n"; # print that newline that wasn't echoed
20
21  my $rsh=Expect->spawn($RSH,$host_to_login_to);
22
23  # Look for a password prompt in the bastion
24  $rsh->expect(30,'-re','word:\s$')||(die"Never got password prompt\n");
25  print $rsh "$password\r";
..  ..... ....  ............. # Same as in the former example
43  $rsh->interact();
La llamada de la línea 14 retorna una referencia al objeto Expect creado. El constructor tiene dos formas de llamada:
Expect->exp_init(\*FILEHANDLE) 
Expect->init(\*FILEHANDLE)
Una vez creado es posible llamar al método exp_stty el cuál tiene la sintáxis:
$object->stty(qw(mode1 mode2...))
que establece los modos de la terminal virtual creada. En este caso desactivamos el eco a pantalla de los caracteres de entrada. A continuación llamamos a expect en un contexto de lista.
my ($match_num,$error,$match,$before,$after)=$stdin->expect(undef,"\n");

El significado de los elementos retornados es como sigue:

  1. La variable $match_num contiene el mismo valor que se retorna en un contexto escalar: la posición del patrón que ha casado dentro de la lista de patrones que se hayan pasado o bien undef si no hubo matching. En todos los ejemplos que hemos puesto sólo se ha escrito un patrón, pero expect admite que se le pasen varios.
  2. La variable $error contiene un número seguido de una cadena que nos comunica la naturaleza del error. Por ejemplo: 1:TIMEOUT.
  3. Las variables $match, $before y $after contienen las cadenas que han casado, la cadena que precede a esta y la que va después de aquella.
Al hacer que el primer argumento de la llamada a expect en la línea 16 sea undef estamos haciendo que espere tiempo ilimitado8.1 hasta que se encuentre el patrón \n.

Casiano Rodríguez León
2010-03-22
" BORDER="0" SRC="ggoogle.gif" ALT="google">etsiiullpcgull
Sig: Sugerencias Sup: Expect Ant: Control de la Terminal
Casiano Rodríguez León
2010-04-20
="30" ALIGN="BOTTOM" BORDER="0" SRC="gv.jpeg" ALT="ps">modulosperlmonksperldocgoogleetsiiullpcgull
Sig: Práctica: Gestor de Colas Sup: Práctica: Túneles Inversos Ant: Controlando los Ecos
Casiano Rodríguez León
2010-05-05
.perlcritic.org/">perlcriticpbpblogsgoogle code project hosting
Sig: Práctica: Construyendo una Aplicación Sup: Práctica: Túneles Inversos Ant: Práctica: Clave Pública y
Casiano Rodríguez León
2011-02-21
dríguez León
2011-02-21 HTML> sta, 2 si casó con Salir.?\s+, etc. Si ninguno casa se retorna falso.

Una segunda forma de llamada a expect puede verse en las líneas 43 y 60. Estas dos llamadas siguen el formato:

$exp->expect($timeout,
  [ qr/pattern1/i, sub { my $self = shift; .... exp_continue; }],
  [ qr/pattern2/i, sub { my $self = shift; .... exp_continue; }],
  ...
  $prompt
);
En este caso expect establece un bucle. Si se casa con pattern1 se ejecuta la subrutina apuntada por la referencia asociada. Si esta termina en exp_continue se sigue en expect. En ese caso el cronómetro se arranca de nuevo y el tiempo se empieza a contar desde cero. Si se casa con pattern2 se ejecuta la segunda subrutina, etc. Si se casa con $prompt o se sobrepasa el límite $timeout se sale del lazo.

Por ejemplo, en la línea 58:

58  $ftp->expect(
59    $deadline,
60    [qr'Uploading +\S+', \&upload_handler],
61    $ftp_prompt
62  );

Después del put, el mensaje Uploading file sale por cada fichero que se transfiere. Eso significa que el manejador upload_handler se estará ejecutando hasta que aparezca de nuevo el prompt o bien se supere el tiempo limite establecido.

El manejador upload_handler (líneas 29-38) obtiene mediante la llamada al método match la cadena que casó con la expresión regular que ha triunfado Uploading +\S+. El método clear_accum limpia el acumulador del objeto, de manera que la próxima llamada a expect sólo verá datos nuevos. Mediante la expresión regular de la línea 34 se detecta el nombre del fichero implicado y se muestra por pantalla.

Para cerrar la sesión (línea 70) llamamos al método hard_close . Una alternativa a este es usar soft_close o hacer una llamada a expect esperando por 'eof'. Sin embargo esta alternativa toma mas tiempo. En cualquier caso es importante asegurarnos que cerramos adecuadamente y no dejamos procesos consumiendo recursos del sistema.

Al ejecutar el programa anterior obtenemos:

lhp@nereida:~/Lperl/src/expect/tutorial$ sftp_put
Successfully connected to username@ftp.domain.ull.es
Changing directory to remote_dir
Executing put *
..........      ..........      ..........      ..........
images.aux.gz   images.bbl      images.bbl.gz   images.idx
images.idx.gz   images.log      images.log.gz   images.out
images.pl       images.pl.gz    images.tex      images.tex.gz
img1.old        img1.png        img10.old       img10.png
img10.png.gz    img100.old      img100.png      img101.old
img101.png      img102.old      img102.png      img103.old
..........      ..........      ..........      ..........
node95.html.gz  node96.html     node96.html.gz  node97.html
node97.html.gz  node98.html     node98.html.gz  node99.html
node99.html.gz  pause2.png      perl_errata_form.html   perlexamples.css
..........      ..........      ..........      ..........
After put *
Veamos una segunda ejecución a otra máquina que no solicita clave (conexión via ssh usando clave pública-clave privada):
lhp@nereida:~/Lperl/src/expect/tutorial$ sftp_put europa '.' '.' etsii
Successfully connected to europa
Changing directory to .
Executing put etsii
 ...
etsii
After put etsii

Ejercicio 8.9.1   ¿Cómo es que funciona si la máquina no solicita el password? ¿Qué esta ocurriendo en la llamada a expect de las líneas 43-47?

La variable $Expect::Log_Stdout (línea 17) controla la salida de los mensajes de log por STDOUT. Normalmente vale 1. Tiene asociado un método get/set $object->log_stdout(0|1|undef) (alias $object->log_user(0|1|undef)). Cuando el método se llama sin parámetros retorna los valores actuales. Al poner la variable a cero

$Expect::Log_Stdout = 0;
desactivamos globalmente la salida.

Casiano Rodríguez León
2011-06-03