배경

tcl기반의 expect 스크립트는 동작이 완료되지 않는다..

특히 기존 스크립트는 set timeout 5를 하지만, 실제로 timeout이 되지 않아서.. 속을 썩이고 있었다.

proc do_restart {HOST_NAME} {
set timeout 5
        spawn /usr/local/bin/rlogin -l www $HOST_NAME
        expect -re "]# "
        send "cd /home/www\r\n"
        expect -re "]# "
        send "exit\r\n"
        #expect -re "]# "
        interact
}

중간에 문제가 생기면, 마치 이런 상태가 된다. 이 프로세스를 죽이려면, 해당 서버에 들어가서 프로세스를 죽여야 하는 불상사가 생긴다.

Connection closed.
spawn /usr/local/bin/rlogin -l www iza123
Last login: Thu Jun 28 18:23:42 from 9.1.0.2
#################################################################
# This Server is assigned Web Service 
#################################################################
exit
^M

그래서, expect에서 문제가 생겼을 때 expect 자체에서 해결방법은 찾기 어려웟, 대신 timeout을 제대로 주어. Shell로 빠져나오게 하는 방법은 알아냈다.

#!/usr/bin/expect -f
proc do_restart {HOST_NAME} {
spawn /usr/local/bin/rlogin -l www $HOST_NAME
        expect -re "]# " {
                send "cd /home/www\r\n"
        } timeout {
 send_user "### Time out during connecting $HOST_NAME!! ###\r\n"
                exit
        }
        expect -re "]# "
        send "exit\r\n"
        #expect -re "]# "
        interact
}

if {$argc < 1} {
 puts "Usage : restart <project name>"
 exit
}

set NUM 1000
set HOST [lindex $argv 0]

puts "Target Servers $HOST"
for {set i 0} {$i < $NUM} {incr i 1} {
    do_restart $HOST
}

puts "Target Servers $HOST, test completed"

expect에서 문제가 생겼을 때 제대로 쉘로 되돌아올 수 있게 할 수 있다.

Connection closed.
spawn /usr/local/bin/rlogin -l www ida2
Last login: Thu Jun 28 18:30:37 from 1.1.0.1
#################################################################
# This Server is assigned Web Service 
#################################################################
### Time out during connecting ida2!! ###
[dev1:/home1/knight/test]

하지만 expect 쉘 자체 성능은 꽤나 좋지 않습니다. 타임아웃으로 shell이 종료되는 것은 expect가 좋지 않다는 것!!

Perl로 이동

expect 기능이 perl에도 있음을 찾아냈다. CPAN이라는 그룹에서 만든 expect는 tcl기반이 아니었다.

Expect기능이 perl의 모듈로 되어 있어서, e65008서버에 expect.pm (perl module)을 설치하였다. 그리고, 이 expect로 사용성 테스트해보았는데, ssh, rlogin으로 spawn을 만번정도 각각 테스트를 해보았는데, 한번도 fail나지 않았다.

아주 훌륭하지 않은가.

Expect 설치

expect 모듈을 설치한다. make install은 root계정으로 해야한다.

wget expect모듈 (wget http://nchc.dl.sourceforge.net/sourceforge/expectperl/Expect-1.20.tar.gz)
tar zxvf Excpect-1.20.tar.gz
cd Expect-1.20
perl Makefile.PL
make
make install

현재 expect 모듈 위치는 다음과 같다.
perl의 expect 모듈을 이용하여 wrapper를 만든다.
path가 걸려있는 /usr/local/bin에 펄 코드를 위치시켜 놓아, shell script에서 바로 호출할 수 있도록 한다.

#!/usr/bin/perl
use Expect;

my $spawn;
my $timeout = 10;
my $result;
my $command;
my $prompt  = '[\]\$\>\#]\s$'; 

if ($#ARGV < 0) 
{
    printUsage();
    exit 0;
} 

while($#ARGV >= 0) 
{
    my $option = shift(@ARGV);
    if($option eq "-s") 
    {
        $spawn = trim(shift(@ARGV));
    } 
    elsif($option eq "-timeout") 
    {
        $timeout = trim(shift(@ARGV));
    } 
    elsif($option eq "-r") 
    {
        $result = trim(shift(@ARGV));
    } 
    elsif($option eq "-c") 
    {
        $command = trim(shift(@ARGV));
    }
}

if (!$spawn) 
{
    print "spawn is not given.";
    exit -1;
}

#if (!$result) 
#{
#    print "expect value is not given.";
#    exit -1;
#}

if (!$command) 
{
    print "command is not given.";
    exit -1;
}

my $exp = Expect->spawn($spawn)
    or die "Cannot spawn ".$spawn.": $!\n";

my $qr_result;
if ($result eq "") {
   $qr_result = $prompt; 
} else {
   $qr_result = $result;
}
print $qr_result;
$exp->expect($timeout,
             [qr "$qr_result" => \&exec],
             [timeout => \&timeouterr],
);

sub timeouterr
{
    die "###### timeout!! ######\n";
}

sub printUsage 
{
    print "Usage\n";
    print "  expect.pl -s [spawn] -r [expect value] -c [command] \n";
    print "  expect.pl -s [spawn] -r [expect value] -c [command] -timeout [timeout time] \n";
    print "\n";
    print " ex) ./expect.pl -s \"rlogin -l knight iabcde.google.com\" -r \"] \" -c \"restart.sh;exit\" -timeout 10 \n";
    print " * default timeout value is 2 seconds.\n";
}

sub exec
{
    my $lexp = shift;
    $lexp->send("$command\n");
    exp_continue;
}

sub debug 
{
    print $_[0]."\n";
}

sub trim($) 
{
    my $string = shift;
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
}

자 코드를 만들었으니, 제대로 동작이 되는지 확인한다.

perl -c expect.pl

문제가 없으면, 다음의 결과가 나올 것이다.

[a50236:/usr/local/bin]# perl -c expect.pl
expect.pl syntax OK

하지만, 다음의 종속 모듈때문에 링킹 에러가 나는 경우가 존재한다.

[a50236:/data/env/project]# perl -c /usr/local/bin/expect.pl 
Can't locate IO/Pty.pm in @INC (@INC contains: /usr/lib/perl5/5.8.0/i386-linux-thread-multi /usr/lib/perl5/5.8.0 /usr/lib/perl5/site_perl/5.8.0/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.0 /usr/lib/perl5/site_perl /usr/lib/perl5/vendor_perl/5.8.0/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.0 /usr/lib/perl5/vendor_perl /usr/lib/perl5/5.8.0/i386-linux-thread-multi /usr/lib/perl5/5.8.0 .) at /usr/lib/perl5/site_perl/5.8.0/Expect.pm line 22.
BEGIN failed--compilation aborted at /usr/lib/perl5/site_perl/5.8.0/Expect.pm line 22.
Compilation failed in require at /usr/local/bin/expect.pl line 2.
BEGIN failed--compilation aborted at /usr/local/bin/expect.pl line 2.

문제는 IO/Pty.pm 모듈의 종속 모듈인데, 다운받는다.

wget IO-Tty모듈  (wget http://www.cpan.org/modules/by-module/IO/IO-Tty-1.07.tar.gz)

IO/Pty.pm을 설치한다. make install은 root계정으로 해야한다.

wget expect모듈 (wget http://nchc.dl.sourceforge.net/sourceforge/expectperl/Expect-1.20.tar.gz)
tar zxvf Excpect-1.20.tar.gz
cd Expect-1.20
perl Makefile.PL
make
make install

테스트 bash shell script는 다음과 같이 만든다.

[esz18:/home/www/work]# cat expect.sh
#!/bin/bash
eval 'exec expect.pl -s "rlogin -l knight dev1.google.com" -c "exit" -timeout 3'

제약사항

물론 이 코드는 커보러스/rlogin 환경에서만 적절히 사용될 수 있다. 환경에 따라 커스터마이징을 할 필요가 있다.

참조

http://search.cpan.org/~rgiersig/Expect-1.20/Expect.pod
http://iama.rrecktek.com/syslog/installsnare.pl

'perl' 카테고리의 다른 글

bash는 float 변수 연산이 안된다.  (0) 2007.10.21
펄 해쉬 이야기 #2  (0) 2007.10.19
펄 Hash 관련 정보 #1  (0) 2007.10.19
패턴 매칭  (0) 2007.09.23
패턴 매칭  (0) 2007.09.23
Posted by '김용환'
,