Eliminar colores de la salida

141

Tengo un script que produce resultados con colores y necesito eliminar los códigos ANSI.

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript

El resultado es (en el archivo de registro):

java (pid  12321) is running...@[60G[@[0;32m  OK  @[0;39m]

No sabía cómo poner el personaje ESC aquí, así que puse @en su lugar.

Cambié el guión a:

#!/bin/bash

exec > >(tee log)   # redirect the output to a file but keep it on stdout
exec 2>&1

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"

Pero ahora me da (en el archivo de registro):

java (pid  12321) is running...@[60G[  OK  ]

¿Cómo puedo eliminar esto también @[60G?

¿Tal vez hay una manera de deshabilitar completamente el color para todo el script?

Pawel P.
fuente
Para node / npm, puede usar strip-ansi: github.com/chalk/strip-ansi .
Joshua Pinter

Respuestas:

166

Según Wikipedia , el comando [m|K]en el sedque está utilizando está específicamente diseñado para manejar m(el comando de color) y K(el comando "borrar parte de la línea"). Su secuencia de comandos está tratando de establecer la posición absoluta del cursor en 60 ( ^[[60G) para obtener todos los OK en una línea, que su sedlínea no cubre.

(Correctamente, [m|K]probablemente debería ser (m|K)o [mK], porque no estás tratando de hacer coincidir un carácter de tubería. Pero eso no es importante en este momento).

Si cambia esa coincidencia final en su comando [mGK]ao (m|G|K), debería poder capturar esa secuencia de control adicional.

./somescript | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g"
Jeff Bowman
fuente
29
Usuarios de BSD / OSX: generalmente no tenemos la opción -r de sed. brew install gnu-sedinstalará una versión capaz. Corre con gsed.
Nicolai S
1
Si lo hago echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, obtengo: foo^O bar$Entonces, supongo que algunos caracteres no se eliminan correctamente, ¿verdad? ¿Sabes cómo corregir?
edi9999
1
@ edi9999 Por lo que puedo ver, la diferencia es que las configuraciones de color más allá de 16 colores (como setafsoporte) requieren más parámetros que solo dos; mi expresión regular admite dos. Cambiar el primer ?out para *debería ayudar. El manejo sgr0es posible, pero en función de una búsqueda, es probable que crezca fuera del alcance de esta respuesta basada en expresiones regulares.
Jeff Bowman
Ok, agregué una respuesta que agrega una seda la tubería para quitar el carácter "cambio"
edi9999
77
Esto no funciona de manera confiable ya que puede haber un tercer valor (ala [38;5;45m). Esta respuesta alternativa funciona unix.stackexchange.com/a/55547/168277
davemyron
30

No pude obtener resultados decentes de ninguna de las otras respuestas, pero lo siguiente funcionó para mí:

somescript | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g"

Si solo eliminé el control char "^ [", dejó el resto de los datos de color, por ejemplo, "33m". Incluyendo el código de color y "m" hizo el truco. Estoy desconcertado con s / \ x1B // g no funciona porque \ x1B [31m ciertamente funciona con echo.

JoeAndrieu
fuente
66
En OSX (BSD sed), use en -Elugar de -rpara expresiones regulares extendidas. Más se puede encontrar aquí
Assambar
tuve que reemplazar {1,3}a {,3}(de lo contrario, seguía faltando algunos controles), gracias por su solución!
acción el
66
Ya que pueden ser múltiples números separados por punto y coma (para color de fondo, negrita, cursiva, etc.). Este comando funcionó para mí:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu
Este (de los muchos que probé) funcionó con la salida de Ansible que se había ejecutado con unbuffer.
Martin
23

En mi humilde opinión, la mayoría de estas respuestas intentan demasiado restringir lo que está dentro del código de escape. Como resultado, terminan perdiendo códigos comunes como [38;5;60m(ANSI color 60 en primer plano del modo de 256 colores).

También requieren la -ropción que habilita las extensiones GNU . Estos no son obligatorios; solo hacen que la expresión regular se lea mejor.

Aquí hay una respuesta más simple que maneja los escapes de 256 colores y funciona en sistemas con no GNU sed:

./somescript | sed 's/\x1B\[[0-9;]\+[A-Za-z]//g'

Esto capturará cualquier cosa que comience [, tenga cualquier número de decimales y punto y coma, y ​​termine con una letra. Esto debería detectar cualquiera de las secuencias de escape ANSI comunes .

Para divertirse, aquí hay una solución más grande y más general (pero mínimamente probada) para todas las secuencias de escape ANSI concebibles :

./somescript | sed 's/\x1B[@A-Z\\\]^_]\|\x1B\[[0-9:;<=>?]*[-!"#$%&'"'"'()*+,.\/]*[][\\@A-Z^_`a-z{|}~]//g'

(y si tiene el problema SI de @ edi9999, agréguelo | sed "s/\x0f//g"al final; esto funciona para cualquier carácter de control al reemplazarlo 0fcon el hexadecimal del carácter no deseado)

meustrus
fuente
Este funcionó muy bien para sacar el color de la cadena de la salida predefinida az Azure cli
volvox
Corregido @elig. Resulta que tenía una serie de problemas, comenzando con un editor que reemplazaba todos mis guiones por versiones Unicode extrañas, pero también un montón de escapes incorrectos: |en sed, ]dentro de una clase de caracteres en sed, y' en una cadena de comillas con comillas simples. Ahora está funcionando para mí para un caso de prueba muy básico.
meustrus
20

Para Mac OSX o BSD uso

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'
grebulon
fuente
1
Extraño, este funcionó bien para Debian, pero el anterior no.
cy8g3n
Este funcionó parcialmente. Sin embargo, si abro un archivo en Excel, todavía veo este carácter especial "?" al final de cada línea.
doudy_05
@ doudy_05 Intente pasar el -Eindicador para sed para habilitar la expresión regular extendida.
Alexander Zinchenko
14

También tuve el problema de que a veces aparecía el personaje SI.

Sucedió, por ejemplo, con esta entrada: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Aquí hay una manera de quitar también el carácter SI (shift in) (0x0f)

./somescript | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sed "s/\x0f//g"
edi9999
fuente
2
No estoy seguro de por qué esta respuesta recibe tan poco crédito. Este es el único que trabaja para mí ...
m8mble
8

Hmm, no estoy seguro de si esto funcionará para usted, pero 'tr' eliminará (eliminará) códigos de control .

./somescript | tr -d '[:cntrl:]'
Dale_Reagan
fuente
32
De repente, también se eliminan nuevas líneas
ruX
Sí, LF y CR (códigos) son códigos de control; Si está interesado en más de una línea, entonces esto puede no ser una solución. Como parece que está ejecutando un programa JAVA, supongo que los colores se administran desde allí; De lo contrario, debería mirar la configuración de la consola (es decir, la configuración del terminal / esquema de color) y / o las opciones para cada comando que admite 'colores', es decir, ls --color = never
Dale_Reagan
3
Me gusta esta respuesta por su elegancia, incluso si hace más que solo eliminar colores. ¡Gracias!
Johann Philipp Strathausen
77
en realidad deja códigos allí, vea ls -l + su comando:rwxr-xr-x 1 tokra admin 22 Oct 18 14:21 [0m[01;36m/usr/local/opt/gradle[0m -> [01;34m../Cellar/gradle/4.2.1[0m/
A Kra
7

Tuve un problema similar. Todas las soluciones que encontré funcionaron bien para los códigos de color, pero no eliminaron los caracteres agregados por "$(tput sgr0)"(restablecer atributos).

Tomando, por ejemplo, la solución en el comentario de davemyron, la longitud de la cadena resultante en el ejemplo a continuación es 9, no 6:

#!/usr/bin/env bash

string="$(tput setaf 9)foobar$(tput sgr0)"
string_sed="$( sed -r "s/\x1B\[[0-9;]*[JKmsu]//g" <<< "${string}" )"
echo ${#string_sed}

Para que funcione correctamente, la expresión regular tuvo que extenderse para que también coincida con la secuencia agregada por sgr0(" \E(B"):

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"
Jarodiv
fuente
@Jarodiv: gracias por el enfoque más completo. Todas las respuestas proporcionadas sobre este tema abordan SOLO las secuencias de control ANSI / VT100 (ej: "\ e [31mHello World \ e [0m"), sin embargo, no repare nada causado por el formato de texto TPUT (ej: tput smso / tput setaf X / tput rmso / tput sgr0). Como resultado, después de todas las ejecuciones 'sed', quedaba algún otro desorden en los registros. ¡Esta es una solución pura para mis casos de uso!
sin rostro
5

Función mucho más simple en Bash puro para filtrar códigos ANSI comunes de una secuencia de texto:

# Strips common ANSI codes from a text stream

shopt -s extglob # Enable Bash Extended Globbing expressions
ansi_filter() {
  local line
  local IFS=
  while read -r line || [[ "$line" ]]; do
    echo "${line//$'\e'[\[(]*([0-9;])[@-n]/}"
  done
}

Ver:

  1. linuxjournal.com: Globbing extendido
  2. gnu.org: expansión del parámetro bash
Léa Gris
fuente
1
Esto no funciona Prueba con tldr. (Aunque uso zsh, también podría ser por eso.)
HappyFace
De hecho, Zsh no comprenderá el gloging extendido de Bash extglobo probablemente tampoco entenderá el reemplazo de cadenas por completo.
Léa Gris
¿Habilité el extendido glob de zsh ... El reemplazo de cadenas también debería ser posix?
HappyFace
El reemplazo de la cadena no es POSIX. Puede usar cualquiera de los métodos alternativos que se sedmencionan aquí que funcionarán con Zsh.
Léa Gris
Esta solución tiene la ventaja de almacenar en línea el texto. Intenté con sed pero estaba bloqueando mi pipa.
Guillermo Prandi
3

La solución de @ jeff-bowman me ayudó a deshacerme de ALGUNOS de los códigos de color. Agregué otra pequeña porción a la expresión regular para eliminar un poco más:

sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # Original. Removed Red ([31;40m[1m[error][0m)
sed -r "s/\x1B\[([0-9];)?([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" # With an addition, removed yellow and green ([1;33;40m[1m[warning][0m and [1;32;40m[1m[ok][0m)
                ^^^^^^^^^
                remove Yellow and Green (and maybe more colors)
zstolar
fuente
2

Aquí hay una solución Bash pura.

Guardar como strip-escape-codes.sh, hacer ejecutable y luego ejecutar <command-producing-colorful-output> | ./strip-escape-codes.sh.

Tenga en cuenta que esto elimina todos los códigos / secuencias de escape ANSI. Si solo desea eliminar los colores, reemplácelos [a-zA-Z]por "m".

Bash> = 4.0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local _input="$1" _i _char _escape=0
    local -n _output="$2"; _output=""
    for (( _i=0; _i < ${#_input}; _i++ )); do
        _char="${_input:_i:1}"
        if (( ${_escape} == 1 )); then
            if [[ "${_char}" == [a-zA-Z] ]]; then
                _escape=0
            fi
            continue
        fi
        if [[ "${_char}" == $'\e' ]]; then
            _escape=1
            continue
        fi
        _output+="${_char}"
    done
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done

Golpe <4.0:

#!/usr/bin/env bash

# Strip ANSI escape codes/sequences [$1: input string, $2: target variable]
function strip_escape_codes() {
    local input="${1//\"/\\\"}" output="" i char escape=0
    for (( i=0; i < ${#input}; ++i )); do         # process all characters of input string
        char="${input:i:1}"                       # get current character from input string
        if (( ${escape} == 1 )); then             # if we're currently within an escape sequence, check if
            if [[ "${char}" == [a-zA-Z] ]]; then  # end is reached, i.e. if current character is a letter
                escape=0                          # end reached, we're no longer within an escape sequence
            fi
            continue                              # skip current character, i.e. do not add to ouput
        fi
        if [[ "${char}" == $'\e' ]]; then         # if current character is '\e', we've reached the start
            escape=1                              # of an escape sequence -> set flag
            continue                              # skip current character, i.e. do not add to ouput
        fi
        output+="${char}"                         # add current character to output
    done
    eval "$2=\"${output}\""                       # assign output to target variable
}

while read -r line; do
    strip_escape_codes "${line}" line_stripped
    echo "${line_stripped}"
done
Maxxim
fuente
Bueno, esta solución podría ser aún menos complicada.
Alexander Zinchenko
1

La idea controvertida sería reconfigurar la configuración del terminal para este entorno de proceso para que el proceso sepa que el terminal no admite colores.

Algo así me TERM=xterm-mono ./somescriptviene a la mente. YMMV con su sistema operativo específico y la capacidad de su script para comprender la configuración de color del terminal.

AB
fuente
-7

Esto funciona para mi:

./somescript | cat
spiderlama
fuente
3
Eso depende de cómo somescriptse implemente. Puede o no reconocer que su salida estándar es una tty. (Las palabras delincuentes en realidad codifican códigos de escape específicos del terminal en el programa, y ​​se rompen horriblemente cuando se usan en otros terminales o en scripts).
Toby Speight
Gracias Toby Usé django's manage.py para probar, pero lo que dijiste tiene sentido.
spiderlama