Removendo cores da saída


140

Eu tenho algum script que produz saída com cores e preciso remover os códigos ANSI.

#!/bin/bash

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

./somescript

A saída é (no arquivo de log):

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

Eu não sabia como colocar o caractere ESC aqui, então coloquei @em seu lugar.

Eu mudei o script para:

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

Mas agora ele me fornece (no arquivo de log):

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

Como também posso remover isso ' @[60G?

Talvez haja uma maneira de desativar completamente a coloração de todo o script?


Para o nó / npm, você pode usar strip-ansi: github.com/chalk/strip-ansi .
— Joshua Pinter

Respostas:


165

Segundo a Wikipedia , o [m|K]no sedcomando que você está usando é especificamente projetado para lidar com m(o comando de cor) e K(a "parte de apagamento da linha de comando"). Seu script está tentando definir a posição absoluta do cursor como 60 ( ^[[60G) para obter todos os OKs em uma linha, que sua sedlinha não cobre.

(Devidamente, [m|K]provavelmente deveria ser (m|K)ou [mK], porque você não está tentando corresponder a um caractere de pipe. Mas isso não é importante no momento.)

Se você alternar a correspondência final em seu comando para [mGK]ou (m|G|K), poderá capturar essa sequência de controle extra.

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

29
Usuários de BSD / OSX: Normalmente, não temos a opção -r para sed. brew install gnu-sedinstalará uma versão capaz. Corra com gsed.
— Nicolai S

1
Se sim echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, entendo: foo^O bar$então acho que alguns caracteres não foram removidos corretamente, certo? Você sabe como corrigir?
— Edi9999 23/02

1
@ edi9999 Até onde sei, a diferença é que as configurações de cores além de 16 cores (como setafsuporte) exigem mais parâmetros do que apenas dois; meu regex suporta dois. Alterar a primeira ?saída *deve ajudar. A manipulação sgr0é possível, mas com base em uma pesquisa, ela provavelmente cresce fora do escopo desta resposta baseada em regex hacky.
— Jeff Bowman

Ok, eu adicionei uma resposta que adiciona um sedpara o tubo para retirar a "mudança de" caráter
— edi9999

7
Isso não funciona de maneira confiável, pois pode haver um terceiro valor (ala [38;5;45m). Esta resposta alternativa funciona unix.stackexchange.com/a/55547/168277
— davemyron

30

Não consegui resultados decentes com nenhuma das outras respostas, mas o seguinte funcionou para mim:

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

Se eu apenas removi o caractere de controle "^ [", ele deixou o restante dos dados de cores, por exemplo, "33m". Incluir o código de cores e "m" fez o truque. Estou intrigado com s / \ x1B // g não funciona porque \ x1B [31m certamente funciona com eco.


6
No OSX (BSD sed), use em -Evez de -rpara regex estendido. Mais pode ser encontrado aqui
— Assambar

i teve que substituir {1,3}a {,3}(caso contrário ele ainda estava pulando alguns controles), graças para a sua solução!
— ação 17/04

6
Como podem ser vários números separados por ponto e vírgula (para cor de fundo, negrito, itálico, etc ...). Este comando funcionou para mim:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
— saeedgnu

Este (dos muitos que eu testei) trabalhou com a saída Ansible que foi executada com o buffer.
— Martin

23

IMHO, a maioria dessas respostas se esforça demais para restringir o que está dentro do código de escape. Como resultado, eles acabam perdendo códigos comuns como [38;5;60m(cor ANSI 60 de primeiro plano no modo de 256 cores).

Eles também exigem a -ropção que permite extensões GNU . Estes não são necessários; eles apenas fazem o regex ler melhor.

Aqui está uma resposta mais simples que lida com as fugas de 256 cores e funciona em sistemas com não-GNU sed:

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

Isso irá capturar qualquer coisa que comece com [, tenha qualquer número de casas decimais e ponto-e-vírgula e termine com uma letra. Isso deve capturar qualquer uma das seqüências de escape ANSI comuns .

Para diversão, aqui está uma solução maior e mais geral (mas minimamente testada) para todas as seqüências de escape ANSI concebíveis :

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

(e se você tiver o problema de SI do @ edi9999, adicione | sed "s/\x0f//g"até o final; isso funciona para qualquer caractere de controle substituindo 0fpelo hexadecimal do caractere indesejado)


Este funcionou bem para remover a cor da saída pretendida do az azi do Azure.
— volvox

Fixed @elig. Acontece que ele teve vários problemas, começando com algum editor substituindo todos os meus traços por versões estranhas de unicode, mas também um monte de escapamentos impróprios - |no sed, ]dentro de uma classe de caracteres no sed e 'em uma sequência de caracteres de citação única. Agora ele está trabalhando para mim em um caso de teste muito básico.
— meustrus 23/03

20

Para Mac OSX ou BSD, use

./somescript | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g'

1
Estranho, este funcionou bem para o debian, mas o outro acima não.
— cy8g3n 5/12/19

Este funcionou parcialmente. No entanto, se eu abrir um arquivo no Excel, ainda estou vendo esse caractere especial "?" no final de cada linha.
— doudy_05

@ doudy_05 Tente passar o -Esinalizador para sed para ativar o regexp estendido.
— Alexander Zinchenko

14

Eu também tive o problema de que, às vezes, o personagem SI aparecia.

Aconteceu, por exemplo, com esta entrada: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Aqui está uma maneira de também remover o caractere SI (shift in) (0x0f)

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

2
Não sei por que essa resposta recebe tão pouco crédito. Este é o único trabalho para mim ...
— m8mble

8

Hmm, não tenho certeza se isso funcionará para você, mas 'tr' irá 'remover' (excluir) os códigos de controle - tente:

./somescript | tr -d '[:cntrl:]'

32
De repente, também remove novas linhas
— ruX

Sim, LF e CR (códigos) são códigos de controle; se você estiver interessado em mais de uma linha, talvez isso não seja uma solução. Como parece que você está executando um programa JAVA, acho que as cores são gerenciadas a partir daí; Caso contrário, você precisaria examinar a configuração do console (ou seja, configurações do terminal / esquema de cores) e / ou as opções para cada comando que suporta 'cores', ou seja, ls --color = never
— Dale_Reagan

3
Gosto desta resposta por sua elegância, mesmo que faça mais do que apenas remover cores. Obrigado!
— Johann Philipp Strathausen

7
na verdade, deixa códigos lá, veja ls -l + your command: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/
— Para Kra

7

Eu tive um problema parecido. Todas as soluções que encontrei funcionaram bem para os códigos de cores, mas não removeram os caracteres adicionados por "$(tput sgr0)"(redefinindo atributos).

Tomando, por exemplo, a solução no comentário de davemyron, o comprimento da sequência resultante no exemplo abaixo é 9, não 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 funcionar corretamente, o regex precisou ser estendido para corresponder também à sequência adicionada por sgr0(" \E(B"):

string_sed="$( sed -r "s/\x1B(\[[0-9;]*[JKmsu]|\(B)//g" <<< "${string}" )"

@ Jarodiv - obrigado pela abordagem mais abrangente. Todas as respostas fornecidas neste tópico tratam APENAS das seqüências de controle ANSI / VT100 (ex: "\ e [31mOlá Mundo \ e [0m"), no entanto, não corrigem nada causado pela formatação de texto TPUT (por exemplo: tput smso / tput setaf X / tput rmso / tput sgr0). Como resultado, depois de todas as execuções 'sed', havia outra bagunça restante nos logs. Esta é uma solução pura para os meus casos!
— sem rosto

5

Função muito mais simples no Bash puro para filtrar códigos ANSI comuns de um fluxo 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
}

Vejo:

  1. linuxjournal.com: Globbing estendido
  2. gnu.org: Expansão de parâmetros Bash

1
Isso não funciona. Teste com tldr. (Embora eu uso zsh por isso pode ser também por causa disso.)
— HappyFace

De fato, o Zsh não entenderá a extensão alargada de Bash extglobou provavelmente também não entenderá a substituição de cadeias por completo.
— Léa Gris

Eu habilitei o estendido glob do zsh ... A substituição de string também deve ser posix?
— HappyFace 28/07/19

A substituição da cadeia não é POSIX. Você pode usar qualquer um dos métodos alternativos usando os sedmencionados aqui que funcionarão com o Zsh.
— Léa Gris

Esta solução tem a vantagem de fazer buffer de linha no texto. Eu tentei com o sed, mas estava bloqueando meu cachimbo.
— Guillermo Prandi

3

A solução da @ jeff-bowman me ajudou a me livrar de ALGUNS códigos de cores. Adicionei outra pequena porção ao regex para remover um pouco mais:

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)

2

Aqui está uma solução Bash pura.

Salve como strip-escape-codes.sh, torne executável e execute <command-producing-colorful-output> | ./strip-escape-codes.sh.

Observe que isso remove todos os códigos / seqüências de escape ANSI. Se você deseja descascar apenas cores, substitua [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

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//\"/\\\"}" 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

Bem, essa solução pode ser ainda menos complicada.
— Alexander Zinchenko

1

A idéia controversa seria reconfigurar as configurações do terminal para esse ambiente de processo para permitir que o processo saiba que o terminal não suporta cores.

Algo como TERM=xterm-mono ./somescriptvem à minha mente. YMMV com seu sistema operacional específico e capacidade de seu script para entender as configurações de cores dos terminais.


-7

Isso funciona para mim:

./somescript | cat

3
Isso depende de como somescripté implementado. Pode ou não reconhecer que sua saída padrão é um tty. (As palavras infratores realmente codificam códigos de escape específicos do terminal no programa e quebram horrivelmente quando usadas em outros terminais ou em scripts).
— precisa

Obrigado Toby. Usei o manage.py do django para testar, mas o que você disse faz sentido.
— 27617 spiderlama
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.