Linux下如何同时输出scp/sftp命令进度到终端和日志文件


阅读 2 次

问题现象描述

在编写自动化部署脚本时,我们经常需要使用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)。此外,进度信息实际上是使用终端控制字符实现的动态更新,这种特殊输出方式在通过管道传递时会被过滤。

完整解决方案

要完整捕获所有输出,我们需要:

  1. 同时重定向stdout和stderr
  2. 保持终端交互特性
  3. 正确处理控制字符

方法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