bash: imprimir stderr en color rojo


123

¿Hay alguna forma de hacer que bash muestre mensajes stderr en color rojo?


44
Supongo que bash nunca coloreará su salida: algunos programas pueden querer analizar algo, y la coloración estropeará los datos con secuencias escapadas. Una aplicación GUI debería manejar colores, supongo.
kolypto

La combinación de la respuesta de Balázs Pozsár y killdash9 da la nitidez: function color { "$@" 2> >(sed $'s,.*,\e[31m&\e[m,') } funciona para bash y zsh. No se puede agregar esto como una respuesta b / c reputación.
Heinrich Hartmann

1
Estoy esperando una respuesta que modifique bash para hacer esto. Todas las soluciones a continuación en realidad modifican stderr y posiblemente incluso reordenan wrt stdout, lo que rompe las cosas cuando la secuencia de bytes exacta de stderr debe conservarse, por ejemplo, cuando se realiza la tubería.
masterxilo

Respuestas:


98
command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)

66
¡Excelente! Pero me pregunto si hay una manera de hacerlo permanente :)
kolypto

99
Gran consejo! Sugerencia: al agregar >&2justo antes ; done), el resultado destinado a stderr en realidad se escribe en stderr. Eso es útil si desea capturar la salida normal del programa.
henko

8
Los siguientes usos tput, y es un poco más legible en mi opinión:command 2> >(while read line; do echo -e "$(tput setaf 1)$line$(tput sgr0)" >&2; done)
Stefan Lasiewski

2
Creo que ejecutar 2 procesos tput para cada línea de salida no es elegante en absoluto. Quizás si almacena la salida de los comandos tput en una variable y los usa para cada eco. Pero, de nuevo, la legibilidad no es realmente mejor.
Balázs Pozsár

1
Esta solución no conserva los espacios en blanco, pero me gusta por su brevedad. IFS= read -r linedebería ayudar pero no lo hace. No estoy seguro de por qué.
Max Murphy

89

Método 1: usar la sustitución del proceso:

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

Método 2: crear una función en un script bash:

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

Úselo así:

$ color command

Ambos métodos mostrarán los comandos stderren rojo.

Sigue leyendo para obtener una explicación de cómo funciona el método 2. Hay algunas características interesantes demostradas por este comando.

  • color()... - Crea una función bash llamada color.
  • set -o pipefail- Esta es una opción de shell que conserva el código de retorno de error de un comando cuya salida se canaliza a otro comando. Esto se hace en un subshell, que se crea entre paréntesis, para no cambiar la opción pipefail en el shell externo.
  • "$@"- Ejecuta los argumentos de la función como un nuevo comando. "$@"es equivalente a"$1" "$2" ...
  • 2>&1- Redirige el stderrcomando a stdoutpara que se convierta en sed's stdin.
  • >&3- Abreviatura de 1>&3, esto redirige stdouta un nuevo descriptor de archivo temporal 3. 3se enruta de nuevo stdoutmás tarde.
  • sed ...- Debido a los redireccionamientos anteriores, sed's stdines el stderrdel comando ejecutado. Su función es rodear cada línea con códigos de color.
  • $'...' Una construcción bash que hace que comprenda los caracteres con barra invertida
  • .* - Coincide con toda la línea.
  • \e[31m - La secuencia de escape ANSI que hace que los siguientes caracteres sean rojos
  • &- El sedcarácter de reemplazo que se expande a toda la cadena coincidente (la línea completa en este caso).
  • \e[m - La secuencia de escape ANSI que restablece el color.
  • >&2- Abreviatura de 1>&2este redirige sed's stdouta stderr.
  • 3>&1- Redirige el descriptor de archivo temporal 3nuevamente stdout.

99
+1 mejor respuesta! absolutamente subestimado!
muhqu

3
Gran respuesta e incluso mejor explicación
Daniel Serodio

¿Por qué necesita hacer toda la redirección adicional? parece exagerado
qodeninja 02 de

1
¿Hay alguna manera de hacerlo funcionar zsh?
Eyal Levin

1
ZSH no reconoce los formularios de redireccionamiento abreviado. Solo necesita dos 1 más, es decir:zsh: color()(set -o pipefail;"$@" 2>&1 1>&3|sed $'s,.*,\e[31m&\e[m,'1>&2)3>&1
Rekin

26

También puede consultar stderred: https://github.com/sickill/stderred


Wow, esta utilidad es genial, lo único que necesitaría es tener un repositorio apto que lo instale para todos los usuarios, con una línea, sin tener que hacer más trabajo para habilitarlo.
sorin

Parecía funcionar bien cuando lo probé con un script de compilación en un terminal separado, pero dudo en usarlo globalmente (en .bashrc). Gracias sin embargo!
Joel Purra

2
En OS X El Capitan, la forma en que funciona (DYLD_INSERT_LIBRARIES) está "dañada" en los archivos binarios del sistema porque están protegidos por SIP. Por lo tanto, podría ser mejor usar las opciones de bash dadas en otras respuestas.
hmijail

1
@hmijail para MacOS, siga github.com/sickill/stderred/issues/60 para que podamos encontrar una solución, ya existe una parcial pero está un poco defectuosa.
sorin

15

La manera bash de hacer que stderr permanezca rojo permanentemente es usar 'exec' para redirigir las transmisiones. Agregue lo siguiente a su 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;'

He publicado sobre esto anteriormente: Cómo configurar el color de fuente para STDOUT y STDERR



1
Esta es la mejor respuesta con diferencia; fácil de implementar sin instalación / que requiere privilegio sudo, y se puede generalizar a todos los comandos.
Luke Davis el

1
Lamentablemente, esto no funciona bien con el encadenamiento de comandos (comando && nextCommand || errorHandlerCommand). La salida de error va después de la salida de errorHandlerCommand.
carlin.scott

1
Del mismo modo, si hago source ~/.bashrcdos veces esto, mi terminal básicamente se bloquea.
Dolph

@Dolf: En mi bashrc, me protejo fácilmente de esto con una declaración if circundante para evitar que este código se vuelva a cargar. De lo contrario, el problema es la redirección 'exec 9> & 2' después de que la redirección ya haya tenido lugar. Tal vez cámbielo a una constante si sabe dónde apunta originalmente> 2.
gospes


7

Hice un script de envoltura que implementa la respuesta de Balázs Pozsár en bash puro. Guárdelo en sus comandos $ PATH y prefijo para colorear su salida.

    #! / bin / bash

    if [$ 1 == "--help"]; entonces
        echo "Ejecuta un comando y colorea todos los errores ocurridos"
        echo "Ejemplo:` basename $ {0} `wget ..."
        echo "(c) o_O Tync, ICQ # 1227-700, ¡Disfruta!"
        salida 0
        fi

    # Archivo temporal para detectar todos los errores
    TMP_ERRS = $ (mktemp)

    # Ejecutar comando
    "$ @" 2>> (mientras lee la línea; do echo -e "\ e [01; 31m $ line \ e [0m" | tee --append $ TMP_ERRS; hecho)
    EXIT_CODE = $?

    # Mostrar todos los errores nuevamente
    si [-s "$ TMP_ERRS"]; entonces
        echo -e "\ n \ n \ n \ e [01; 31m === ERRORES === \ e [0m"
        cat $ TMP_ERRS
        fi
    rm -f $ TMP_ERRS

    # Terminar
    salir $ EXIT_CODE


2
Esto podría hacerse más eficiente si "| tee ..." se puso después de "hecho".
Juliano

3

Puedes usar una función 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

Añado> & 2 para imprimir en stderr


44
No aborda el problema. No ha proporcionado una forma de separar stderr de stdout, que es lo que le interesa al OP.
Jeremy Visser

1

Tengo una versión ligeramente modificada del script de O_o Tync. Necesitaba hacer estas modificaciones para OS X Lion y no es perfecto porque el script a veces se completa antes que el comando envuelto. He añadido un sueño, pero estoy seguro de que hay una mejor manera.

#!/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

1

Esta solución funcionó para mí: https://superuser.com/questions/28869/immediately-tell-which-output-was-sent-to-stderr

He puesto esta función en mi .bashrco .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
}

Entonces, por ejemplo:

$ rse cat non_existing_file.txt

me dará una salida roja.


Puede agregar set -o pipefail;antes (evalpara el código de salida de redireccionamiento
kvaps

también agregue "a eval para preservar espacios en los argumentos
kvaps


0

una versión con 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.