带ProxyCommand的SFTP结合Expect脚本运行失败,求助排查
带代理的SFTP脚本执行报错排查与解决
问题场景
尝试执行带HTTP代理的SFTP命令,用expect脚本实现自动化交互,但运行后报错。原脚本代码如下:
sftpCommand="sftp -o ConnectTimeout=3 -o "ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password@1 %h %p" -oPort=22 user-sftp2@202.89.99.20" /usr/bin/expect << EOD set timeout -1 spawn /bin/sh -c $sftpCommand expect { "*?assword:*" { send "$sftpPassword\r" } "*>*" { send "put $datewise_dir $remoteDir\r" } "*>*" { send "bye\r" } } EOD
报错信息
运行后出现以下错误:
line 6: {--proxy-type}: command not found spawn /bin/sh -c /bin/sh: -c: option requires an argument
问题原因
- 引号嵌套冲突:
sftpCommand变量中,ProxyCommand部分使用的双引号和外层变量的双引号未做处理,导致shell解析时提前截断命令,把--proxy-type当成了独立命令执行。 - expect变量未转义:
spawn /bin/sh -c $sftpCommand中直接使用未加引号的变量,shell会对变量内容分词,破坏了命令的完整性,导致/bin/sh -c没有获取到有效参数。 - expect匹配逻辑冗余:两个
*>*匹配规则会导致逻辑混乱,只会触发第一个匹配到的动作,无法完成后续的上传和退出操作。
解决步骤
1. 修复变量引号嵌套
将ProxyCommand内部的双引号转义,或者改用单引号包裹整个变量,确保shell能正确解析完整命令:
# 方案1:转义内部双引号 sftpCommand="sftp -o ConnectTimeout=3 -o \"ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password@1 %h %p\" -oPort=22 user-sftp2@202.89.99.20" # 方案2:用单引号包裹整个变量 sftpCommand='sftp -o ConnectTimeout=3 -o "ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password@1 %h %p" -oPort=22 user-sftp2@202.89.99.20'
2. 修复expect的spawn命令
在expect中引用$sftpCommand时,用双引号包裹,避免shell分词:
spawn /bin/sh -c "$sftpCommand"
3. 优化expect匹配逻辑
调整匹配规则,使用exp_continue实现顺序匹配,确保完成密码输入、文件上传、退出的完整流程:
/usr/bin/expect << EOD set timeout -1 spawn /bin/sh -c "$sftpCommand" expect { "*?assword:*" { send "$sftpPassword\r" exp_continue # 输入密码后继续等待SFTP提示符 } "*>*" { send "put $datewise_dir $remoteDir\r" expect "*>*" { send "bye\r" } # 上传完成后执行退出 } } EOD
完整修复后的脚本
sftpCommand="sftp -o ConnectTimeout=3 -o \"ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password@1 %h %p\" -oPort=22 user-sftp2@202.89.99.20" /usr/bin/expect << EOD set timeout -1 spawn /bin/sh -c "$sftpCommand" expect { "*?assword:*" { send "$sftpPassword\r" exp_continue } "*>*" { send "put $datewise_dir $remoteDir\r" expect "*>*" { send "bye\r" } } } EOD
内容的提问来源于stack exchange,提问作者Ketan
相关产品推荐
相关产品推荐

