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 |& ...
но это не изменило выход.
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
с, как и выше.
Что вы можете сделать, чтобы позволить read
захватить все промежуточные строки прогресса означает преобразовать все возвраты каретки в реальные разрывы строк, пропуская материал через tr \\r \\n
, Таким образом, каждое состояние печатается на новой строке вместо перезаписи предыдущей.
Итак, весь цикл, который вы использовали, может выглядеть так:
git clone --progress http://someRepo 2>&1 | tr \\r \\n |
while read -r line ; do
echo "bypassed: ${line}"
done
В моей системе вывод, показанный в терминале этим решением, все еще не полностью плавный и немного заикается, но я не смог улучшить это дальше. По крайней мере, теперь у вас есть реальный результат "прогресса".
Вот полное решение, не очень хорошее, но хорошо работающее!
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