Redirecionamento de stdout ignora linhas sem nova linha

Redirecionamento de stdout ignora linhas sem nova linha

Estou tentando que meu stderrterminal seja impresso em vermelho. O script abaixo redireciona 2para uma armadilha personalizada 8na depuração.

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "${RED}${line}${COLORRESET}"
    done
)
function undirect(){ exec 2>&9; } # reset to original 9 (==2)
function redirect(){ exec 2>&8; } # set to custom 8
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Vem deaqui, com uma explicação clara.

Parece funcionar muito bem, mas a entrada não terminada em nova linha não é impressa. Citando o autorevangelhosde novo:

bash> echo -en "hi\n" 1>&2
    hi       <-- this is red
bash> echo -en "hi" 1>&2
bash> echo -en "hi" 1>&2
bash> echo -en "hi\n" 1>&2
    hihihi   <-- this is red

Eu não consigo descobrir o porquê. O conteúdo que não é de nova linha parece acabar em algum tipo de buffer. Ele nem chega ao descritor de arquivo 8ou, de alguma forma, não deseja ser impresso imediatamente. Onde isso vai? redirecté chamado corretamente todas as vezes. Além disso, IFS=''significa que não há delimitador, então não entendo muito bem por que o eco ocorre em 8linhas.

Uma correção de bug seria muito apreciada. Vinculei a resposta citada a esta pergunta.

Toda esta solução, como salienta Gilles, não é totalmente perfeita. Estou tendo problemas com leitura, stdin, barras de progresso, não consigo sunem source. E frequentemente problemas graves, como canos quebrados e saídas inesperadas de terminais. Se alguém chegou aqui pelo meu link, considere usarhttps://github.com/sickill/stderredem vez disso, está muito melhor (sem problemas ainda) (embora echo bla >&2permaneça não vermelho eo respectivo problema está encerrado)

Responder1

Você obteve a saída de linhas parciais, como parte da mesma linha no ponto onde a nova linha foi impressa. As partes da linha são armazenadas em buffer read,é isso que faz:

Olera concessionária deve ler uma única linha lógica da entrada padrão

Por exemplo, isso é impresso <foobar>após um segundo, não <foo><bar>.

(echo -n foo ; sleep 1 ; echo bar) | (read x ; echo "<$x>")

Se você quiser capturar a entrada em partes menores que linhas completas, você precisará fazer outra coisa, por exemplo, com Perl. Isso seria impresso <foo><bar\n>(com a nova linha antes do last >, pois, diferentemente readdo Perl, o Perl não lida especialmente com a nova linha final. Não deve importar com a coloração.)

(echo -n foo ; sleep 1 ; echo bar) | 
    perl -e '$|=1; while(sysread STDIN,$a,9999) { print "<$a>"}'

Se você tiver os códigos de controle para cores ( REDe COLORRESET) exportados no ambiente, poderá usá-los no script Perl como aqui:

perl -e '$|=1; while(sysread STDIN,$a,9999) {print "$ENV{RED}$a$ENV{COLORRESET}"}'

Responder2

No Bash você pode usar a -dopção readbuiltin, que define o símbolo de fim de linha. man bashafirma isto:

-d delim    The first character of delim is used to terminate the input line, 
            rather than newline.

Se não estiver definido, readespera \naparecer para considerar uma string como uma linha. Mas ao usar a -dopção, você pode definir NULcomo delimitador. É claro que você também precisa finalizar a entrada com NUL.

Exemplo:

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s\n" "$line"
    done

Saída:

x

y
z

Mais uma vez, mas agora o printfloop whilenão funciona \n.

printf "%s\0" $'x\n' y z | while IFS='' read -r -d $'\0' line
    do
        printf "%s" "$line"
    done

Saída:

x
yz(...)

Eu adicionei (...)e isso significa que não há fim de linha no final da segunda linha. Mas o texto ainda é processado e impresso.

informação relacionada