我们通过Shell脚本可以实现简单的控制流功能,比如说循环、判断、对本地文件进行一些简单的操作、输入输出写日志等,但都是对于本地机器而言进行的操作。那么当我们需要在一台机器上操作另一台机器时,那该怎么办呢?

expect就是用来实现这种交互功能的工具。

expect介绍

expect是Unix系统中用来进行自动化控制和测试的软件工具,作为Tcl脚本语言的一个扩展,应用在交互式软件中如telnet,ftp,Passwd,fsck,rlogin,tip,ssh等等。expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预。说白了,expect就是一套用来实现自动交互功能的软件,比如上面说的一台机子操作另一台机子。

在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是expect!

expect安装

我用的是Ubuntu发行版,因此执行命令

1
apt-get install expect

就ok啦,CentOS用户可以通过命令 yum install expect 安装

expect相关命令

  • spawn:启动新的进程
  • expect:从进程接收字符串(换行后系统自动打印的字符串)
  • send:用于向进程发送字符串
  • interact:允许用户交互
  • exp_continue: 匹配多个字符串在执行动作后加此命令

expect参数

1
set name [lindex $argv 0]

使用[lindex $argv 0]进行读取参数,使用set将参数添加进变量中

实例详解

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/expect

set timeout 30
set host "10.11.43.90"
set username "root"
set password "123456"

spawn ssh $username@$host
expect "*password*"
{send "$password\r"}
interact
  • set timeout 30:设置超时时间,单位为秒,默认情况下是10秒,也可设为-1,表示一直运作
  • set host/username/password:设置变量
  • spawn ssh $username@$host:启动新的进程,spawn主要的功能是给ssh运行进程加个壳,用来传递交互指令。这里变量的引用和Shell脚本中一致,直接使用$或者使用${}都可以
  • expect “password“:接受的字符串中是否包含’password’这个子串
  • {send “$password\r”}:如果包含,则发送$password过去并回车,即执行交互动作
  • interact:执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。

expect登录其他服务器执行命令

以下内容是一台机器通过ssh登录另一台机器,并在另一台机器上执行命令将以日期作为分区的文件从一个hdfs集群转移至另一个hdfs集群上。参数为一个日期,如果传了参数,则使用用户传的参数,否则使用前一天的日期作为分区标志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#! /usr/bin/expect
# 设置超时时间 等待三十秒
set timeout 30
# 传入参数
set name [lindex $argv 0]

# 设置时间
set date [ clock format [ clock seconds ] -format "%Y%m%d" ]
set secon [ clock seconds ]
set yestoday_secon 0

set yestoday_secon [expr {$secon - 86400} ]
set yestoday [ clock format [ expr {$yestoday_secon} ] -format "%Y%m%d" ]

if {"$name" > 0} {
set time "$name"
} else {
set time "$yestoday"
}

spawn ssh hadoop@10.11.43.90
expect "*assword"
send "123456\r"

expect "hadoop"
send "hadoop distcp hdfs://10.11.56.29/apps/hive/warehouse/location_stay/dt=${time}/* /DOMAIN_B/DISNEY/LOCATION/APP/HY/location_stay/dt=${time}\r"

expect "hadoop"
send "exit\r"

expect eof
exit