Python中使用pexpect通过scp传输文本文件时传输中断怎么办?
最近搞 jenkins,发现用 python 的 pexpect 蛮方便的,但是发现问题用 scp 传文件只传了一半,代码如下:
def scp(user,host,password):
child = pexpect.spawn("scp " + "/root/.jenkins/workspace/item1111/fuck.css "+user+"@"+host+":"+codePath+"/fuck.css.new")
ret = child.expect([pexpect.TIMEOUT, user])
if ret == 1:
child.sendline(password)
ret = child.expect([pexpect.TIMEOUT, pexpect.EOF])
child.close()
然后排查了半天发现fuck.css只传了半个就结束了,坑啊,没办法暂时只能换种方式传文件(用 curl 了汗),
但是为了要让 scp 能完整传文件我下班后 gooooooooogle 试了半天还是没办法,于是到论坛来问了:
有什么办法让 scp 能完整传文件?
Python中使用pexpect通过scp传输文本文件时传输中断怎么办?
Python 大神去哪里了,我记得坛子里很多人生苦短我用 Python 者来着的
遇到pexpect配合scp传文本文件中断的问题,核心在于scp的交互超时和缓冲区限制。scp在传输过程中会间歇性停顿,而pexpect的默认超时时间较短,容易误判为传输完成或超时。同时,如果传输的数据量较大,pexpect的缓冲区可能被填满,导致后续输出无法被捕获,进而引发超时或异常。
解决的关键是调整pexpect的等待逻辑和缓冲区大小。下面是一个改进后的示例代码,它通过延长超时时间、使用expect的timeout参数动态等待传输完成,并适当增大缓冲区:
import pexpect
import sys
def scp_transfer_with_retry(local_file, remote_file, host, user, password, port=22):
"""
使用pexpect通过scp传输文件,增加超时和缓冲区处理
"""
# 构建scp命令
cmd = f'scp -P {port} {local_file} {user}@{host}:{remote_file}'
try:
# 启动子进程,设置较大的缓冲区(例如1MB)
child = pexpect.spawn(cmd, timeout=60, maxread=1048576)
# 等待密码提示
index = child.expect(['password:', pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
child.sendline(password)
elif index == 1:
print("连接失败或命令立即退出")
return False
elif index == 2:
print("等待密码提示超时")
return False
# 动态等待传输完成:持续检查是否出现EOF(传输结束)或超时
while True:
try:
# 每次等待一小段时间,避免长时间阻塞
child.expect(pexpect.EOF, timeout=5)
break # 传输完成,退出循环
except pexpect.TIMEOUT:
# 传输仍在进行,继续等待
continue
except Exception as e:
print(f"传输过程中出现异常: {e}")
return False
# 检查退出状态
child.wait()
if child.exitstatus == 0:
print("文件传输成功")
return True
else:
print(f"传输失败,退出码: {child.exitstatus}")
return False
except pexpect.ExceptionPexpect as e:
print(f"pexpect异常: {e}")
return False
# 使用示例
if __name__ == "__main__":
success = scp_transfer_with_retry(
local_file='./test.txt',
remote_file='/tmp/test.txt',
host='192.168.1.100',
user='username',
password='yourpassword'
)
if not success:
sys.exit(1)
主要改进点:
- 延长超时:将
spawn的timeout设为60秒,避免短时间无响应就超时。 - 增大缓冲区:
maxread=1048576(1MB)防止大数据量时缓冲区溢出。 - 动态等待传输完成:使用循环配合短超时的
expect来持续检查传输状态,直到遇到EOF(表示scp进程结束)。 - 异常处理:捕获并处理可能的pexpect异常和scp退出状态。
如果文件很大或网络不稳定,可以考虑在循环等待中加入进度检测(例如通过文件大小变化判断),或者改用rsync等更健壮的工具。不过对于大多数文本文件传输,上述调整应该能解决中断问题。
一句话总结:调大超时和缓冲区,用循环动态等待传输完成。
尝试一下 Fabric,项目中使用体验非常好
目前猜测,不应该用 EOF 作为 expect,应该用 100 代表的百分百完成作为 expect。
Python 的 fabric 真好,相见恨晚
处理好 stdin scp 只要 wait 就行了
第二次 ret 是多少,试试把 timeout 设长点。
推荐理由:
1 支持 ftp,sftp,webdav。
2 无需先下载整个文件,winscp 模块支持,从 ftp 服务器上,获取文件的校验码,从而得知是否文件是变化的。
有 Get-WinSCPItemChecksum 从而可以同步文件。
网页 https://winscp.net/eng/docs/protocols 的,Checksum calculation 章节,详细说明了这个内容。
3 支持命令行显示 ftp 完成进度,文件权限,文件掩码,传输限速,是否覆盖。文件传输模式( ascii or 二进制)详见此命令:
New-WinSCPTransferOption


