Esta é uma pergunta muito interessante e eu tive que experimentar por um tempo, mas a resposta é realmente super simples!
Como funciona a saída de progresso do git
? Ele mostra uma única linha de status com uma porcentagem de progresso e atualiza o tempo todo até que seja concluído. Ele faz isso não imprimindo um avanço de linha \n
, mas um caractere retorno de carro \r
no final da linha de progresso, o que faz com que o cursor de saída retorne novamente ao início da linha pronto para sobrescrever a última linha escrita novamente com um valor atualizado.
Você pode observar isso canalizando a saída de git
através de cat -A
, que não interpreta caracteres invisíveis, mas os exibe, por exemplo, \r
torna-se ^M
e as quebras de linha também são denotadas com $
:
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)
[...]
Agora, por que isso afeta read
? É óbvio, como diz help read
(extrair, ênfase minha):
Read a line from the standard input and split it into fields.
read
espera por quebras de linha ( \n
) antes de terminar. A saída de git
não continha nenhuma quebra de linha durante a exibição de progresso, portanto read
não foi concluído. Somente depois que a exibição de progresso foi concluída e git
realmente imprimiu a próxima linha, read
consumiu toda a saída, incluindo todos os estados intermediários e retornos de carro, e você o repetiu dentro do seu loop. Se você canalizar a echo
ou done
a cat -A
, verá a mesma saída cheia de ^M
s, como acima.
O que você pode fazer para permitir que read
capture todas as linhas de progresso intermediárias é traduzir todos os retornos de carro para quebras de linha reais, canalizando o material através de tr \r \n
. Dessa forma, cada estado é impresso em uma nova linha, em vez de sobrescrever a linha anterior.
Então, o loop completo que você usou poderia ter a seguinte aparência:
git clone --progress http://someRepo 2>&1 | tr \r \n |
while read -r line ; do
echo "bypassed: ${line}"
done
No meu sistema, a saída mostrada no terminal por essa solução ainda não é totalmente fluente e gagueja um pouco, mas não consegui melhorar ainda mais. Pelo menos agora você tem o resultado real "progresso".