问题现象描述
在编写自动化部署脚本时,我们经常需要使用scp/sftp命令从远程服务器获取文件。为了记录操作日志,通常会使用tee
命令将输出同时显示在终端和写入日志文件。但实际使用中发现,文件传输的进度条信息却无法通过这种方式捕获。
# 直接执行scp的输出示例
scp user@remote:/path/to/file /local/path
file.zip 100% 78MB 39.1MB/s 00:02
# 使用tee时的输出示例
scp user@remote:/path/to/file /local/path | tee -a release.log
# 仅显示文件名,没有进度信息
为什么进度信息会丢失
这是因为scp/sftp的进度信息是通过标准错误(stderr)输出的,而默认情况下tee
只捕获标准输出(stdout)。此外,进度信息实际上是使用终端控制字符实现的动态更新,这种特殊输出方式在通过管道传递时会被过滤。
完整解决方案
要完整捕获所有输出,我们需要:
- 同时重定向stdout和stderr
- 保持终端交互特性
- 正确处理控制字符
方法1:使用script命令记录完整会话
# 开始记录
script -q -a release.log -c "scp -v user@remote:/path/to/file /local/path"
# -q 静默模式
# -a 追加到日志文件
# -c 执行指定命令
方法2:结合stdbuf和tee
# 对于支持-v参数的scp/sftp
scp -v user@remote:/path/to/file /local/path 2>&1 | stdbuf -oL tee -a release.log
# 解释:
# 2>&1 将stderr重定向到stdout
# stdbuf -oL 保持行缓冲
方法3:使用pv命令显示进度
如果只需要显示进度而不需要原生的scp进度条,可以结合pv命令:
# 需要先获取远程文件大小
filesize=$(ssh user@remote "stat -c%s /path/to/file")
scp user@remote:/path/to/file /local/path | pv -s ${filesize} | tee -a release.log
最佳实践建议
根据实际需求,推荐以下方案:
- 需要完整记录所有交互细节:使用
script
命令 - 仅需要基本进度显示:使用
scp -v
结合tee
- 需要美观的进度条:使用
pv
命令方案
对于自动化脚本,建议添加时间戳以便后续分析:
scp -v user@remote:/path/to/file /local/path 2>&1 | stdbuf -oL awk '{print strftime("[%Y-%m-%d %H:%M:%S]"), $0}' | tee -a release.log