Estou tentando que meu stderr
terminal seja impresso em vermelho. O script abaixo redireciona 2
para uma armadilha personalizada 8
na 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 8
ou, 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 8
linhas.
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 su
nem 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 >&2
permaneç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 read
do 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 ( RED
e 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 -d
opção read
builtin, que define o símbolo de fim de linha. man bash
afirma isto:
-d delim The first character of delim is used to terminate the input line,
rather than newline.
Se não estiver definido, read
espera \n
aparecer para considerar uma string como uma linha. Mas ao usar a -d
opção, você pode definir NUL
como 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 printf
loop while
nã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.