bash: stderr de impressão na cor vermelha

123

Existe uma maneira de fazer o bash exibir mensagens stderr na cor vermelha?

Kolypto
fonte
4
Eu acho que o bash nunca colorirá sua saída: algum programa pode querer analisar algo, e a coloração estragará os dados com seqüências de escape. Um aplicativo GUI deve lidar com cores, eu acho.
kolypto
A combinação da resposta de Balázs Pozsár e killdash9 fornece o nítido: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } funciona para o bash e o zsh. Não é possível adicionar isso como resposta b / c reputação.
Heinrich Hartmann
1
Estou aguardando uma resposta que modifique o bash para fazer isso. As soluções a seguir, todos efectivamente modificar stderr e, possivelmente, até mesmo o reordenar wrt saída padrão o qual quebra as coisas, quando a sequência de bytes exacta de stderr deve ser preservada por exemplo quando tubagem.
Masterxilo # 21/18

Respostas:

98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Balázs Pozsár
fonte
6
Ótimo! Mas eu me pergunto se há uma maneira de torná-lo :) permanente
kolypto
9
Ótima dica! Sugestão: Ao adicionar >&2um pouco antes ; done), a saída destinada ao stderr é realmente gravada no stderr. Isso é útil se você deseja capturar a saída normal do programa.
henko
8
As seguintes utilizações tput, e é ligeiramente mais legível na minha opinião:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski
2
Eu acho que a execução de 2 processos de tput para cada linha de saída não é nada elegante. Talvez se você armazenasse a saída dos comandos tput em uma variável e os usasse para cada eco. Mas, novamente, a legibilidade não é realmente melhor.
Balázs Pozsár
1
Esta solução não preserva o espaço em branco, mas eu gosto por sua brevidade. IFS= read -r linedeve ajudar, mas não ajuda. Não sei por que.
Max Murphy
89

Método 1: Usar substituição de processo:

command 2> >(sed $'s,.*,\e[31m&\e[m,'>&2)

Método 2: Crie uma função em um script bash:

color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1

Use-o assim:

$ color command

Ambos os métodos mostrarão os comandos stderrem vermelho.

Continue lendo para obter uma explicação de como o método 2 funciona. Existem alguns recursos interessantes demonstrados por este comando.

  • color()... - Cria uma função bash chamada cor.
  • set -o pipefail- Esta é uma opção de shell que preserva o código de retorno de erro de um comando cuja saída é canalizada para outro comando. Isso é feito em um subshell, criado pelos parênteses, para não alterar a opção de pipefail no shell externo.
  • "$@"- Executa os argumentos para a função como um novo comando. "$@"é equivalente a"$1" "$2" ...
  • 2>&1- Redireciona o stderrdo comando para stdoutmodo que se torna sed's stdin.
  • >&3- Abreviação de 1>&3, isso redireciona stdoutpara um novo descritor de arquivo temporário 3. 3é roteado de volta para stdoutmais tarde.
  • sed ...- Por causa dos redirecionamentos acima, sed's stdiné o stderrcomando executado. Sua função é cercar cada linha com códigos de cores.
  • $'...' Uma construção bash que faz com que ele entenda caracteres com escape de barra invertida
  • .* - Corresponde a linha inteira.
  • \e[31m - A sequência de escape ANSI que faz com que os seguintes caracteres fiquem vermelhos
  • &- O sedcaractere de substituição que se expande para toda a cadeia correspondente (a linha inteira nesse caso).
  • \e[m - A sequência de escape ANSI que redefine a cor.
  • >&2- Abreviação para 1>&2, este redireciona sed's stdoutpara stderr.
  • 3>&1- Redireciona 3novamente o descritor de arquivo temporário stdout.
killdash9
fonte
9
+1 melhor resposta! absolutamente subestimado!
muhqu
3
Ótima resposta e explicação ainda melhor #
Daniel Serodio 22/10
Por que você precisa fazer todo o redirecionamento adicional? parece um exagero
qodeninja
1
Existe uma maneira de fazê-lo funcionar zsh?
Eyal Levin
1
O ZSH não reconhece os formulários de redirecionamento abreviado. Ele só precisa de mais dois zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
1s
26

Você também pode conferir stderred: https://github.com/sickill/stderred

doença
fonte
Uau, esse utilitário é ótimo, a única coisa que ele precisa é ter um repositório apt que o instale para todos os usuários, com uma linha, sem ter que fazer mais trabalho para habilitá-lo.
22412
Parecia funcionar bem quando o testei com um script de construção em um terminal separado, mas hesito em usá-lo globalmente (in .bashrc). Obrigado embora!
Joel Purra
2
No OS X El Capitan, a maneira como isso funciona (DYLD_INSERT_LIBRARIES) é "interrompida" nos binários do sistema porque eles são protegidos pelo SIP. Portanto, pode ser melhor usar as opções do bash fornecidas em outras respostas.
hmijail
1
@hmijail para MacOS, siga github.com/sickill/stderred/issues/60 para encontrar uma solução alternativa, uma parcial já existe, mas é um pouco problemática.
sorin
15

A maneira básica de tornar o stderr permanentemente vermelho é usar 'exec' para redirecionar fluxos. Adicione o seguinte ao seu bashrc:

exec 9>&2
exec 8> >(
    while IFS='' read -r line || [ -n "$line" ]; do
       echo -e "\033[31m${line}\033[0m"
    done
)
function undirect(){ exec 2>&9; }
function redirect(){ exec 2>&8; }
trap "redirect;" DEBUG
PROMPT_COMMAND='undirect;'

Eu postei isso anteriormente: Como definir a cor da fonte para STDOUT e STDERR

evangelhos
fonte
1
Esta é a melhor resposta de longe; É fácil de implementar sem a instalação / requer privilégio sudo e pode ser generalizado para todos os comandos.
Luke Davis
1
Infelizmente, isso não funciona bem com o encadeamento de comandos (comando && nextCommand || errorHandlerCommand). A saída de erro segue a saída errorHandlerCommand.
Carlin.scott 18/06/19
1
Da mesma forma, se eu fizer isso source ~/.bashrcduas vezes, meu terminal basicamente trava.
Dolph
@Dolf: No meu bashrc, eu me protejo facilmente com uma declaração if circundante para impedir que esse código seja recarregado. Caso contrário, o problema é o redirecionamento 'exec 9> & 2' após o redirecionamento já ter ocorrido. Talvez mude para uma constante se você souber para onde> 2 está apontando originalmente.
gospes 01/10/1918
7

Eu criei um script de wrapper que implementa a resposta de Balázs Pozsár em pura festa. Salve-o em seus comandos $ PATH e prefix para colorir sua saída.

    #! / bin / bash

    if [$ 1 == "--help"]; então
        echo "Executa um comando e colore todos os erros ocorridos"
        echo "Exemplo:` nome da base $ {0} `wget ..."
        eco "(c) o_O Tync, ICQ # 1227-700, divirta-se!"
        saída 0
        fi

    # Arquivo temporário para capturar todos os erros
    TMP_ERRS = $ (mktemp)

    # Executar comando
    "$ @" 2>> (enquanto lê a linha; faça eco-e "\ e [01; 31m $ line \ e [0m" | tee - adicione $ TMP_ERRS; pronto)
    EXIT_CODE = $?

    # Exibe todos os erros novamente
    if [-s "$ TMP_ERRS"]; então
        eco -e "\ n \ n \ n \ e [01; 31m === ERROS === \ e [0m"
        cat $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Terminar
    sair $ EXIT_CODE

Kolypto
fonte
2
Isso poderia ser mais eficiente se "| tee ..." fosse colocado depois de "pronto".
Juliano
3

Você pode usar uma função como esta


 #!/bin/sh

color() {
      printf '\033[%sm%s\033[m\n' "$@"
      # usage color "31;5" "string"
      # 0 default
      # 5 blink, 1 strong, 4 underlined
      # fg: 31 red,  32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
      # bg: 40 black, 41 red, 44 blue, 45 purple
      }
string="Hello world!"
color '31;1' "$string" >&2

Anexo> & 2 para imprimir em stderr

Ali Mezgani
fonte
4
Não está resolvendo o problema. Você não forneceu uma maneira de separar stderr de stdout, e é nisso que o OP está interessado.
Jeremy Visser
1

Eu tenho uma versão ligeiramente modificada do script do O_o Tync. Eu precisava fazer esses mods para o OS X Lion e não é perfeito porque o script às vezes é concluído antes do comando finalizado. Adicionei um sono, mas tenho certeza de que há uma maneira melhor.

#!/bin/bash

   if [ $1 == "--help" ] ; then
       echo "Executes a command and colorizes all errors occured"
       echo "Example: `basename ${0}` wget ..."
       echo "(c) o_O Tync, ICQ# 1227-700, Enjoy!"
       exit 0
       fi

   # Temp file to catch all errors
   TMP_ERRS=`mktemp /tmp/temperr.XXXXXX` || exit 1

   # Execute command
   "$@" 2> >(while read line; do echo -e "$(tput setaf 1)$line\n" | tee -a $TMP_ERRS; done)
   EXIT_CODE=$?

   sleep 1
   # Display all errors again
   if [ -s "$TMP_ERRS" ] ; then
       echo -e "\n\n\n$(tput setaf 1) === ERRORS === "
       cat $TMP_ERRS
   else
       echo "No errors collected in $TMP_ERRS"
   fi
   rm -f $TMP_ERRS

   # Finish
   exit $EXIT_CODE
Penhasco
fonte
1

Esta solução funcionou para mim: https://superuser.com/questions/28869/immedately-tell-which-output-was-sent-to-stderr

Eu coloquei essa função no meu .bashrcou .zshrc:

# Red STDERR
# rse <command string>
function rse()
{
    # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace
    # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back
    ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/$(echo -en \\033)[31;1m\1$(echo -en \\033)[0m/") 3>&1 1>&2 2>&3
}

Então, por exemplo:

$ rse cat non_existing_file.txt

vai me dar uma saída vermelha.

Eyal Levin
fonte
Você pode adicionar set -o pipefail;antes (evalpara o código de saída de redirecionamento
kvaps 27/06
adicione também o "to eval para preservar espaços nos argumentos
kvaps 27/06
1

usando xargs e printf:

command 2> >(xargs -0 printf "\e[31m%s\e[m" >&2)
Carlos Barcellos
fonte
0

uma versão usando fifos

mkfifo errs
stdbuf -o0 -e0 -i0 grep . foo | while read line; do echo -e "\e[01;31m$line  \e[0m" >&2; done &
stdbuf -o0 -e0 -i0 sh $script 2>errs
desenterrar
fonte