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 обновляется в реальном времени?


РЕДАКТИРОВАТЬ

Я также уже пробовал stdbuf и даже unbuffer, как предложено здесь и здесь

stdbuf -i0 -o0 -e0 git clone --progress http://someRepo |& ...

stdbuf -oL git clone --progress http://someRepo |& ...

unbuffer git clone --progress http://someRepo |& ...

, но это не так t изменить выход.

0
задан 28 December 2017 в 18:24

2 ответа

Это очень интересный вопрос, и мне пришлось немного поэкспериментировать, но ответ на самом деле очень прост!

Как работает вывод прогресса 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 с, как и выше.

Что вы можете сделать, чтобы read захватить все промежуточные строки прогресса, - это перевести все возвраты каретки в реальные разрывы строк, пропустив материал через tr \\r \\n. Таким образом, каждое состояние печатается на новой строке вместо перезаписи предыдущей.

Итак, полный цикл, который вы использовали, может выглядеть следующим образом:

git clone --progress http://someRepo 2>&1 | tr \\r \\n |
    while read -r line ; do
        echo "bypassed: ${line}"
    done

В моей системе вывод, показанный в терминале этим решением, все еще не полностью плавный и немного запинается, но я не был в состоянии улучшить это дальше. По крайней мере, теперь у вас есть реальный вывод "progress"

3
ответ дан 28 December 2017 в 18:24

Вот готовое решение, не очень красивое, но работает хорошо!

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
0
ответ дан 25 May 2020 в 19:02

Другие вопросы по тегам:

Похожие вопросы: