shell/shell-Expect.md
2025-03-28 20:09:53 +08:00

5.7 KiB
Raw Permalink Blame History

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 安装

[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. 命令之后是参数,相互用空格间隔

命令 参数 [参数]

2. 使用变量

[root@wxin ~]# set var 1000   # 定义变量<br>set var [lindex $argv 0] #将 var = argv[0]<br>$foo

3. 嵌套命令

将一个命令的输出,作为另一个命令的输入参数

命令 [命令 参数]

4. 双引号

将词组标记为一个参数,双引号内$符号有效

命令 "hello world $foo"

5. 大括号

将词组标记为一个参数,但大括号内无法扩展变量

命令 {hello world}

6. 反斜线,转义

6.示例解决ssh登录验证免交互

1.命令选项说明

  • spawn expect 内部命令启动一个shell程序。
  • expect 期望哪些内容
  • yes/no 就send发送 yes \r 表示回车
  • password 就 send 发送 centos
  • exp_continue跳过循环就继续下一条语句。
  • interact 允许用户交互

2. Expect 实现免交互公钥推送

安装和生成公钥

[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循环判断在线主机

[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解释器

[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脚本

#!/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..."