Shell脚本-expect

一、前言

在 Linux 系统管理与自动化运维中,我们经常需要编写 Shell 脚本来完成各种任务。但有些命令(如 sshscppasswdftp 等)在执行时会等待用户手动输入密码或确认信息,这就导致脚本无法完全自动化运行。

为了解决这个问题,我们可以使用一个强大的工具 ------ expect

本文将带你深入理解 expect 的工作原理,并通过多个实用案例,教你如何在 Shell 脚本中使用 expect 实现自动应答、自动登录、自动传输文件等交互式操作

二、什么是 expect

expect 是一个基于 Tcl(Tool Command Language)语言的自动化交互程序,能够"模拟"用户输入,自动响应命令行中的提示信息(如密码输入、确认提示等),从而实现脚本的全自动运行。

✅ 适用场景:

  • 自动 SSH 登录远程服务器
  • 自动 SCP 文件传输
  • 自动修改用户密码
  • 自动与 FTP/Telnet 等交互式程序通信

三、安装 expect

大多数 Linux 发行版默认不安装 expect,需要手动安装。

Ubuntu / Debian:

bash 复制代码
sudo apt-get update
sudo apt-get install expect

CentOS / RHEL / Fedora:

bash 复制代码
sudo yum install expect
# 或者使用 dnf(较新版本)
sudo dnf install expect

安装完成后,可通过以下命令验证是否成功:

bash 复制代码
expect -v
# 输出类似:expect version 5.45.4

四、expect 基本语法

expect 脚本的基本结构如下:

复制代码
#!/usr/bin/expect

set timeout <秒数>
spawn <要执行的命令>
expect "<期望出现的提示符>"
send "<发送的响应内容>\r"
expect eof

关键命令说明:

命令 说明
spawn 启动一个新的进程(如 ssh、scp 等)
expect 等待某个输出字符串(提示符、密码提示等)
send 向进程发送字符串(如密码、回车等)
set timeout 设置等待超时时间(-1 表示永不超时)
expect eof 等待进程结束
interact 将控制权交还给用户(用于部分自动化)

💡 注意:send 发送的内容末尾通常要加上 \r(回车符),相当于按下回车键。

五、实战案例

✅ 案例 1:自动 SSH 登录远程服务器

复制代码
#!/usr/bin/expect

set timeout 30
set host "192.168.1.100"
set user "root"
set password "your_password"

spawn ssh $user@$host

expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" }
}

expect "#"
interact

📌 说明:

  • 使用 expect {} 处理多种可能的提示(如首次连接时的 yes/no)。
  • exp_continue 表示继续等待下一个匹配。
  • interact 表示登录成功后将终端控制权交给用户。

✅ 案例 2:自动 SCP 传输文件

复制代码
#!/usr/bin/expect

set timeout 30
set host "192.168.1.100"
set user "root"
set password "your_password"
set local_file "/tmp/data.txt"
set remote_path "/root/"

spawn scp $local_file $user@$host:$remote_path

expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" }
}

expect eof

📌 说明:

  • 文件传输完成后,expect eof 等待进程结束。
  • 适用于定时备份、批量部署等场景。

✅ 案例 3:Shell 脚本中调用 expect

你可以在普通的 Shell 脚本中嵌入 expect 脚本,实现混合编程:

bash 复制代码
#!/bin/bash

HOST="192.168.1.100"
USER="root"
PASS="your_password"

expect << 'EOF'
set timeout 30
spawn ssh $env(USER)@$env(HOST)
expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$env(PASS)\r" }
}
expect "#"
send "uptime\r"
sleep 1
send "exit\r"
expect eof
EOF

📌 技巧:

  • 使用 << 'EOF' 将 expect 脚本作为 here-document 嵌入。
  • 通过 $env(VAR) 获取 Shell 变量(需在 expect 中启用环境变量传递)。

六、常见问题与解决方案

❌ 问题 1:expect: command not found

原因 :系统未安装 expect 或路径错误。

解决

bash 复制代码
which expect  # 查看是否安装
# 若未安装,请使用包管理器安装

❌ 问题 2:密码错误或登录失败未捕获

建议:增加错误处理机制:

复制代码
expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" }
    timeout { puts "连接超时"; exit 1 }
    eof { puts "连接失败或主机不可达"; exit 1 }
}

❌ 问题 3:中文乱码或特殊字符问题

解决 :确保终端编码一致,或避免在 send 中使用特殊字符。可使用 stty 调整终端设置。

七、安全建议 ⚠️

虽然 expect 很强大,但在生产环境中使用时需要注意以下安全问题:

  1. 避免明文存储密码

    将密码写在脚本中存在泄露风险。建议:

    • 使用 SSH 免密登录(公钥认证)
    • 使用 ssh-agent 或密钥管理工具
  2. 限制脚本权限

    设置脚本权限为 600,仅允许所有者读写:

    复制代码
    chmod 600 auto_ssh.exp
  3. 使用配置文件替代硬编码

    将主机、用户名、密码等信息放在独立的配置文件中,并加密保护。

八、总结

功能 工具 说明
自动交互 expect 模拟用户输入,实现自动化
启动进程 spawn 执行需要交互的命令
匹配输出 expect 等待特定提示
发送输入 send 发送密码、命令等
超时控制 set timeout 防止无限等待

优点 :简单高效,适用于各种交互式命令。 ❌ 缺点:明文密码有安全隐患,建议结合 SSH 密钥使用。

九、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!