Git 实时将输出生成到文件中,但我无法在 while 循环中直接实时回显它

Git 实时将输出生成到文件中,但我无法在 while 循环中直接实时回显它

我想要的是

我的实际长期目标是绕过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实时更新一样?


编辑

我也已经尝试过stdbufunbuffer按照建议这里这里

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才会获取所有输出,包括所有中间状态和回车符,并在循环内回显它。 如果您也将echodone通过管道传输cat -A,您会看到相同的输出,其中全是^Ms,如上所示。

为了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

相关内容