使用 tee 从 GitHub 克隆管道输出会丢失输出

使用 tee 从 GitHub 克隆管道输出会丢失输出

我想我应该将 GitHub 存储库克隆的输出记录到文件中。如果我跑

git clone [email protected]:my_repo/my_repo.git | tee -a log

我得到一个空log文件,但命令行输出如下:

Cloning into 'my_repo'...
remote: Enumerating objects: 284, done.
remote: Counting objects: 100% (228/228), done.
remote: Compressing objects: 100% (133/133), done.
remote: Total 284 (delta 122), reused 162 (delta 69), pack-reused 56
Receiving objects: 100% (284/284), 873.22 KiB | 2.71 MiB/s, done.
Resolving deltas: 100% (138/138), done.

这对我来说已经有点神秘了。我想我应该尝试将 和 都通过管道传输stderrstdout文件

git clone [email protected]:my_repo/my_repo.git 2>&1 | tee -a log

现在log文件和命令行都得到了

克隆到“my_repo”...

我在这里缺少什么?如何将这些remote: ...行放入文件中tee

答案1

正如评论中提到的,git检查标准输出/错误是否是终端。如果是的话,程序会在终端输出中添加一些额外的糖分,以文本的形式指示进度和一些统计数据。如果不是,它会将标准流的输出保持在最低限度。

在内部它使用isatty() 例如这里所示。这是命令行程序的一种常见做法。最重要的是要剥离 ANSI 颜色,因为您通常不希望将其放入文件中,弄乱管道处理等。例如检查:

/bin/ls --color=auto -1 # ls print column in pipe
/bin/ls --color=auto | sed -n 'l'
/bin/ls --color=always | sed -n 'l'

可以选择通过说或始终抑制或始终打印来git覆盖这一点。进度输出被打印出来,并且(通常)不用于消费。尽管如此:-q--quiet--progressstderr

git clone --progress foo/bar.git 2>&1 | tee log

其他程序可能没有类似的选项,并且必须跳过一些步骤才能获取数据。这样做是为了最大限度地减少混乱等,例如在自动化脚本中,仍然打印到stderr,但将其减少为错误消息。例如git

$ git clone -q https://nonexistig-url.com/foo.git
fatal: https://nonexistig-url.com/foo.git/info/refs not valid: 
       is this a git repository?
$ echo $?
128

没有任何进展,但会收到错误消息,这通常在事后设想。克隆过程本身的进展被抑制。

有时可以通过其他方式获取信息,即通过后处理或运行其他或附加命令。例如git count-objects -v


假终端

如果确实想要抑制输出,并且程序没有像这样的选项git,一种选择是让程序“认为”它写入终端,即使它没有。实现这一目标的各种方法;我用于临时事物的一种方法是,我只是出于某种原因想要它来替换系统isatty()与一个总是返回 true

echo 'int isatty(int fd) { return 1; }' | \
gcc -O2 -fpic -shared -ldl -o faketty.so -xc -

strip faketty.so
chmod 400 faketty.so

与包装脚本一起使用,faketty如下所示:

#! /bin/sh -
LD_PRELOAD=/path/to/faketty.so "$@"

并用作:

faketty git clone foo

正如代码所示,它确实不进行任何文件描述符检查等,而只是一种黑客攻击。


生成数据的问题

git clone命令不会对输出着色,但会显示进度。完成此操作的方法是重复覆盖终端中的同一行。它通常写道:

Cloning into 'foo'...\n
remote: Enumerating objects: 236, done.        \n
\rReceiving objects:   0% (1/236)   
\rReceiving objects:   1% (3/236)   
\rReceiving objects:   2% (5/236)   
...
\rReceiving objects:  79% (187/236)   
\rremote: Total 236 (delta 0), reused 0 (delta 0), pack-reused 236        \n
...
\rReceiving objects:  99% (234/236)   
\rReceiving objects: 100% (236/236)   
\rReceiving objects: 100% (236/236), 79.45 MiB | 104.73 MiB/s, done.\n
...

这里的\r,与回车一样,告诉终端将光标移动到行首与 不同\n,如换行/行尾,它将光标移动到行的开头下一行

结果每次写覆盖上一行直到remote: Total 236 …结束的行\n,并且该过程在下一行继续,结果对于查看者来说是这样的:

Cloning into 'foo'...
remote: Total 236 (delta 0), reused 0 (delta 0), pack-reused 236        
Receiving objects: 100% (236/236), 79.45 MiB | 104.73 MiB/s, done.
...

包括每次写入的视觉进度。对于这种情况,cat当终端解释回车时,简单的日志看起来不错,但 ased -n l log将显示真实内容。可选的类似sed 's/\r/\n/g' log显示“线”

清理

至于这个案例,照原样,一个简单的管道或日志槽的后处理sed就足够了:

sed 's/.*\r//'
# Optionally to trim trailing white space, either of:
sed 's/.*\r//;s/ \+$//'
sed 's/.*\r\| \+$//g'

它有些脆弱,因为进度输出可能会在发行版、版本等之间发生变化,因此不能保证信息相同。如果输出有颜色,则需要进一步删除这些序列。

人们将失去显示的进度(实时计数),并且只有在命令通过之前git通过管道传输时才能获得最终结果。sedtee

相关内容