Shell Expect

------ ## 一:Expect ### 1. Expect 介绍 ​ 通过 Shell 可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候可能会需要实现和交互程序如telnet服务器等进行交互的功能。而Expect就使用来实现这种功能的工具。 ​ Expect 是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。 ​ Expect 是一个用来实现自动交互功能的软件套件 (Expect [is a] software suite for automating interactive tools)。使用它系统管理员 的可以创建脚本用来实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些输入都需要手工输入进行的。 Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。 ​ Expect 需要 Tcl 编程语言的支持,要在系统上运行 Expect 必须首先安装Tcl。 ### 2. Expect 工作原理 - Expect 的工作方式象一个通用化脚本工具。用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。 - 脚本由一系列 expect-send 对组成:expect 等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。解决人机交互的问题。 ### 3. Expect 安装 ```bash [root@wxin ~]# yum -y install expect tcl tclx tcl-devel ``` ### 4. Expect 命令 Expect 的核心是 spawn、expect、send、set。 - spawn:调用要执行的命令 - expect:监听交互输出 - send:进行交互输入 - set:设置变量值 - interact:交互完后,将控制权交给控制台。 - expect eof:与 spawn 对应,表示捕捉终端输出信息终止,类似 if...endif - settimeout -1:设置 expect 永不超时 - settimeout 300:如果 300 后没有捕捉到 expect 的监听的内容,那么就退出 ### 5. Expect 语法 Expect 使用的是 tcl 语法,语法结构如下 #### 1. 命令之后是参数,相互用空格间隔 ```shell 命令 参数 [参数] ``` #### 2. 使用变量 ```bash [root@wxin ~]# set var 1000 # 定义变量
set var [lindex $argv 0] #将 var = argv[0]
$foo ``` #### 3. 嵌套命令 将一个命令的输出,作为另一个命令的输入参数 ```shell 命令 [命令 参数] ``` #### 4. 双引号 将词组标记为一个参数,双引号内`$`符号有效 ```shell 命令 "hello world $foo" ``` #### 5. 大括号 将词组标记为一个参数,但大括号内无法扩展变量 ```shell 命令 {hello world} ``` #### 6. 反斜线,转义 ### 6.示例解决`ssh`登录验证免交互 #### 1.命令选项说明 - spawn expect 内部命令,启动一个shell程序。 - expect 期望哪些内容 - yes/no 就send发送 yes ,\r 表示回车 - password 就 send 发送 centos - exp_continue,跳过循环,就继续下一条语句。 - interact 允许用户交互 #### 2. Expect 实现免交互公钥推送 **安装和生成公钥** ```shell [root@client ~]# vim key.sh #!/bin/bash # 创建一个IP地址文件。 >ip.txt # 检测expect是否安装,检测公钥是否创建。 rpm -q expect &> /dev/null if [ $? -ne 0 ]; then yum install -y expect tcl tclx tcl-devel fi if [ ! -f ~/.ssh/id_rsa ]; then ssh-keygen -P "" -f ~/.ssh/id_rsa fi #使用for循环ping测试主机是否在线。之前插入安装和准备秘钥。 [root@client ~]# bash key.sh Generating public/private rsa key pair. Created directory '/root/.ssh'. Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:SjHCxgoBPmduW2ia/FC/gYorRqsZG5JUui4F5IB7EK4 root@client The key's randomart image is: +---[RSA 2048]----+ |*. | |*o o | |=* += o | |+oOo.. o | |E=.* .. S | |ooO =. . | |*B.o o. | |*B+ o | |@+ . . | +----[SHA256]-----+ ``` 注意!!!:缩进绝对不能用空格,必须回车 **通过`shell`循环判断在线主机** ```bash [root@client ~]# vim address.sh #!/bin/bash # 创建一个IP地址文件 >ip2.txt # 使用for循环ping测试主机是否在线。 for i in {2..254}; do { ip=192.168.159.$i ping -c1 -W1 $ip &> /dev/null if [ $? -eq 0 ]; then echo "$ip" >> ip2.txt fi }& done [root@client ~]# bash address.sh [root@client ~]# cat ip2.txt 192.168.159.2 192.168.159.131 192.168.159.130 ``` **通过`expect`进行交互** 使用`expect`解释器 ```bash [root@client ~]# expect <<-eof > spawn ssh-copy-id 192.168.159.130 > expect { > "yes/no" { send "yes\n"; exp_continue } > "password" { send "123456\n" } > } > expect eof > eof ``` 使用`shell`脚本 ```bash #!/bin/bash # 创建一个IP地址文件 >ip.txt # 检查公钥是否存在 if [ ! -f /root/.ssh/id_rsa.pub ]; then echo "SSH公钥不存在,正在生成..." ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa fi # 使用for循环ping测试主机是否在线。 for i in {2..254}; do { ip=192.168.159.$i ping -c1 -W1 $ip &> /dev/null if [ $? -eq 0 ]; then echo "$ip" >> ip.txt /usr/bin/expect <<-EOF set timeout 10 spawn ssh-copy-id $ip expect { "yes/no" { send "yes\n"; exp_continue } "password:" { send "123456\n" } } expect eof EOF fi }& done wait echo "finish..." ```