Linux Shell: 使用 Expect 自动化 SCP 和 SSH 连接的 Shell 脚本详解
文章目录
- 0. 引言
- 2. 解决方案
- 3. 脚本详解
- 脚本1:使用 SSH 和 Expect 自动化登录远端机器
- 脚本说明
- 脚本2:使用 SCP 和 Expect 自动化文件上传
- 脚本说明
- 脚本3:使用 SCP 和 Expect 自动化文件下载
- 脚本说明
- 4. 脚本的使用方法
- 5. 关键技术点
- 5.1. Expect 脚本的引号处理
- 5.2. 处理密码中的特殊字符
- 5.3. 自动接受主机密钥
- 6. 参考资料
0. 引言
在日常的开发过程中,我们经常需要在不同的服务器之间传输文件或者远程登录。Expect 工具是常用的自动登录工具之一,但是在处理包含特殊字符的密码(例如 &
、$
等)时,需要特别注意。这些字符在 Shell 和 Expect 中有特殊的含义,可能导致脚本执行出错。
2. 解决方案
通过以下方法,我们可以编写健壮的脚本:
- 使用 Expect 自动化交互式命令:Expect 是一个基于 Tcl 的工具,专门用于自动化交互式程序。
- 正确处理特殊字符:在 Expect 脚本中,使用单引号和双引号的组合,确保密码中的特殊字符被正确处理。
- 增加超时时间:防止由于网络延迟导致的超时错误。
3. 脚本详解
脚本1:使用 SSH 和 Expect 自动化登录远端机器
#!/bin/bash
# 设置终端类型以确保兼容性
export TERM=xterm-256color
# IP 地址和密码
ip='192.168.1.10'
password='your_password_here'
# 从 known_hosts 文件中删除目标主机,防止 SSH 警告
ssh-keygen -f "/home/$(whoami)/.ssh/known_hosts" -R "${ip}"
# 使用 Expect 自动化 SSH 连接过程
expect -c '
set timeout 10
set password "'"$password"'"
spawn ssh -o StrictHostKeyChecking=no root@'"$ip"'
expect {
# 自动回复 "yes" 以接受主机的真实性
"*yes/no*" { send "yes\r"; exp_continue }
# 当出现密码提示时,发送实际的密码
"*password:*" { send "$password\r"; exp_continue }
eof
}
interact
'
脚本说明
- 清理 known_hosts:使用
ssh-keygen -R
命令从known_hosts
文件中删除目标主机,防止因主机密钥变化导致的 SSH 警告。 - 自动化 SSH 登录:使用 Expect 自动化输入密码,实现无人工干预的 SSH 登录。
- 动态主目录:使用
$(whoami)
获取当前用户名,避免硬编码用户路径。
脚本2:使用 SCP 和 Expect 自动化文件上传
#!/bin/bash
# 设置终端类型以确保兼容性
export TERM=xterm-256color
# 目标文件和 IP 地址
file=$1
ip='192.168.1.10'
# 密码(此处已做修改,实际使用时请替换为你的密码)
password='your_password_here'
# 本地目标目录
destination_dir=$2
if [[ -z $destination_dir ]]; then
destination_dir='~/'
fi
# 使用 Expect 自动化 SCP 过程
expect -c '
set timeout 10
set password "'"$password"'"
spawn scp -o StrictHostKeyChecking=no '"$file"' root@'"$ip"':'"$destination_dir"'
expect {
# 自动回复 "yes" 以接受主机的真实性
"*yes/no*" { send "yes\r"; exp_continue }
# 当出现密码提示时,发送实际的密码
"*password:*" { send "$password\r"; exp_continue }
eof
}
'
脚本说明
- 参数处理:接受两个参数,
$1
为要传输的文件,$2
为目标目录。 - 特殊字符处理:
- 使用单引号
'
包裹整个 Expect 脚本,防止 Bash 提前解析其中的内容。 - 在单引号内,使用
'"$variable"'
的方式正确嵌入 Bash 变量。
- 使用单引号
- Expect 模式匹配:
- 使用
"*yes/no*"
和"*password:*"
进行模式匹配,确保自动回复正确的内容。
- 使用
- 超时时间:将超时时间设置为 10 秒,防止网络延迟导致的超时。
脚本3:使用 SCP 和 Expect 自动化文件下载
#!/bin/bash
# 设置终端类型以确保兼容性
export TERM=xterm-256color
# 远程文件路径和 IP 地址
remote_file=$1
ip='192.168.1.10'
# 密码(此处已做修改,实际使用时请替换为你的密码)
password='your_password_here'
# 本地目标目录(默认为当前目录)
destination_dir=$2
if [[ -z $destination_dir ]]; then
destination_dir='.'
fi
# 使用 Expect 自动化 SCP 下载过程
expect -c '
set timeout 10
set password "'"$password"'"
spawn scp -o StrictHostKeyChecking=no root@'"$ip"':'"$remote_file"' '"$destination_dir"'
expect {
# 自动回复 "yes" 以接受主机的真实性
"*yes/no*" { send "yes\r"; exp_continue }
# 当出现密码提示时,发送实际的密码
"*password:*" { send "$password\r"; exp_continue }
eof
}
'
脚本说明
- 参数处理:接受两个参数,
$1
为远程服务器上的文件路径,$2
为本地目标目录。 - 特殊字符处理:
- 与前两个脚本相同,正确处理密码中的特殊字符。
- Expect 模式匹配:
- 自动处理主机真实性确认和密码输入。
- 超时时间:同样设置为 10 秒。
4. 脚本的使用方法
为了方便地使用上述脚本,可以按照以下步骤进行配置:
-
将脚本存放在指定目录
建议将三个脚本(例如
ssh_my.sh
、scp_my.sh
和rscp_my.sh
)放置在~/.ssh
目录下。这样可以统一管理与 SSH 和 SCP 相关的脚本。mv ssh_my.sh ~/.ssh/ mv scp_my.sh ~/.ssh/ mv rscp_my.sh ~/.ssh/
-
赋予脚本执行权限
确保脚本具有可执行权限:
chmod +x ~/.ssh/ssh_my.sh chmod +x ~/.ssh/scp_my.sh chmod +x ~/.ssh/rscp_my.sh
-
在 Shell 配置文件中设置别名
为了在终端中方便地调用这些脚本,可以在
~/.bashrc
或~/.zshrc
文件中添加别名:alias sshmy='~/.ssh/ssh_my.sh' alias scpmy='~/.ssh/scp_my.sh' alias rscpmy='~/.ssh/rscp_my.sh'
-
重新加载配置文件
修改完配置文件后,重新加载使之生效:
source ~/.bashrc # 或者 source ~/.zshrc
-
使用别名调用脚本
-
登录远程服务器
sshmy
-
上传文件到远程服务器
scpmy 本地文件路径 [远程目标目录]
示例:
scpmy ./example.txt /root/
-
从远程服务器下载文件
rscpmy 远程文件路径 [本地目标目录]
示例:
rscpmy /root/example.txt ./
-
5. 关键技术点
5.1. Expect 脚本的引号处理
- 单引号:将整个 Expect 脚本包裹在单引号内,防止 Bash 对其中内容的变量和特殊字符进行解析。
- 双引号嵌入变量:在单引号内,需要嵌入 Bash 变量时,使用
'"$variable"'
的形式。
5.2. 处理密码中的特殊字符
- 由于密码中可能包含 Shell 或 Expect 的特殊字符,直接使用可能导致解析错误。
- 通过上述引号处理方法,可以安全地传递包含特殊字符的密码。
5.3. 自动接受主机密钥
- 使用
-o StrictHostKeyChecking=no
选项,自动接受新的主机密钥,防止脚本因交互式提示而挂起。 - 在 Expect 脚本中,匹配
"*yes/no*"
,并发送"yes\r"
,进一步确保自动化。
6. 参考资料
- Expect 官方文档
- Bash Shell 脚本高级编程指南