通过expect脚本完成SCP文件传输

通过SCP做文件拷贝是比较方便的,但如果是大文件且传输时间比较长的情况下,则是需要在后台自动执行较为方便。但SCP的交互过程中需要输入口令,怎么通过脚本来解决这个问题呢?

expect

在实际工作中,我们运行命令、脚本或程序时,经常需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。

比如,在使用ssh、scp等命令访问远程机器时,会遇到需要添加ssh key fingerprint的应答和手工输入密码的流程。

1
2
3
4
5
6
7
[develop@localhost ~]$ scp fauria-vsftpd.tar develop@192.168.0.137:/home/develop/file/
The authenticity of host '192.168.0.137 (192.168.0.137)' can't be established.
ECDSA key fingerprint is SHA256:Fmvff/8AZyx5LpgrVQsYF8w+zKTaDabmtCYRwNPqP1o.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.0.137' (ECDSA) to the list of known hosts.
develop@192.168.0.137's password:
[develop@localhost ~]$

那么为了解决大文件且长时间的文件传输,要在后台运行的需求,就需要用到expect脚本。

expect安装

这里以CentOS 7.6.1810为例子,如果是联网情况,执行:

1
yum -y install expect

会自动安装tcl、expect。

如果是隔离网环境下,可以通过源码编译的方式安装,也可以通过离线rpm安装。

这里我选择通过离线rpm的形式,毕竟还是会方便一些。关于如何获取离线rpm,可以参考我之前写的文章CentOS 7停服(EOL)后如何下载RPM包

离线rpm包一共两个文件,通过rpm -ivh命令安装即可,注意要先安装tcl rpm:

1
2
3
tcl-8.5.13-8.el7.x86_64.rpm

expect-5.45-14.el7_1.x86_64.rpm

expect脚本

有别于Shell脚本,注意expect脚本中的Shebang行是不同的。

比如创建一个名为scp_with_password.exp的expect脚本,内容如下:

1
2
3
4
5
6
7
8
9
#!/usr/bin/expect -f
set timeout -1
set password "your_password"
spawn scp -P 10022 -r /home/develop/file/ develop@192.168.0.103:/home/develop/file/
expect "*yes/no"
send "yes\r"
expect "password:"
send "$password\r"
expect eof

脚本内容和执行流程如下:
1、set用于配置变量。比如expect自身变量或者脚本中需要传递的变量,其中timeout关键字较为重要,决定脚本执行的超时时间,我这里因为这涉及到大文件且长时间的传输,就设置为-1,会在进程执行完成后再退出。关于timeout,文档中是这样解释的:

eof, the corresponding body is executed upon end-of-file. If a pattern is the keyword timeout, the corresponding body is executed upon timeout. If no timeout keyword is used, an implicit null action is executed upon timeout. The default timeout period is 10 seconds but may be set, for example to 30, by the command “set timeout 30”. An infinite timeout may be designated by the value -1. If a pattern is the keyword default, the corresponding body is executed upon either timeout or end-of-file.

2、spawn用于启动指定进程
3、expect用于获取指定关键字,主要是匹配程序的交互输出
4、send用于向指定程序发送指定字符,我这里会自动确认添加ssh key fingerprint以及输入password
5、任务执行完成后退出

expect执行

执行命令:

1
expect scp_with_password.exp

但如果需要挂起在后台执行,搭配nohup命令操作即可:

1
nohup expect scp_with_password.exp > t-scp.log 2>&1 &

具体执行时的文件传输情况,则可以直接查看t-scp.log文件。

关于expect有很多资料可以阅读,是非常实用的技巧,像一把瑞士军刀,非常容易操作。我这里只是简单记录一下,expect在某些条件下是真的救命工具。

参考

1、https://github.com/dunwu/linux-tutorial/blob/master/docs/linux/expect.md
2、https://linux.die.net/man/1/expect