Bash下scp命令输出来源及捕获输出到变量/数组方法
问题1:scp进度文本的来源
- 你看到的传输进度100%是本地scp客户端生成输出的,和远程端无关:远程服务器上的sshd进程只负责传文件数据和协议控制报文,根本不会返回进度类文本,传输百分比、速率、剩余时间全是本地客户端统计已传数据量后自己算出来的。
- 常规重定向抓不到这部分内容的核心原因:scp只要检测到自己跑在真实终端(TTY)里,就不会把进度写到标准输出(fd1)、标准错误(fd2)这两个常规流里,而是直接打开进程绑定的
/dev/tty设备文件往里面写进度,这部分输出直接绕过了普通的流重定向逻辑,这就是你写了重定向规则之后进度照样打在终端上、变量却空着的根本原因。 - 顺带说下你原代码的重定向写法错误:shell解析重定向是从左往右执行的,你写的
2>&1 > $mytty实际效果是:先把标准错误接到命令替换的管道(也就是本来要存到myvar的通道),再把标准输出切到TTY,这时候标准错误还连在管道上,但scp的进度根本不走标准错误,管道里读不到任何东西,myvar自然是空的。就算你把重定向顺序写对成> $mytty 2>&1,还是抓不到进度——毕竟进度压根不经过这两个文件描述符。
问题2:Bash中捕获scp进度输出的正确方法
scp只有在检测到自己运行在终端环境下才会输出进度条,要抓到这部分直接写TTY的内容,得用伪终端(PTY)伪造真实终端环境跑scp,再把所有终端输出截下来,最方便的是用系统默认预装的script命令实现:
捕获到普通变量
#!/bin/bash # 参数说明: # -q 静默模式,不打印script自身的启动、退出提示 # -c 后面接要执行的scp命令 # 末尾指定记录文件为/dev/null,伪终端里的所有输出会走到script的标准流,可供捕获 myvar=$(script -q -c "scp test.js crashdog@webserver.com:/home/crashdog" /dev/null 2>&1) echo "捕获到的scp输出:" echo "$myvar"
捕获到数组
如果需要按行拆分存到数组,配合mapfile和进程替换就行:
#!/bin/bash # 把scp的所有终端输出按行读入scp_output数组 mapfile -t scp_output < <(script -q -c "scp test.js crashdog@webserver.com:/home/crashdog" /dev/null 2>&1) # 遍历输出数组内容 echo "数组存储的输出行:" for line_idx in "${!scp_output[@]}"; do echo "行$((line_idx+1)): ${scp_output[$line_idx]}" done
补充:如果遇到极个别没有预装
script的环境,也可以用expect的spawn创建伪终端运行scp再抓输出,但绝大多数Linux发行版、macOS都默认带script,优先用这个方案最省事。
内容的提问来源于stack exchange,提问作者crashdog
相关产品推荐
相关产品推荐

