Rimozione dei colori dall'output


141

Ho alcuni script che producono output con colori e ho bisogno di rimuovere i codici ANSI.

#!/bin/bash

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

./somescript

L'output è (nel file di registro):

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

Non sapevo come mettere il personaggio ESC qui, quindi ho messo @al suo posto.

Ho cambiato lo script in:

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

Ma ora mi dà (nel file di registro):

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

Come posso rimuovere anche questo " @[60G?

Forse c'è un modo per disabilitare completamente la colorazione per l'intero script?


Per nodo / npm, è possibile utilizzare strip-ansi: github.com/chalk/strip-ansi .
Joshua Pinter

Risposte:


166

Secondo Wikipedia , il [m|K]nel sedcomando che si sta utilizzando è specificamente progettato per gestire m(il comando colore) e K(la "parte cancellazione della linea di comando"). Lo script sta cercando di impostare la posizione assoluta del cursore su 60 ( ^[[60G) per ottenere tutti gli OK in una riga, che la sedlinea non copre.

(Giustamente, [m|K]probabilmente dovrebbe essere (m|K)o [mK], perché non stai cercando di abbinare un personaggio di pipa. Ma non è importante in questo momento.)

Se cambi l'ultima partita nel tuo comando su [mGK]o (m|G|K), dovresti essere in grado di catturare quella sequenza di controllo extra.

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

29
Utenti BSD / OSX: di solito non abbiamo l'opzione -r per sed. brew install gnu-sedinstallerà una versione capace. Corri con gsed.
Nicolai S,

1
Se lo faccio echo "$(tput setaf 1)foo$(tput sgr0) bar" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | cat -A, ottengo: foo^O bar$Quindi immagino che alcuni personaggi non vengano rimossi correttamente, giusto? Sai come correggere?
edi9999,

1
@ edi9999 Per quanto ne so, la differenza è che le impostazioni dei colori oltre i 16 colori (come setafsupporti) richiedono più parametri rispetto a solo due; il mio regex ne supporta due. Cambiare il primo ?per *dovrebbe aiutare. La gestione sgr0è possibile ma sulla base di una ricerca probabilmente cresce al di fuori dell'ambito di questa risposta basata sul regex.
Jeff Bowman,

Ok, ho aggiunto una risposta che aggiunge un carattere sedalla pipe per eliminare il carattere "shift in"
edi9999

7
Questo non funziona in modo affidabile in quanto può esserci un terzo valore (ala [38;5;45m). Questa risposta alternativa funziona unix.stackexchange.com/a/55547/168277
davemyron

30

Non sono riuscito a ottenere risultati decenti da nessuna delle altre risposte, ma quanto segue ha funzionato per me:

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

Se ho rimosso solo il carattere di controllo "^ [", ha lasciato il resto dei dati del colore, ad esempio "33m". Compreso il codice colore e "m" ha fatto il trucco. Sono perplesso con s / \ x1B // g non funziona perché \ x1B [31m sicuramente funziona con l'eco.


6
Su OSX (BSD sed), utilizzare -Einvece di -rregex esteso. Altro potrebbe essere trovato qui
Assambar

ho dovuto sostituire {1,3}a {,3}(altrimenti era ancora saltando alcuni controlli), grazie per la vostra soluzione!
azione il

6
Dal momento che potrebbero essere più numeri separati da punti e virgola (per il colore di sfondo, grassetto, corsivo, ecc ...). Questo comando ha funzionato per me:sed -r "s/[[:cntrl:]]\[([0-9]{1,3};)*[0-9]{1,3}m//g"
saeedgnu,

Questo (dei tanti che ho testato) ha funzionato con l'output Ansible che era stato eseguito con un buffer.
Martin,

23

IMHO, la maggior parte di queste risposte si sforza troppo per limitare ciò che è all'interno del codice di escape. Di conseguenza, finiscono per perdere codici comuni come[38;5;60m (primo piano ANSI colore 60 dalla modalità 256 colori).

Richiedono anche l' -ropzione che abilita estensioni GNU . Questi non sono richiesti; fanno solo leggere meglio il regex.

Ecco una risposta più semplice che gestisce le fughe a 256 colori e funziona su sistemi con non GNU sed:

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

Questo prenderà tutto ciò che inizia con [, ha un numero qualsiasi di decimali e punti e virgola e termina con una lettera. Questo dovrebbe catturare una qualsiasi delle sequenze di escape ANSI comuni .

Per i divertimenti, ecco una soluzione più ampia e più generale (ma minimamente testata) per tutte le possibili sequenze di fuga ANSI :

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

(e se hai il problema SI di @ edi9999, aggiungi | sed "s/\x0f//g"alla fine; questo funziona per qualsiasi carattere di controllo sostituendolo 0fcon l'esagono del carattere indesiderato)


Questo ha funzionato bene per mettere insieme il colore dall'output predefinito di Azure az cli.
volvox,

Risolto @elig. Si è scoperto che aveva una serie di problemi, a cominciare da alcuni editor che sostituivano tutti i miei trattini con strane versioni unicode, ma anche un mucchio di escape impropri: |in sed, ]all'interno di una classe di caratteri in sed e 'in una stringa bash a virgoletta singola. Ora funziona per me per un caso di test molto semplice.
Meustrus

20

Per Mac OSX o BSD utilizzare

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

1
Strano, questo ha funzionato bene per Debian ma altri sopra non lo hanno fatto.
cy8g3n,

Questo ha funzionato parzialmente. Tuttavia, se apro un file in Excel, vedo ancora questo carattere speciale "?" alla fine di ogni riga.
doudy_05,

@ doudy_05 Prova a passare il -Eflag per sed per abilitare regexp esteso.
Alexander Zinchenko,

14

Ho anche avuto il problema che a volte appariva il personaggio SI.

È successo ad esempio con questo input: echo "$(tput setaf 1)foo$(tput sgr0) bar"

Ecco un modo per eliminare anche il carattere SI (shift in) (0x0f)

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

2
Non sono sicuro del perché questa risposta riceva così poco credito. Questo è l'unico che funziona per me ...
m8mble

8

Hmm, non sono sicuro se questo funzionerà per te, ma 'tr' eliminerà '(eliminerà) i codici di controllo - prova:

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

32
All'improvviso rimuove anche nuove linee
ruX,

Sì, LF e CR (codici) sono codici di controllo; se sei interessato a più di una riga, questa potrebbe non essere una soluzione. Dal momento che sembra che tu stia eseguendo un programma JAVA, immagino che i colori siano gestiti da lì; Altrimenti dovresti guardare la configurazione della tua console (ovvero le impostazioni del terminale / combinazione di colori) e / o le opzioni per ciascun comando che supporta i 'colori', cioè ls --color = never
Dale_Reagan

3
Mi piace questa risposta per la sua eleganza, anche se non si limita a rimuovere i colori. Grazie!
Johann Philipp Strathausen,

7
in realtà lascia i codici lì, vedi ls -l + il tuo 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 il

7

Ho avuto un problema simile. Tutte le soluzioni che ho trovato hanno funzionato bene per i codici colore ma non hanno rimosso i caratteri aggiunti da "$(tput sgr0)"(reimpostazione degli attributi).

Prendendo, ad esempio, la soluzione nel commento di davemyron la lunghezza della stringa risultante nell'esempio seguente è 9, non 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}

Per funzionare correttamente, è stato necessario estendere il regex affinché corrispondesse anche alla sequenza aggiunta da sgr0(" \E(B"):

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

@Jarodiv - grazie per l'approccio più comprensivo. Tutte le risposte fornite su questo argomento riguardano SOLO le sequenze di controllo ANSI / VT100 (ad es. "\ E [31mHello World \ e [0m"), tuttavia non correggere alcunché causato dalla formattazione del testo TPUT (es: tput smso / tput setaf X / tput rmso / tput sgr0). Di conseguenza, dopo tutte le esecuzioni 'sed', nei registri rimanevano altri problemi. Questa è una soluzione pura ai miei casi d'uso!
senza volto,

5

Funzione molto più semplice in puro Bash per filtrare i codici ANSI comuni da un flusso di testo:

# 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
}

Vedere:

  1. linuxjournal.com: Extended Globbing
  2. gnu.org: espansione dei parametri di Bash

1
Questo non funziona Prova con tldr. (Anche se uso zsh, quindi potrebbe essere anche per questo.)
HappyFace,

In effetti, Zsh non capirà l'estensione globale di Bash extglobo probabilmente nemmeno capirà del tutto la sostituzione delle stringhe.
Léa Gris,

Ho abilitato l'estensione estesa di zsh ... Anche la sostituzione delle stringhe dovrebbe essere posix?
HappyFace,

La sostituzione della stringa non è POSIX. Puoi usare uno dei metodi alternativi usando sedmenzionato qui che funzionerà con Zsh.
Léa Gris,

Questa soluzione ha il vantaggio di eseguire il buffering di riga del testo. Ho provato con sed ma stava bloccando il blocco della mia pipa.
Guillermo Prandi,

3

La soluzione di @ jeff-bowman mi ha aiutato a sbarazzarmi di ALCUNI codici colore. Ho aggiunto un'altra piccola parte al regex per rimuoverne un po 'di più:

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

Ecco una soluzione Bash pura.

Salva come strip-escape-codes.sh, rendi eseguibile e poi esegui<command-producing-colorful-output> | ./strip-escape-codes.sh .

Si noti che questo elimina tutti i codici / sequenze di escape ANSI. Se si desidera rimuovere solo i colori, sostituirlo [a-zA-Z]con"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

Bene, questa soluzione potrebbe essere ancora meno complicata.
Alexander Zinchenko,

1

L'idea controversa sarebbe quella di riconfigurare le impostazioni del terminale per questo ambiente di processo per far sapere al processo che il terminale non supporta i colori.

TERM=xterm-mono ./somescriptMi viene in mente qualcosa di simile . YMMV con il tuo sistema operativo specifico e la capacità del tuo script di comprendere le impostazioni del colore del terminale.


-7

Questo funziona per me:

./somescript | cat

3
Dipende da come somescriptviene implementato. Può riconoscere o meno che il suo output standard è un tty. (Le parole trasgressori in realtà codificano nel programma codici di escape specifici del terminale e si rompono in modo orribile se utilizzati su altri terminali o negli script).
Toby Speight,

Grazie Toby. Ho usato django's manage.py per testare, ma ciò che hai detto ha senso.
spiderlama,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.