Linux 下使用 Expect 实现自动化文件传输
现状背景
生产环境的业务应用机器因日志文件持续积累(gz 压缩格式,总量超过 100G,按策略需保留最近 2 年),触发了磁盘空间告警。经排查,同一局域网内存在闲置机器且磁盘空间充裕,因此计划将历史日志迁移到闲置机器上以释放生产机器的磁盘压力。
(注:所有机器资源均为按月付费的云服务器。)
补充说明:
- • 局域网内机器之间通过 SSH + 账密方式登录(未配置免密认证)
- • 日志文件路径格式为
/mnt/logs/app/yyyy-MM/yyyy-MM-dd-i.gz,单个文件约 300M,每天产生 5-6 个文件
核心诉求:在多台机器之间批量传输大量文件,需要自动化手段替代手动逐台操作。
需求
在目标闲置机器上执行一条命令,传入年月参数(如 2024-06),脚本自动遍历所有生产机器,批量复制对应月份的日志文件到本地。
关键核心 — Expect 命令
当 SSH/SCP 等命令需要输入密码时,标准的 shell 脚本无法自动响应交互提示。Expect 正是解决这类问题的工具——它能监听进程的交互提示(如密码输入框),并自动发送预设的应答内容,将原本需要人工干预的操作编排成可自动执行的脚本。
安装方式:
yum -y install expect
Expect 四大核心指令
- • spawn:启动一个新的子进程,后续的命令交互都在该进程中进行
- • expect:等待并匹配子进程输出的特定字符串(如密码提示符)
- • send:向子进程发送字符串(模拟用户输入),通常需要以
\r 结尾表示回车确认 - • interact:将控制权交还给用户,允许手动交互(自动化场景中通常不使用)
关键机制: 只有 spawn 启动的进程所产生的输出才会被 expect 捕获。spawn 会 fork 出一个子进程,Expect 仅监听该子进程的三类信息:标准输出中的提示文本、进程结束信号(eof)、以及超时事件(timeout)。
实例
#!/usr/bin/expect# 设置全局超时时间,单位秒set timeout 1800# 获取命令行参数if {$argc < 1} {puts "Usage: $argv0 <date>"exit 1}# 解析参数set date_param [lindex $argv 0]set username "myusername"set password "mypassword"# 定义服务器IP列表set ips {"192.168.1.10" "192.168.1.11" "192.168.1.12"}# 远程目录路径set remote_base_dir "/mnt/logs/xxx"# 基础本地备份目录set base_local_dir "/mnt/backup"# 遍历每台服务器foreach ip $ips {# 为每台服务器创建独立的本地目录 /mnt/backup/ip/date_paramset local_dir "$base_local_dir/$ip/$date_param" # 检查并创建本地目录(如果不存在) if {![file exists $local_dir]} { if {[catch {exec mkdir -p $local_dir} result]} { puts "创建目录 $local_dir 失败: $result" continue } else { puts "创建目录: $local_dir" } } puts "正在从 $ip 复制文件到 $local_dir ..." # 构造远程目录路径 set remote_dir "$remote_base_dir/$date_param" # 执行 scp 命令 spawn scp -r $username@$ip:$remote_dir/* $local_dir # 等待并响应密码提示 expect { "*yes/no*" { send "yes\r" exp_continue } "*password:*" { send "$password\r" } timeout { puts "连接 $ip 超时" continue } } # 等待命令执行完成 expect { eof { puts "从 $ip 复制完成" } timeout { puts "从 $ip 复制超时" } }}puts "所有文件复制完成。"