我想要的是
我的实际长期目标是绕过git
过程的所有输出绕过到另一个函数中(为了制作进度条 - 请参阅我以前的问题了解更多详细信息)。
我目前所得到的
我以为我的问题已经解决了这个问题但现在看来,还只是部分如此。
我已经知道了git
stderr
仅按设计产生输出- 我不得不使用该
--progress
选项git
来强制git
真正打印出进度。
现在如果我跑
#or also &> output.file
git clone --progress http://someRepo > output.file 2>&1
我可以通过同时在第二个终端运行来查看完整进度
tail -f output.file
我明白了
Cloning into 'someRepo' ...
remote: Counting objects: 2618, done.
remote: Compressing objects: 100% (14/14), done.
Receiving objects: 4% (106/2618), 42.11 MiB | 5.67 MiB/s
实时更新直至完成
Cloning into 'someRepo' ...
remote: Counting objects: 2618, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 2618 (delta 2), reused 12 (delta 1), pack-reused 2603
Receiving objects: 100% (2618/2618), 258.95 MiB | 5.71 MiB/s, Done.
Resolving differences: 100% (1058/1058), Done.
Checking connectivity ... Done.
到目前为止一切顺利。
我的问题
现在我不想再把这个输出放入文件中,而是将它绕过函数。所以我尝试了
#or also |&
git clone --progress http://someRepo 2>&1 | {
while read -r line
do
# echo is for testing only
# later I want to pass this to another function
# EDIT: I added bypassed in order to see what is bypassed
echo "bypassed: ${line}"
done
}
但是像这样运行它我只得到显示的输出(编辑:我将其添加bypassed:
到行中echo
以查看它是如何传递的。)
bypassed: Cloning into 'someRepo'
bypassed: remote: Counting objects: 2618, done.
remote: Compressing objects: 100% (14/14), done.
并且只有在最后下载完成时,我才会将其余部分全部一次性跳转到此输出(编辑:我将其添加bypassed:
到echo
行中以查看它是如何传递的。)
bypassed: Cloning into 'someRepo' ...
bypassed: remote: Counting objects: 2618, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 2618 (delta 2), reused 12 (delta 1), pack-reused 2603
Receiving objects: 100% (2618/2618), 258.95 MiB | 5.71 MiB/s, Done.
Resolving differences: 100% (1058/1058), Done.
bypassed: Checking connectivity ... Done.
因此显然进度块的 5 行都是一次性传入的.. 也许这是管道和 while 循环的问题?
那么我遗漏了什么?我该如何运行命令才能让输出也实时“回显”/绕过到我的函数中,就像tail -f output.file
实时更新一样?
编辑
我也已经尝试过stdbuf
了unbuffer
按照建议这里和这里
stdbuf -i0 -o0 -e0 git clone --progress http://someRepo |& ...
stdbuf -oL git clone --progress http://someRepo |& ...
unbuffer git clone --progress http://someRepo |& ...
但这并没有改变输出。
答案1
这是一个非常有趣的问题,我不得不实验了一段时间,但答案其实非常简单!
的进度输出如何git
工作?它显示带有进度百分比的单个状态行,并一直更新它直到完成。它通过不打印换行符来实现这一点\n
而是打印回车\r
进度行末尾的字符,这会导致输出光标再次跳回到行首,准备用更新的值再次覆盖最后写入的行。
您可以通过将git
的输出通过管道传输来观察这一点cat -A
,它不会解释不可见的字符但会显示它们,因此例如\r
变成^M
并且换行符另外用 表示$
:
Cloning into 'MyRepository'...$
remote: Counting objects: 2317, done. $
Receiving objects: 0% (1/2317) ^MReceiving objects: 1% (24/2317) ^MReceiving objects: 2% (47/2317) ^MReceiving objects: 3% (70/2317) ^MReceiving objects: 4% (93/2317) ^MReceiving objects: 5% (116/2317)
[...]
那么,为什么这会影响呢read
?很明显,正如help read
所说(摘录,重点是我的):
Read a line from the standard input and split it into fields.
read
等待换行符 ( \n
) 才完成。 的输出git
在进度显示期间不包含任何换行符,因此read
未完成。 只有在进度显示完成并git
真正打印下一行后,read
才会获取所有输出,包括所有中间状态和回车符,并在循环内回显它。 如果您也将echo
或done
通过管道传输cat -A
,您会看到相同的输出,其中全是^M
s,如上所示。
为了read
捕获所有中间进度行,您可以将所有回车符转换为真正的换行符,方法是通过管道传输内容tr \\r \\n
。这样,每个状态都会打印在新行上,而不是覆盖上一行。
因此,您使用的完整循环可能如下所示:
git clone --progress http://someRepo 2>&1 | tr \\r \\n |
while read -r line ; do
echo "bypassed: ${line}"
done
在我的系统上,此解决方案在终端中显示的输出仍然不完全流畅,并且有点卡顿,但我无法进一步改进。至少你现在有了真正的“进度”输出。
答案2
这是一个完整的解决方案,虽然不是很好,但效果很好!
total=0
{
git clone --progress http:yourRepo 2>&1 | tr \\r \\n |
while read -r line ; do
cur=`grep -oP '\d+(?=%)' <<< ${line}`
total=$((total+cur))
percent=$(bc <<< "scale=2;100*($total/11767)")
echo "$percent/1" | bc
done
} | whiptail --title "Cloning" --gauge "Cloning Git Repository" 8 78 0