Exibe stdout e stderr em dois fluxos separados

12

Estou procurando uma maneira de separar visualmente stdout e stderr, de modo que eles não entrem e possam ser facilmente identificados. Idealmente, stdout e stderr teriam áreas separadas na tela em que são exibidas, por ex. em colunas diferentes. Por exemplo, saída que teria ficado assim:

~$ some command
some useful output info
ERROR: an error
more output
ERROR: has occurred
another message
~$ 

seria semelhante a algo assim:

~$ some command          |
some useful output info  |
more output              |  ERROR: an error
another message          |  ERROR: has occurred
~$                       |
    
por Zoey Hewll 19.06.2016 / 11:06

3 respostas

4

Você pode usar o recurso de divisão vertical do GNU screen :

#! /bin/bash -
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP

FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit

conf=$tmpdir/conf

cat > "$conf" << 'EOF' || exit
split -v
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF

CMD="$*"
export FIFO CMD

screen -mc "$conf"

Para usar por exemplo como:

that-script 'ls / /not-here'

A idéia é que ele execute a tela com um arquivo conf temporário que inicia duas janelas de tela em um layout dividido vertical. No primeiro, rodamos o seu comando com o stderr conectado ao segundo.

Usamos um pipe nomeado para a segunda janela para comunicar seu dispositivo tty ao primeiro, e também para o primeiro para informar o segundo quando o comando é feito.

A outra vantagem em comparação com as abordagens baseadas em pipe é que a stdout e stderr do comando ainda estão conectadas a dispositivos tty, portanto, isso não afeta o armazenamento em buffer. Ambos os painéis também podem ser rolados para cima e para baixo independentemente (usando o modo de cópia de screen ).

Se você executar um shell como bash interativamente com esse script, notará que o prompt será exibido na segunda janela, enquanto o shell lerá o que você digitar na primeira janela quando esses shells emitirem o prompt. stderr.

No caso de bash , o echo do que você digita também aparecerá na segunda janela, já que echo é produzido pelo shell (readline no caso de bash ) em stderr também. Com alguns outros shells como ksh93 , ele será mostrado na primeira janela (saída echo pelo driver do dispositivo de terminal, não pelo shell), a menos que você coloque o shell em emacs ou vi modo com set -o emacs ou set -o vi .

    
por 01.08.2016 / 18:45
1

Esta é uma solução feia baseada no script annotate-output do Debian ANOTATO-SAÍDA (1) . Não tenho certeza se é isso que você está procurando, mas pode ser algo para começar:

#!/bin/bash 

readonly col=150 # column to start error output 

add_out ()
{
    while IFS= read -r line; do
        echo "$1: $line"
    done
    if [ ! -z "$line" ]; then
        echo -n "$1: $line"
    fi
}

add_err ()
{
    while IFS= read -r line; do
        printf "%*s  %s %s: %s\n" $col "|" "$1" "$line"
    done
    if [ ! -z "$line" ]; then
        printf "%*s %s: %s" $col "$1" "$line"
    fi
}

cleanup() { __st=$?; rm -rf "$tmp"; exit $__st; }
trap cleanup 0
trap 'exit $?' 1 2 13 15

tmp=$(mktemp -d --tmpdir annotate.XXXXXX) || exit 1
OUT=$tmp/out
ERR=$tmp/err

mkfifo $OUT $ERR || exit 1

add_out OUTPUT < $OUT &
add_err ERROR < $ERR &

echo "I: Started $@"
"$@" > $OUT 2> $ERR ; EXIT=$?
rm -f $OUT $ERR
wait

echo "I: Finished with exitcode $EXIT"

exit $EXIT

Você pode testá-lo usando ./this_script another_script ou command .

    
por 19.06.2016 / 13:18
1

Vou tentar analisar a seguinte parte da sua pergunta:

would instead look something like this:

 ~$ some command
 some useful output info |
 more output             | ERROR: an error
 another message         | ERROR: has occurred 
 ~$ 

Se alguém quisesse quebrar o que você quer é:

1) O fluxo stdout não terminaria cada linha com um CR LF , mas sim com um '|' personagem. Isso não alinharia os dois fluxos juntos, é claro, e o alinhamento está fora de questão porque teria que prever a extensão de futuras linhas adicionadas ao stdout , o que é obviamente impossível.

2) Supondo que nos esquecemos do alinhamento, nós simplesmente produziríamos o stderr depois de ser processado por um pipeline que adiciona "ERROR:" ao início de cada linha. Eu suponho que isso é muito fácil, fazendo um script simples e certifique-se de que o stderr sempre saia desse script.

Mas isso criaria uma saída como esta:

~$ some command
 some useful output info|
 more output|  ERROR: an error
 another message|  ERROR: has occurred

O que não é realmente útil, é? Também não acredito, é o que você está depois também!

O problema com a questão inicial, eu acho é que você não leva em conta a natureza serial de cada linha anexada em um fluxo, em conexão com o fato de que ambos os fluxos podem ser escritos de forma assíncrona. Eu acredito que a solução mais próxima possível seria usar ncurses .
Vejo.
[ link
[ link

Para fazer o que você está depois, é necessário armazenar em buffer os dois fluxos e combiná-los para produzir um terceiro buffer que use elementos dos dois buffers. Em seguida, despeje o terceiro buffer na tela do terminal, apagando a tela do terminal e repintando-a cada vez que o terceiro buffer for alterado. Mas é assim que o ncurses funciona, então por que reinventar a roda e não a partir daí?
Em qualquer caso, você teria que assumir o controle da tela do terminal ! E realinhe o texto na versão reimpressa da tela como desejar. Muito parecido com um videogame com caracteres terminais.
Espero que minha resposta seja útil para esclarecer as limitações do que você é depois ...
Desculpe-me por repetir isso, mas o maior problema com o que você mostrou é como o "processador" dos fluxos stdout e stderr saberá antecipadamente o comprimento das futuras linhas adicionadas a ele, a fim de alinhá-las adequadamente. / p>     

por 17.07.2016 / 11:42