Linux expect命令详解
- 前言
- 问题场景
- 命令介绍
- 1. send命令
- 2. expect命令
- (1)基础知识
- (2)模式-动作
- 3. spawn命令
- 4.interact命令
- 解决最开始的问题
- expect -f 参数?
- 脚本示例
前言
? expect是一种脚本语言,它能够代替人工实现与终端的交互,开发人员不必再守候在电脑旁边输入密码,或是根据系统的输出再运行相应的命令。
? 借助expect,可以将交互过程写在一个脚本上,使之自动化完成所需要的交互操作。形象的说,像ssh登录,ftp登录等都符合交互的定义。
1.判断当前系统是否安装expect环境:which expect,一般是安装在/usr/bin/expect目录下
2.没有则进行安装:yum install expect -y进行安装
3.使用场景
- shell脚本实现ssh自动登录远程服务器
#!/usr/bin/expect
spawn ssh 登录用户名@目标主机
expect "*password:"
send "密码\r"
expect "*#"
interact
下文我们首先提出一个问题,然后介绍基础知四个命令,最后提出解决方法。
问题场景
如何从A机器ssh登录到B机器上,然后执行B机器上的命令?如何使之自动化完成?
命令介绍
Expect中最关键的四个命令是send,expect,spawn,interact
spawn:交互程序开始后面跟命令或者指定程序(在壳内启动这个进程)
expect:获取匹配信息匹配成功则执行expect后面的程序动作(检测由壳内进程发出的特定交互指令反馈字符串后向下执行)
send:用于向进程发送字符串(从壳外向壳内进程发送一条字符串,换行符为确认结束)
interact:允许用户交互
其它命令
exp_continue 在expect中多次匹配就需要用到
send_user 用来打印输出 相当于shell中的echo
exit 退出expect脚本
eof expect执行结束 退出
set 定义变量
puts 输出变量
set timeout 设置超时时间
1. send命令
send命令接收一个字符串参数,并将该参数发送到进程(想象给目标进程加了一层壳,从壳外面send密码到壳内的进程,这个过程类似模拟人类输入密码)。
expect1.1> send "hello world\n"
hello world
2. expect命令
(1)基础知识
expect命令和send命令正好相反,expect通常是用来等待一个进程的反馈。expect可以接收一个字符串参数,也可以接收正则表达式参数。和上文的send命令结合,现在可以看一个最简单的交互式的例子:
#!/usr/bin/expect
expect "hi\n"
send "hello there!\n"
这两行代码的意思是:从标准输入中等到hi和换行键后,向标准输出输出hello there! 。
tips: $expect_out(buffer)存储了所有对expect的输入,<$expect_out(0,string)>存储了匹配到expect参数的输入。
比如如下程序:
#!/usr/bin/expect
expect "hi\n"
send "you typed $expect_out(buffer)"
send "but I only expected $expect_out(0,string)"
当在标准输入中输入时
test
hi
运行结果如下
you typed: test
hi
I only expected: hi
(2)模式-动作
expect最常用的语法是来自tcl语言的模式-动作。这种语法极其灵活,下面就各种语法分别说明。
单一分支模式语法:
expect "hi" {send "You said hi"}
壳内进程发出的反馈字符串被壳外的expect匹配到hi字符串后,会输出"you said hi"
多分支模式语法:
#!/usr/bin/expect
expect "hi" { send "You said hi\n" } "hello" { send "Hello yourself\n" } "bye" { send "That was unexpected\n" }
匹配到hi,hello,bye任意一个字符串时,执行相应的输出。等同于如下写法:
#!/usr/bin/expect
expect {
"hi" { send "You said hi\n"}
"hello" { send "Hello yourself\n"}
"bye" { send "That was unexpected\n"}
}
3. spawn命令
上文的案例都是和标准输入输出进行交互,但是我们跟希望他可以和某一个进程进行交互。spawm命令就是用来启动新的进程的。spawn后的send和expect命令都是和spawn打开的进程进行交互的。结合上文的send和expect命令看一下更复杂的程序。
#!/usr/bin/expect
set timeout -1
spawn ftp ftp.test.com //打开新的进程,该进程用户连接远程ftp服务器
expect "Name" //进程返回Name时
send "user\r" //向进程输入anonymous\r
expect "Password:" //进程返回Password:时
send "123456\r" //向进程输入don@libes.com\r
expect "ftp> " //进程返回ftp>时
send "binary\r" //向进程输入binary\r
expect "ftp> " //进程返回ftp>时
send "get test.tar.gz\r" //向进程输入get test.tar.gz\r
这段代码的作用是登录到ftp服务器ftp ftp.uu.net上,并以二进制的方式下载服务器上的文件test.tar.gz。
4.interact命令
到现在为止,已经可以结合spawn、expect、send自动化的完成很多任务了。但是,如何让人在适当的时候干预这个过程呢。比如下载完ftp文件时,仍然可以停留在ftp命令行状态,以便手动的执行后续命令。interact可以达到这些目的。下面的案例在自动登录ftp后,允许用户交互。
#!/usr/bin/expect
spawn ftp ftp.test.com
expect "Name"
send "user\r"
expect "Password:"
send "123456\r"
interact
解决最开始的问题
如何从A机器上ssh登录到B机器上,然后执行B机器上的命令?如何使之自动化完成?
下面一段脚本实现了从机器A登录到机器B,然后执行机器B上的pwd命令,并停留在B机器上,等待用户交互。具体含义请参考上文。执行方法 。./test.sh 用户名 主机地址 密码
#!/usr/bin/expect -f
# 设置超时
set timeout -1
# 壳内启动ssh进程,取到命令行传递的第一个参数作为用户名,取出命令行的第二个参数作为主机地址
spawn ssh [lindex $argv 0]@[lindex $argv 1]
# 取出命令行传递的第三个参数作为密码
expect "*password:" { send "[lindex $argv 2]\r" }
expect "$*" { send "pwd\r" }
interact
expect -f 参数?
expect脚本的开头一般都写/usr/bin/expect -f,这个-f选项有什么作用呢?
比如如下脚本
#!/usr/bin/expect -f
# 定义变量i,默认值为0,将参数总个数$argc赋值给i
for {set i 0} {$i < $argc} {incr i} {
# 输出 每一个参数
puts "arg $i: [lindex $argv $i]"
}
运行./test.sh -c "puts foo" hehe bar输出如下
(此处其实没有搞明白foo是为什么会输出,推测可能是将puts foo解释为输出foo字符串这个命令了,expect中的puts类似bash中的echo)
foo
arg 0: hehe
arg 1: bar
如果改成#!/usr/bin/expect,则输出如下(expect将命令行传递的内容以空格符为分隔全部理解为参数了)
arg 0: -c
arg 1: puts foo
arg 2: hehe
arg 3: bar
脚本示例
- ssh远程登录主机执行命令,在shell脚本中执行expect命令
#!/bin/bash
passwd='登录密码'
/usr/bin/expect <
- expect执行多条命令
#!/usr/bin/expect -f
set timeout 10
spawn sudo su - root
expect "*password*"
send "登录密码\r"
expect "#*"
send "ls\r"
expect "#*"
send "df -Th\r"
send "exit\r"
expect eof
- 创建ssh key,将id_rsa和id_rsa.pub文件分发到各台主机上面。
# 1.创建主机配置文件
[root@localhost script]# cat host
192.168.1.10 root 123456
192.168.1.20 root 123456
192.168.1.30 root 123456
[root@localhost script]# ls
copykey.sh hosts
# 2.编写copykey.sh脚本,自动生成密钥并分发key.
[root@localhost script]# vim copykey.sh
#!/bin/bash
# 判断id_rsa密钥文件是否存在
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
else
echo "id_rsa has created ..."
fi
#分发到各个节点,这里分发到host文件中的主机中.
while read line
do
user=`echo $line | cut -d " " -f 2`
ip=`echo $line | cut -d " " -f 1`
passwd=`echo $line | cut -d " " -f 3`
expect <
- shell调用expect执行多行命令
#!/bin/bash
ip=$1
user=$2
password=$3
expect <
参考: