Windows PowerShell 中如何重定向 git gc 并保留进度输出?
单独使用 git gc 2>&1 无法保证保留进度信息。Git 是否默认显示打包进度,取决于 stderr 是否连接终端。PowerShell 重定向 stderr 后,Git 可能检测到管道,从而不再生成这些信息。需要捕获进度日志时,可以通过伪终端运行 git gc,再捕获包装程序的输出。
为什么重定向后没有输出
示例中的 Enumerating objects、Compressing objects 等内容是 Git 的进度信息,通常写入 stderr。stderr 也用于输出正常的诊断和进度提示,有内容并不代表命令失败。
Git 的相关实现通过 isatty(2) 判断 stderr 是否连接终端。例如,Git 2.49.0 的 pack-objects 使用以下语句设置默认进度状态:
progress = isatty(2);
重定向改变输出目标后,Git 可能不再生成进度信息,管道也就收不到这些内容。Git pack-objects 源码
PowerShell 中的 2>&1 会将 Error 流合并到 Success 流,但不会强制外部程序生成输出。PowerShell 重定向文档
只需在控制台合并显示
如果 stdout 仍连接交互式控制台,可以让 cmd.exe 处理重定向:
cmd.exe /d /c 'git gc 2>&1'
单引号使 2>&1 随命令字符串传给 cmd.exe。只要 stdout 仍连接控制台,合并后的 stderr 也会指向控制台,从而保留 Git 的终端检测条件。/d 禁用 AutoRun,/c 执行命令后退出。cmd.exe 文档
这种方式适合直接显示。如果再给整个命令添加管道、变量捕获或文件重定向,stdout 就可能变成管道或文件,进度仍可能消失。
需要捕获进度时使用 winpty
如果环境中安装了 winpty,且该版本支持 -Xallow-non-tty 和 -Xplain,可以让 Git 在 winpty 提供的控制台中运行,再将控制台内容转为可捕获的输出。winpty 的工作方式和这两个选项可分别在项目说明与参数解析源码中核对。
以下示例假设 Git 安装在默认的 Program Files 目录,且包含 usr\bin\winpty.exe;请按实际安装位置调整路径:
$winpty = Join-Path $env:ProgramFiles 'Git\usr\bin\winpty.exe'
& $winpty -Xallow-non-tty -Xplain git.exe gc 2>&1 |
Tee-Object -FilePath .\git-gc.log
其中:
-Xallow-non-tty允许 winpty 自身的输入或输出连接到非终端目标。-Xplain请求纯文本形式的控制台输出。Tee-Object将收到的内容写入日志,并继续输出到当前管道。
Git 的 stderr 需要保持连接到 winpty 提供的控制台。示例捕获的是 winpty 转出的控制台内容,不应把 2>&1 放进 Git 子进程内部,让它直接指向日志文件。
这些 -X 参数属于 winpty 的内部选项,并非所有安装都提供相同支持。如果路径不存在或参数无法识别,需要使用可用的伪终端工具;普通重定向无法替代伪终端。
注意事项
git gc 没有文档化的 --progress 参数,不能直接套用其他 Git 子命令的写法。--quiet 用于抑制进度,也不能通过取消静默来保证在管道中强制显示进度。git gc 文档
伪终端捕获的是终端显示内容。由于进度会原地刷新,日志可能包含重复状态,换行效果也可能与屏幕不同,因此不适合依赖固定行数解析。脚本还应在命令结束后立即检查 $LASTEXITCODE 来判断执行结果,不能仅凭有无进度文字判断成功或失败。
备注:内容仅供参考。
