![使用 tee 从 GitHub 克隆管道输出会丢失输出](https://linux22.com/image/200755/%E4%BD%BF%E7%94%A8%20tee%20%E4%BB%8E%20GitHub%20%E5%85%8B%E9%9A%86%E7%AE%A1%E9%81%93%E8%BE%93%E5%87%BA%E4%BC%9A%E4%B8%A2%E5%A4%B1%E8%BE%93%E5%87%BA.png)
我想我应该将 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.
这对我来说已经有点神秘了。我想我应该尝试将 和 都通过管道传输stderr
到stdout
文件
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
--progress
stderr
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
通过管道传输时才能获得最终结果。sed
tee